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