001 /*
002 * Copyright 2008-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package griffon.core.editors;
017
018 import javax.annotation.Nonnull;
019 import javax.annotation.Nullable;
020 import javax.annotation.concurrent.GuardedBy;
021 import java.beans.PropertyEditor;
022 import java.beans.PropertyEditorSupport;
023 import java.lang.ref.WeakReference;
024 import java.util.ArrayList;
025 import java.util.Collections;
026 import java.util.LinkedHashSet;
027 import java.util.List;
028 import java.util.Set;
029
030 import static griffon.util.GriffonClassUtils.requireNonEmpty;
031 import static griffon.util.GriffonNameUtils.isBlank;
032 import static java.util.Objects.requireNonNull;
033
034 /**
035 * @author Andres Almiray
036 * @since 2.4.0
037 */
038 public class PropertyEditorChain extends PropertyEditorSupport implements ExtendedPropertyEditor {
039 private final Class<?> targetClass;
040 private final Object lock = new Object[0];
041 private final WeakReference<Class<? extends PropertyEditor>>[] propertyEditorClasses;
042 @GuardedBy("lock")
043 private WeakReference<PropertyEditor>[] propertyEditors;
044 private String format;
045
046 @SuppressWarnings("unchecked")
047 public PropertyEditorChain(@Nonnull Class<?> targetClass, @Nonnull Class<? extends PropertyEditor>[] propertyEditorClasses) {
048 this.targetClass = requireNonNull(targetClass, "Argument 'targetClass' must not be null");
049 requireNonEmpty(propertyEditorClasses, "Argument 'propertyEditorClasses' must not be null nor empty");
050 // let's make sure propertyEditorClasses contains unique elements
051 Set<Class<? extends PropertyEditor>> classes = new LinkedHashSet<>();
052 Collections.addAll(classes, propertyEditorClasses);
053
054 int i = 0;
055 this.propertyEditorClasses = new WeakReference[classes.size()];
056 for (Class<? extends PropertyEditor> klass : classes) {
057 this.propertyEditorClasses[i++] = new WeakReference<Class<? extends PropertyEditor>>(klass);
058 }
059 }
060
061 @Override
062 @Nullable
063 public String getFormat() {
064 return format;
065 }
066
067 @Override
068 public void setFormat(@Nullable String format) {
069 this.format = format;
070 }
071
072 public int getSize() {
073 return propertyEditorClasses.length;
074 }
075
076 @SuppressWarnings("unchecked")
077 public PropertyEditorChain copyOf() {
078 List<Class<? extends PropertyEditor>> classes = new ArrayList<>();
079 for (WeakReference<Class<? extends PropertyEditor>> reference : propertyEditorClasses) {
080 if (reference.get() != null) {
081 classes.add(reference.get());
082 }
083 }
084 return new PropertyEditorChain(targetClass, classes.toArray(new Class[classes.size()]));
085 }
086
087 @SuppressWarnings("unchecked")
088 public PropertyEditorChain copyOf(Class<? extends PropertyEditor> propertyEditorClass) {
089 requireNonNull(propertyEditorClass, "Argument 'propertyEditorClass' must not be null");
090 List<Class<? extends PropertyEditor>> classes = new ArrayList<>();
091 for (WeakReference<Class<? extends PropertyEditor>> reference : propertyEditorClasses) {
092 if (reference.get() != null) {
093 classes.add(reference.get());
094 }
095 }
096 if (!classes.contains(propertyEditorClass)) {
097 classes.add(propertyEditorClass);
098 }
099 return new PropertyEditorChain(targetClass, classes.toArray(new Class[classes.size()]));
100 }
101
102 @Override
103 public String toString() {
104 StringBuilder sb = new StringBuilder(super.toString());
105 sb.append("[").append(targetClass.getName()).append(']');
106 return sb.toString();
107 }
108
109 @Override
110 public String getAsText() {
111 return isBlank(getFormat()) ? getAsTextInternal() : getFormattedValue();
112 }
113
114 @Override
115 public void setAsText(String str) throws IllegalArgumentException {
116 if (isBlank(getFormat())) {
117 setAsTextInternal(str);
118 } else {
119 setFormattedValue(str);
120 }
121 }
122
123 @Override
124 public void setValue(Object value) {
125 if (value instanceof CharSequence) {
126 setFormattedValue(String.valueOf(value));
127 } else {
128 setValueInternal(value);
129 }
130 }
131
132 @Override
133 @SuppressWarnings({"unchecked", "rawtypes"})
134 public String getFormattedValue() {
135 initPropertyEditors();
136
137 Object value = super.getValue();
138 for (WeakReference<PropertyEditor> reference : propertyEditors) {
139 try {
140 PropertyEditor propertyEditor = reference.get();
141 if (propertyEditor != null && propertyEditor instanceof ExtendedPropertyEditor) {
142 ExtendedPropertyEditor extendedPropertyEditor = (ExtendedPropertyEditor) propertyEditor;
143 extendedPropertyEditor.setFormat(format);
144 extendedPropertyEditor.setValue(value);
145 return extendedPropertyEditor.getFormattedValue();
146 }
147 } catch (Exception e) {
148 // ignore. next editor
149 }
150 }
151
152 throw illegalValue(value, targetClass);
153 }
154
155 @Override
156 @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
157 public void setFormattedValue(String value) {
158 initPropertyEditors();
159
160 for (WeakReference<PropertyEditor> reference : propertyEditors) {
161 try {
162 PropertyEditor propertyEditor = reference.get();
163 if (propertyEditor != null && propertyEditor instanceof ExtendedPropertyEditor) {
164 ExtendedPropertyEditor extendedPropertyEditor = (ExtendedPropertyEditor) propertyEditor;
165 extendedPropertyEditor.setFormat(format);
166 extendedPropertyEditor.setValue(value);
167 super.setValue(extendedPropertyEditor.getValue());
168 return;
169 }
170 } catch (Exception e) {
171 // ignore. next editor
172 }
173 }
174
175 throw illegalValue(value, targetClass);
176 }
177
178 protected void setValueInternal(Object value) throws IllegalArgumentException {
179 initPropertyEditors();
180
181 for (WeakReference<PropertyEditor> reference : propertyEditors) {
182 try {
183 PropertyEditor propertyEditor = reference.get();
184 if (propertyEditor != null) {
185 propertyEditor.setValue(value);
186 super.setValue(propertyEditor.getValue());
187 return;
188 }
189 } catch (Exception e) {
190 // ignore. next editor
191 }
192 }
193
194 throw illegalValue(value, targetClass);
195 }
196
197 protected Object getValueInternal() {
198 initPropertyEditors();
199
200 Object value = super.getValue();
201 for (WeakReference<PropertyEditor> reference : propertyEditors) {
202 try {
203 PropertyEditor propertyEditor = reference.get();
204 if (propertyEditor != null) {
205 propertyEditor.setValue(value);
206 return propertyEditor.getValue();
207 }
208 } catch (Exception e) {
209 // ignore. next editor
210 }
211 }
212
213 throw illegalValue(value, targetClass);
214 }
215
216 protected String getAsTextInternal() {
217 initPropertyEditors();
218
219 Object value = super.getValue();
220
221 for (WeakReference<PropertyEditor> reference : propertyEditors) {
222 try {
223 PropertyEditor propertyEditor = reference.get();
224 if (propertyEditor != null) {
225 propertyEditor.setValue(value);
226 return propertyEditor.getAsText();
227 }
228 } catch (Exception e) {
229 // ignore. next editor
230 }
231 }
232
233 throw illegalValue(value, targetClass);
234 }
235
236 protected void setAsTextInternal(String text) throws IllegalArgumentException {
237 initPropertyEditors();
238
239 for (WeakReference<PropertyEditor> reference : propertyEditors) {
240 try {
241 PropertyEditor propertyEditor = reference.get();
242 if (propertyEditor != null) {
243 propertyEditor.setAsText(text);
244 super.setValue(propertyEditor.getValue());
245 return;
246 }
247 } catch (Exception e) {
248 // ignore. next editor
249 }
250 }
251
252 throw illegalValue(text, targetClass);
253 }
254
255 protected ValueConversionException illegalValue(Object value, Class<?> klass) {
256 throw new ValueConversionException(value, klass);
257 }
258
259 protected ValueConversionException illegalValue(Object value, Class<?> klass, Exception e) {
260 throw new ValueConversionException(value, klass, e);
261 }
262
263 @SuppressWarnings("unchecked")
264 private void initPropertyEditors() {
265 synchronized (lock) {
266 if (propertyEditors == null) {
267 List<WeakReference<PropertyEditor>> editors = new ArrayList<>();
268 for (WeakReference<Class<? extends PropertyEditor>> propertyEditorClass : propertyEditorClasses) {
269 try {
270 Class<? extends PropertyEditor> klass = propertyEditorClass.get();
271 if (klass != null) {
272 editors.add(new WeakReference<>(klass.newInstance()));
273 }
274 } catch (InstantiationException | IllegalAccessException e) {
275 throw new IllegalArgumentException("Can't create instance", e);
276 }
277 }
278
279 if (!editors.isEmpty()) {
280 propertyEditors = editors.toArray(new WeakReference[editors.size()]);
281 } else {
282 throw new IllegalStateException("No available PropertyEditors for " + this);
283 }
284 }
285 }
286 }
287 }
|