PropertyEditorChain.java
001 /*
002  * Copyright 2008-2015 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.concurrent.GuardedBy;
020 import java.beans.PropertyEditor;
021 import java.beans.PropertyEditorSupport;
022 import java.lang.ref.WeakReference;
023 import java.util.ArrayList;
024 import java.util.List;
025 
026 import static griffon.util.GriffonClassUtils.requireNonEmpty;
027 import static java.util.Objects.requireNonNull;
028 
029 /**
030  @author Andres Almiray
031  @since 2.4.0
032  */
033 public class PropertyEditorChain extends PropertyEditorSupport {
034     private final Class<?> targetClass;
035     private final Object lock = new Object[0];
036     private final WeakReference<Class<? extends PropertyEditor>>[] propertyEditorClasses;
037     @GuardedBy("lock")
038     private WeakReference<PropertyEditor>[] propertyEditors;
039 
040     @SuppressWarnings("unchecked")
041     public PropertyEditorChain(@Nonnull Class<?> targetClass, @Nonnull Class<? extends PropertyEditor>[] propertyEditorClasses) {
042         this.targetClass = requireNonNull(targetClass, "Argument 'targetClass' must not be null");
043         requireNonEmpty(propertyEditorClasses, "Argument 'propertyEditorClasses' must not be null nor empty");
044         this.propertyEditorClasses = new WeakReference[propertyEditorClasses.length];
045         for (int i = 0; i < propertyEditorClasses.length; i++) {
046             this.propertyEditorClasses[inew WeakReference<Class<? extends PropertyEditor>>(propertyEditorClasses[i]);
047         }
048     }
049 
050     @SuppressWarnings("unchecked")
051     public PropertyEditorChain copyOf() {
052         List<Class<? extends PropertyEditor>> classes = new ArrayList<>();
053         for (WeakReference<Class<? extends PropertyEditor>> reference : propertyEditorClasses) {
054             if (reference.get() != null) {
055                 classes.add(reference.get());
056             }
057         }
058         return new PropertyEditorChain(targetClass, classes.toArray(new Class[classes.size()]));
059     }
060 
061     @SuppressWarnings("unchecked")
062     public PropertyEditorChain copyOf(Class<? extends PropertyEditor> propertyEditorClass) {
063         requireNonNull(propertyEditorClass, "Argument 'propertyEditorClass' must not be null");
064         List<Class<? extends PropertyEditor>> classes = new ArrayList<>();
065         for (WeakReference<Class<? extends PropertyEditor>> reference : propertyEditorClasses) {
066             if (reference.get() != null) {
067                 classes.add(reference.get());
068             }
069         }
070         classes.add(propertyEditorClass);
071         return new PropertyEditorChain(targetClass, classes.toArray(new Class[classes.size()]));
072     }
073 
074     @Override
075     public String toString() {
076         StringBuilder sb = new StringBuilder(super.toString());
077         sb.append("[").append(targetClass.getName()).append(']');
078         return sb.toString();
079     }
080 
081     @Override
082     public Object getValue() {
083         initPropertyEditors();
084 
085         Object value = super.getValue();
086         for (WeakReference<PropertyEditor> reference : propertyEditors) {
087             try {
088                 PropertyEditor propertyEditor = reference.get();
089                 if (propertyEditor != null) {
090                     propertyEditor.setValue(value);
091                     return propertyEditor.getValue();
092                 }
093             catch (Exception e) {
094                 // ignore. next editor
095             }
096         }
097 
098         throw illegalValue(value, targetClass);
099     }
100 
101     @Override
102     public String getAsText() {
103         initPropertyEditors();
104 
105         Object value = super.getValue();
106 
107         for (WeakReference<PropertyEditor> reference : propertyEditors) {
108             try {
109                 PropertyEditor propertyEditor = reference.get();
110                 if (propertyEditor != null) {
111                     propertyEditor.setValue(value);
112                     return propertyEditor.getAsText();
113                 }
114             catch (Exception e) {
115                 // ignore. next editor
116             }
117         }
118 
119         throw illegalValue(value, targetClass);
120     }
121 
122     @Override
123     public void setAsText(String textthrows IllegalArgumentException {
124         initPropertyEditors();
125 
126         for (WeakReference<PropertyEditor> reference : propertyEditors) {
127             try {
128                 PropertyEditor propertyEditor = reference.get();
129                 if (propertyEditor != null) {
130                     propertyEditor.setAsText(text);
131                     super.setValue(propertyEditor.getValue());
132                     return;
133                 }
134             catch (Exception e) {
135                 // ignore. next editor
136             }
137         }
138 
139         throw illegalValue(text, targetClass);
140     }
141 
142     protected ValueConversionException illegalValue(Object value, Class<?> klass) {
143         throw new ValueConversionException(value, klass);
144     }
145 
146     protected ValueConversionException illegalValue(Object value, Class<?> klass, Exception e) {
147         throw new ValueConversionException(value, klass, e);
148     }
149 
150     @SuppressWarnings("unchecked")
151     private void initPropertyEditors() {
152         synchronized (lock) {
153             if (propertyEditors == null) {
154                 List<WeakReference<PropertyEditor>> editors = new ArrayList<>();
155                 for (WeakReference<Class<? extends PropertyEditor>> propertyEditorClass : propertyEditorClasses) {
156                     try {
157                         Class<? extends PropertyEditor> klass = propertyEditorClass.get();
158                         if (klass != null) {
159                             editors.add(new WeakReference<>(klass.newInstance()));
160                         }
161                     catch (InstantiationException | IllegalAccessException e) {
162                         throw new IllegalArgumentException("Can't create instance", e);
163                     }
164                 }
165 
166                 if (editors.size() 0) {
167                     propertyEditors = editors.toArray(new WeakReference[editors.size()]);
168                 else {
169                     throw new IllegalStateException("No available PropertyEditors for " this);
170                 }
171             }
172         }
173     }
174 }