PropertyEditorChain.java
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 strthrows 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 = (ExtendedPropertyEditorpropertyEditor;
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 = (ExtendedPropertyEditorpropertyEditor;
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 valuethrows 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 textthrows 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 }