PropertyEditorChain.java
001 /*
002  * Copyright 2008-2016 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     @Nullable
062     public String getFormat() {
063         return format;
064     }
065 
066     public void setFormat(@Nullable String format) {
067         this.format = format;
068     }
069 
070     public int getSize() {
071         return propertyEditorClasses.length;
072     }
073 
074     @SuppressWarnings("unchecked")
075     public PropertyEditorChain copyOf() {
076         List<Class<? extends PropertyEditor>> classes = new ArrayList<>();
077         for (WeakReference<Class<? extends PropertyEditor>> reference : propertyEditorClasses) {
078             if (reference.get() != null) {
079                 classes.add(reference.get());
080             }
081         }
082         return new PropertyEditorChain(targetClass, classes.toArray(new Class[classes.size()]));
083     }
084 
085     @SuppressWarnings("unchecked")
086     public PropertyEditorChain copyOf(Class<? extends PropertyEditor> propertyEditorClass) {
087         requireNonNull(propertyEditorClass, "Argument 'propertyEditorClass' must not be null");
088         List<Class<? extends PropertyEditor>> classes = new ArrayList<>();
089         for (WeakReference<Class<? extends PropertyEditor>> reference : propertyEditorClasses) {
090             if (reference.get() != null) {
091                 classes.add(reference.get());
092             }
093         }
094         if (!classes.contains(propertyEditorClass)) {
095             classes.add(propertyEditorClass);
096         }
097         return new PropertyEditorChain(targetClass, classes.toArray(new Class[classes.size()]));
098     }
099 
100     @Override
101     public String toString() {
102         StringBuilder sb = new StringBuilder(super.toString());
103         sb.append("[").append(targetClass.getName()).append(']');
104         return sb.toString();
105     }
106 
107     @Override
108     public String getAsText() {
109         return isBlank(getFormat()) ? getAsTextInternal() : getFormattedValue();
110     }
111 
112     @Override
113     public void setAsText(String strthrows IllegalArgumentException {
114         if (isBlank(getFormat())) {
115             setAsTextInternal(str);
116         else {
117             setFormattedValue(str);
118         }
119     }
120 
121     @Override
122     public void setValue(Object value) {
123         if (value instanceof CharSequence) {
124             setFormattedValue(String.valueOf(value));
125         else {
126             setValueInternal(value);
127         }
128     }
129 
130     @SuppressWarnings({"unchecked""rawtypes"})
131     public String getFormattedValue() {
132         initPropertyEditors();
133 
134         Object value = super.getValue();
135         for (WeakReference<PropertyEditor> reference : propertyEditors) {
136             try {
137                 PropertyEditor propertyEditor = reference.get();
138                 if (propertyEditor != null && propertyEditor instanceof ExtendedPropertyEditor) {
139                     ExtendedPropertyEditor extendedPropertyEditor = (ExtendedPropertyEditorpropertyEditor;
140                     extendedPropertyEditor.setFormat(format);
141                     extendedPropertyEditor.setValue(value);
142                     return extendedPropertyEditor.getFormattedValue();
143                 }
144             catch (Exception e) {
145                 // ignore. next editor
146             }
147         }
148 
149         throw illegalValue(value, targetClass);
150     }
151 
152     @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
153     public void setFormattedValue(String value) {
154         initPropertyEditors();
155 
156         for (WeakReference<PropertyEditor> reference : propertyEditors) {
157             try {
158                 PropertyEditor propertyEditor = reference.get();
159                 if (propertyEditor != null && propertyEditor instanceof ExtendedPropertyEditor) {
160                     ExtendedPropertyEditor extendedPropertyEditor = (ExtendedPropertyEditorpropertyEditor;
161                     extendedPropertyEditor.setFormat(format);
162                     extendedPropertyEditor.setValue(value);
163                     super.setValue(extendedPropertyEditor.getValue());
164                     return;
165                 }
166             catch (Exception e) {
167                 // ignore. next editor
168             }
169         }
170 
171         throw illegalValue(value, targetClass);
172     }
173 
174     protected void setValueInternal(Object valuethrows IllegalArgumentException {
175         initPropertyEditors();
176 
177         for (WeakReference<PropertyEditor> reference : propertyEditors) {
178             try {
179                 PropertyEditor propertyEditor = reference.get();
180                 if (propertyEditor != null) {
181                     propertyEditor.setValue(value);
182                     super.setValue(propertyEditor.getValue());
183                     return;
184                 }
185             catch (Exception e) {
186                 // ignore. next editor
187             }
188         }
189 
190         throw illegalValue(value, targetClass);
191     }
192 
193     protected Object getValueInternal() {
194         initPropertyEditors();
195 
196         Object value = super.getValue();
197         for (WeakReference<PropertyEditor> reference : propertyEditors) {
198             try {
199                 PropertyEditor propertyEditor = reference.get();
200                 if (propertyEditor != null) {
201                     propertyEditor.setValue(value);
202                     return propertyEditor.getValue();
203                 }
204             catch (Exception e) {
205                 // ignore. next editor
206             }
207         }
208 
209         throw illegalValue(value, targetClass);
210     }
211 
212     protected String getAsTextInternal() {
213         initPropertyEditors();
214 
215         Object value = super.getValue();
216 
217         for (WeakReference<PropertyEditor> reference : propertyEditors) {
218             try {
219                 PropertyEditor propertyEditor = reference.get();
220                 if (propertyEditor != null) {
221                     propertyEditor.setValue(value);
222                     return propertyEditor.getAsText();
223                 }
224             catch (Exception e) {
225                 // ignore. next editor
226             }
227         }
228 
229         throw illegalValue(value, targetClass);
230     }
231 
232     protected void setAsTextInternal(String textthrows IllegalArgumentException {
233         initPropertyEditors();
234 
235         for (WeakReference<PropertyEditor> reference : propertyEditors) {
236             try {
237                 PropertyEditor propertyEditor = reference.get();
238                 if (propertyEditor != null) {
239                     propertyEditor.setAsText(text);
240                     super.setValue(propertyEditor.getValue());
241                     return;
242                 }
243             catch (Exception e) {
244                 // ignore. next editor
245             }
246         }
247 
248         throw illegalValue(text, targetClass);
249     }
250 
251     protected ValueConversionException illegalValue(Object value, Class<?> klass) {
252         throw new ValueConversionException(value, klass);
253     }
254 
255     protected ValueConversionException illegalValue(Object value, Class<?> klass, Exception e) {
256         throw new ValueConversionException(value, klass, e);
257     }
258 
259     @SuppressWarnings("unchecked")
260     private void initPropertyEditors() {
261         synchronized (lock) {
262             if (propertyEditors == null) {
263                 List<WeakReference<PropertyEditor>> editors = new ArrayList<>();
264                 for (WeakReference<Class<? extends PropertyEditor>> propertyEditorClass : propertyEditorClasses) {
265                     try {
266                         Class<? extends PropertyEditor> klass = propertyEditorClass.get();
267                         if (klass != null) {
268                             editors.add(new WeakReference<>(klass.newInstance()));
269                         }
270                     catch (InstantiationException | IllegalAccessException e) {
271                         throw new IllegalArgumentException("Can't create instance", e);
272                     }
273                 }
274 
275                 if (editors.size() 0) {
276                     propertyEditors = editors.toArray(new WeakReference[editors.size()]);
277                 else {
278                     throw new IllegalStateException("No available PropertyEditors for " this);
279                 }
280             }
281         }
282     }
283 }