001 /*
002 * Copyright 2002-2004 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * 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, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package org.springframework.richclient.application.support;
017
018 import java.beans.PropertyEditor;
019 import java.lang.reflect.Modifier;
020 import java.math.BigDecimal;
021 import java.text.DateFormat;
022 import java.util.Date;
023 import java.util.HashMap;
024 import java.util.Iterator;
025 import java.util.List;
026 import java.util.Map;
027 import java.util.Properties;
028 import java.util.Set;
029
030 import org.apache.commons.logging.Log;
031 import org.apache.commons.logging.LogFactory;
032 import org.springframework.beans.propertyeditors.ClassEditor;
033 import org.springframework.beans.propertyeditors.CustomBooleanEditor;
034 import org.springframework.beans.propertyeditors.CustomDateEditor;
035 import org.springframework.beans.propertyeditors.CustomNumberEditor;
036 import org.springframework.richclient.util.ClassUtils;
037 import org.springframework.util.Assert;
038
039 /**
040 * This provides a default implementation of {@link PropertyEditorRegistry}
041 *
042 * @author Jim Moore
043 */
044 public class DefaultPropertyEditorRegistry
045 //implements PropertyEditorRegistry
046 {
047 private static final Log logger = LogFactory.getLog(
048 DefaultPropertyEditorRegistry.class);
049
050 private Map propertyEditorByClass = new HashMap();
051
052 private Map propertyEditorByClassAndProperty = new HashMap();
053
054 public DefaultPropertyEditorRegistry() {
055 initDefaultEditors();
056 }
057
058 /**
059 * Initialize the default property editors, covering primitive and other basic value types.
060 */
061 protected void initDefaultEditors() {
062 setPropertyEditor(int.class, DefaultIntegerEditor.class);
063 setPropertyEditor(Integer.class, DefaultIntegerEditor.class);
064 setPropertyEditor(long.class, DefaultLongEditor.class);
065 setPropertyEditor(Long.class, DefaultLongEditor.class);
066 setPropertyEditor(float.class, DefaultFloatEditor.class);
067 setPropertyEditor(Float.class, DefaultFloatEditor.class);
068 setPropertyEditor(double.class, DefaultDoubleEditor.class);
069 setPropertyEditor(Double.class, DefaultDoubleEditor.class);
070 setPropertyEditor(BigDecimal.class, DefaultBigDecimalEditor.class);
071 setPropertyEditor(Date.class, DefaultDateEditor.class);
072 }
073
074 private static class DefaultIntegerEditor extends CustomNumberEditor {
075 public DefaultIntegerEditor() {
076 super(Integer.class, true);
077 }
078 }
079
080 private static class DefaultLongEditor extends CustomNumberEditor {
081 public DefaultLongEditor() {
082 super(Long.class, true);
083 }
084 }
085
086 private static class DefaultFloatEditor extends CustomNumberEditor {
087 public DefaultFloatEditor() {
088 super(Float.class, true);
089 }
090 }
091
092 private static class DefaultDoubleEditor extends CustomNumberEditor {
093 public DefaultDoubleEditor() {
094 super(Double.class, true);
095 }
096 }
097
098 private static class DefaultBigDecimalEditor extends CustomNumberEditor {
099 public DefaultBigDecimalEditor() {
100 super(BigDecimal.class, true);
101 }
102 }
103
104 private static class DefaultBooleanEditor extends CustomBooleanEditor {
105 public DefaultBooleanEditor() {
106 super(true);
107 }
108 }
109
110 private static class DefaultDateEditor extends CustomDateEditor {
111 public DefaultDateEditor() {
112 super(DateFormat.getDateInstance(), true);
113 }
114 }
115
116 /**
117 * Adds a list of property editors to the registry extracting the object
118 * class, property name and property editor class from the properties
119 * "objectClass", "propertyName" and "propertyEditorClass".
120 *
121 * @param propertyEditors
122 * the list of property editors. Each element is expected to be
123 * an instance of <code>java.lang.Properties</code>.
124 * @see DefaultPropertyEditorRegistry#setPropertyEditor(Properties)
125 */
126 public void setPropertyEditors(List propertyEditors) {
127 for (Iterator i = propertyEditors.iterator(); i.hasNext();) {
128 setPropertyEditor((Properties)i.next());
129 }
130 }
131
132 /**
133 * Adds a property editor to the registry extracting the object class,
134 * property name and property editor class from the properties
135 * "objectClass", "propertyName" and "propertyEditorClass".
136 *
137 * @param properties
138 * the properties
139 */
140 public void setPropertyEditor(Properties properties) {
141 ClassEditor classEditor = new ClassEditor();
142
143 Class objectClass = null;
144 String propertyName = null;
145 Class propertyEditorClass = null;
146
147 if (properties.get("objectClass") != null) {
148 classEditor.setAsText((String) properties.get("objectClass"));
149 objectClass = (Class) classEditor.getValue();
150 }
151 propertyName = (String) properties.get("propertyName");
152 if (properties.get("propertyEditorClass") != null) {
153 classEditor.setAsText((String) properties.get("propertyEditorClass"));
154 propertyEditorClass = (Class) classEditor.getValue();
155 } else {
156 throw new IllegalArgumentException("propertyEditorClass is required");
157 }
158
159 if (propertyName != null) {
160 setPropertyEditor(objectClass, propertyName, propertyEditorClass);
161 } else if (objectClass != null) {
162 setPropertyEditor(objectClass, propertyEditorClass);
163 } else {
164 throw new IllegalArgumentException("objectClass and/or propertyName are required");
165 }
166 }
167
168
169 public void setPropertyEditor(final Class typeClass,
170 final Class propertyEditorClass) {
171 Assert.notNull(typeClass);
172 verifyPropertyEditorClass(propertyEditorClass);
173
174 if (logger.isDebugEnabled()) {
175 logger.debug("Setting " + propertyEditorClass +
176 " as the property editor for " + typeClass);
177 }
178 this.propertyEditorByClass.put(typeClass, propertyEditorClass);
179 }
180
181
182 private void verifyPropertyEditorClass(final Class propertyEditorClass) {
183 // do some checks so we "fail fast"
184 Assert.notNull(propertyEditorClass);
185 Assert.isTrue(PropertyEditor.class.isAssignableFrom(propertyEditorClass),
186 propertyEditorClass + " is not a " + PropertyEditor.class);
187 try {
188 Assert.isTrue(Modifier.isPublic(propertyEditorClass.getConstructor(null).getModifiers()),
189 propertyEditorClass + " does not have a public no-arg constructor");
190 }
191 catch (NoSuchMethodException e) {
192 throw new IllegalArgumentException(propertyEditorClass +
193 " does not have a no-arg constructor");
194 }
195 }
196
197
198 public void setPropertyEditor(final Class objectType,
199 final String propertyName,
200 final Class propertyEditorClass) {
201 Assert.notNull(objectType);
202 Assert.notNull(propertyName);
203 Assert.isTrue(ClassUtils.isAProperty(objectType, propertyName), "'" + propertyName + "' is not a property of " + objectType);
204 verifyPropertyEditorClass(propertyEditorClass);
205 if (logger.isDebugEnabled()) {
206 logger.debug("Setting " + propertyEditorClass +
207 " as the property editor for the '" + propertyName +
208 "' property of " + objectType);
209 }
210 final ClassAndPropertyKey key =
211 new ClassAndPropertyKey(objectType, propertyName);
212 this.propertyEditorByClassAndProperty.put(key, propertyEditorClass);
213 }
214
215
216 public PropertyEditor getPropertyEditor(final Class typeClass) {
217 final Class editorClass = (Class)ClassUtils.getValueFromMapForClass(
218 typeClass, this.propertyEditorByClass);
219
220 if (editorClass == null) {
221 if (logger.isDebugEnabled()) {
222 logger.debug("Could not find a property editor for " +
223 typeClass);
224 }
225 return null;
226 }
227
228 return instantiatePropertyEditor(editorClass);
229 }
230
231
232 public PropertyEditor getPropertyEditor(final Class objectType,
233 final String propertyName) {
234 final ClassAndPropertyKey key = new ClassAndPropertyKey(objectType,
235 propertyName);
236 Class editorClass =
237 (Class)this.propertyEditorByClassAndProperty.get(key);
238
239 if (editorClass != null) {
240 return instantiatePropertyEditor(editorClass);
241 }
242
243 // maybe it's registered under a different class...
244 final Set keys = this.propertyEditorByClassAndProperty.keySet();
245 final Map map = new HashMap();
246 for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
247 ClassAndPropertyKey propertyKey = (ClassAndPropertyKey)iterator.next();
248 if (propertyName.equals(propertyKey.getPropertyName())) {
249 map.put(propertyKey.getTheClass(),
250 this.propertyEditorByClassAndProperty.get(propertyKey));
251 }
252 }
253
254 editorClass =
255 (Class)ClassUtils.getValueFromMapForClass(objectType, map);
256 if (editorClass != null) {
257 // remember the lookup so it doesn't have to be discovered again
258 setPropertyEditor(objectType, propertyName, editorClass);
259 return instantiatePropertyEditor(editorClass);
260 }
261
262 if (logger.isDebugEnabled()) {
263 logger.debug("Could not find a property editor for the " +
264 propertyName + " property of " + objectType +
265 ", so looking for it by class type");
266 }
267
268 // didn't find it directly, so look for it by the class
269 final Class propertyClass =
270 ClassUtils.getPropertyClass(objectType, propertyName);
271
272 return getPropertyEditor(propertyClass);
273 }
274
275
276 private static PropertyEditor instantiatePropertyEditor(Class propEdClass) {
277 try {
278 return (PropertyEditor)propEdClass.newInstance();
279 }
280 catch (InstantiationException e) {
281 IllegalStateException exp = new IllegalStateException(
282 "Could not instantiate " + propEdClass);
283 exp.initCause(e);
284 throw exp;
285 }
286 catch (IllegalAccessException e) {
287 IllegalStateException exp = new IllegalStateException(
288 "Could not instantiate " + propEdClass);
289 exp.initCause(e);
290 throw exp;
291 }
292 }
293
294
295 //***********************************************************************
296 // INNER CLASSES
297 //***********************************************************************
298
299 private static class ClassAndPropertyKey {
300 private Class theClass;
301 private String propertyName;
302
303 public ClassAndPropertyKey(Class theClass, String propertyName) {
304 if (theClass == null || propertyName == null) throw new NullPointerException();
305 this.propertyName = propertyName;
306 this.theClass = theClass;
307 }
308
309 public String getPropertyName() {
310 return propertyName;
311 }
312
313 public Class getTheClass() {
314 return theClass;
315 }
316
317
318 public boolean equals(Object o) {
319 if (this == o) return true;
320 if (!(o instanceof ClassAndPropertyKey)) return false;
321
322 final ClassAndPropertyKey classAndPropertyKey = (ClassAndPropertyKey)o;
323
324 if (propertyName != null ?
325 !propertyName.equals(classAndPropertyKey.propertyName) :
326 classAndPropertyKey.propertyName != null)
327 return false;
328 if (theClass != null ?
329 !theClass.equals(classAndPropertyKey.theClass) :
330 classAndPropertyKey.theClass != null)
331 return false;
332
333 return true;
334 }
335
336
337 public int hashCode() {
338 int result;
339 result = (theClass != null ? theClass.hashCode() : 0);
340 result = 29 * result +
341 (propertyName != null ? propertyName.hashCode() : 0);
342 return result;
343 }
344
345 }
346
347 }