KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > beans > PropertyEditorRegistrySupport


1 /*
2  * Copyright 2002-2006 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.beans;
18
19 import java.beans.PropertyEditor JavaDoc;
20 import java.io.File JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.math.BigDecimal JavaDoc;
23 import java.math.BigInteger JavaDoc;
24 import java.net.URI JavaDoc;
25 import java.net.URL JavaDoc;
26 import java.util.Collection JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.LinkedList JavaDoc;
30 import java.util.List JavaDoc;
31 import java.util.Locale JavaDoc;
32 import java.util.Map JavaDoc;
33 import java.util.Properties JavaDoc;
34 import java.util.Set JavaDoc;
35 import java.util.SortedMap JavaDoc;
36 import java.util.SortedSet JavaDoc;
37 import java.util.regex.Pattern JavaDoc;
38
39 import org.springframework.beans.propertyeditors.ByteArrayPropertyEditor;
40 import org.springframework.beans.propertyeditors.CharArrayPropertyEditor;
41 import org.springframework.beans.propertyeditors.CharacterEditor;
42 import org.springframework.beans.propertyeditors.ClassArrayEditor;
43 import org.springframework.beans.propertyeditors.ClassEditor;
44 import org.springframework.beans.propertyeditors.CustomBooleanEditor;
45 import org.springframework.beans.propertyeditors.CustomCollectionEditor;
46 import org.springframework.beans.propertyeditors.CustomMapEditor;
47 import org.springframework.beans.propertyeditors.CustomNumberEditor;
48 import org.springframework.beans.propertyeditors.FileEditor;
49 import org.springframework.beans.propertyeditors.InputStreamEditor;
50 import org.springframework.beans.propertyeditors.LocaleEditor;
51 import org.springframework.beans.propertyeditors.PatternEditor;
52 import org.springframework.beans.propertyeditors.PropertiesEditor;
53 import org.springframework.beans.propertyeditors.URIEditor;
54 import org.springframework.beans.propertyeditors.URLEditor;
55 import org.springframework.core.CollectionFactory;
56 import org.springframework.core.JdkVersion;
57 import org.springframework.core.io.Resource;
58 import org.springframework.core.io.support.ResourceArrayPropertyEditor;
59 import org.springframework.util.ClassUtils;
60
61 /**
62  * Base implementation of the PropertyEditorRegistry interface.
63  * Provides management of default editors and custom editors.
64  * Mainly serves as base class for BeanWrapperImpl.
65  *
66  * @author Juergen Hoeller
67  * @since 1.2.6
68  * @see PropertyEditorRegistry
69  * @see BeanWrapperImpl
70  * @see java.beans.PropertyEditorManager
71  * @see java.beans.PropertyEditorSupport#setAsText
72  * @see java.beans.PropertyEditorSupport#setValue
73  */

74 public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
75
76     private Map JavaDoc defaultEditors;
77
78     private Map JavaDoc customEditors;
79
80     private Map JavaDoc customEditorCache;
81
82
83     //---------------------------------------------------------------------
84
// Management of default editors
85
//---------------------------------------------------------------------
86

87     /**
88      * Register default editors in this instance, for restricted environments.
89      * We're not using the JRE's PropertyEditorManager to avoid potential
90      * SecurityExceptions when running in a SecurityManager.
91      * <p>Registers a <code>CustomNumberEditor</code> for all primitive number types,
92      * their corresponding wrapper types, <code>BigInteger</code> and <code>BigDecimal</code>.
93      * @see org.springframework.beans.propertyeditors.ByteArrayPropertyEditor
94      * @see org.springframework.beans.propertyeditors.ClassEditor
95      * @see org.springframework.beans.propertyeditors.CharacterEditor
96      * @see org.springframework.beans.propertyeditors.CustomBooleanEditor
97      * @see org.springframework.beans.propertyeditors.CustomNumberEditor
98      * @see org.springframework.beans.propertyeditors.CustomCollectionEditor
99      * @see org.springframework.beans.propertyeditors.CustomMapEditor
100      * @see org.springframework.beans.propertyeditors.FileEditor
101      * @see org.springframework.beans.propertyeditors.InputStreamEditor
102      * @see org.springframework.jndi.JndiTemplateEditor
103      * @see org.springframework.beans.propertyeditors.LocaleEditor
104      * @see org.springframework.beans.propertyeditors.PropertiesEditor
105      * @see org.springframework.beans.PropertyValuesEditor
106      * @see org.springframework.core.io.support.ResourceArrayPropertyEditor
107      * @see org.springframework.core.io.ResourceEditor
108      * @see org.springframework.transaction.interceptor.TransactionAttributeEditor
109      * @see org.springframework.transaction.interceptor.TransactionAttributeSourceEditor
110      * @see org.springframework.beans.propertyeditors.URLEditor
111      */

112     protected void registerDefaultEditors() {
113         this.defaultEditors = new HashMap JavaDoc(32);
114
115         // Simple editors, without parameterization capabilities.
116
// The JDK does not contain a default editor for any of these target types.
117
this.defaultEditors.put(Class JavaDoc.class, new ClassEditor());
118         this.defaultEditors.put(Class JavaDoc[].class, new ClassArrayEditor());
119         this.defaultEditors.put(File JavaDoc.class, new FileEditor());
120         this.defaultEditors.put(InputStream JavaDoc.class, new InputStreamEditor());
121         this.defaultEditors.put(Locale JavaDoc.class, new LocaleEditor());
122         this.defaultEditors.put(Properties JavaDoc.class, new PropertiesEditor());
123         this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
124         this.defaultEditors.put(URL JavaDoc.class, new URLEditor());
125
126         // Register JDK-1.4-specific editors.
127
if (JdkVersion.isAtLeastJava14()) {
128             this.defaultEditors.put(URI JavaDoc.class, new URIEditor());
129             this.defaultEditors.put(Pattern JavaDoc.class, new PatternEditor());
130         }
131
132         // Default instances of collection editors.
133
// Can be overridden by registering custom instances of those as custom editors.
134
this.defaultEditors.put(Collection JavaDoc.class, new CustomCollectionEditor(Collection JavaDoc.class));
135         this.defaultEditors.put(Set JavaDoc.class, new CustomCollectionEditor(Set JavaDoc.class));
136         this.defaultEditors.put(SortedSet JavaDoc.class, new CustomCollectionEditor(SortedSet JavaDoc.class));
137         this.defaultEditors.put(List JavaDoc.class, new CustomCollectionEditor(List JavaDoc.class));
138         this.defaultEditors.put(SortedMap JavaDoc.class, new CustomMapEditor(SortedMap JavaDoc.class));
139
140         // Default editors for primitive arrays.
141
this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
142         this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());
143
144         // The JDK does not contain a default editor for char!
145
this.defaultEditors.put(char.class, new CharacterEditor(false));
146         this.defaultEditors.put(Character JavaDoc.class, new CharacterEditor(true));
147
148         // Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
149
this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
150         this.defaultEditors.put(Boolean JavaDoc.class, new CustomBooleanEditor(true));
151
152         // The JDK does not contain default editors for number wrapper types!
153
// Override JDK primitive number editors with our own CustomNumberEditor.
154
this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte JavaDoc.class, false));
155         this.defaultEditors.put(Byte JavaDoc.class, new CustomNumberEditor(Byte JavaDoc.class, true));
156         this.defaultEditors.put(short.class, new CustomNumberEditor(Short JavaDoc.class, false));
157         this.defaultEditors.put(Short JavaDoc.class, new CustomNumberEditor(Short JavaDoc.class, true));
158         this.defaultEditors.put(int.class, new CustomNumberEditor(Integer JavaDoc.class, false));
159         this.defaultEditors.put(Integer JavaDoc.class, new CustomNumberEditor(Integer JavaDoc.class, true));
160         this.defaultEditors.put(long.class, new CustomNumberEditor(Long JavaDoc.class, false));
161         this.defaultEditors.put(Long JavaDoc.class, new CustomNumberEditor(Long JavaDoc.class, true));
162         this.defaultEditors.put(float.class, new CustomNumberEditor(Float JavaDoc.class, false));
163         this.defaultEditors.put(Float JavaDoc.class, new CustomNumberEditor(Float JavaDoc.class, true));
164         this.defaultEditors.put(double.class, new CustomNumberEditor(Double JavaDoc.class, false));
165         this.defaultEditors.put(Double JavaDoc.class, new CustomNumberEditor(Double JavaDoc.class, true));
166         this.defaultEditors.put(BigDecimal JavaDoc.class, new CustomNumberEditor(BigDecimal JavaDoc.class, true));
167         this.defaultEditors.put(BigInteger JavaDoc.class, new CustomNumberEditor(BigInteger JavaDoc.class, true));
168     }
169
170     /**
171      * Retrieve the default editor for the given property type, if any.
172      * @param requiredType type of the property
173      * @return the default editor, or <code>null</code> if none found
174      */

175     protected PropertyEditor JavaDoc getDefaultEditor(Class JavaDoc requiredType) {
176         if (this.defaultEditors == null) {
177             return null;
178         }
179         return (PropertyEditor JavaDoc) this.defaultEditors.get(requiredType);
180     }
181
182     /**
183      * Copy the default editors registered in this instance to the given target registry.
184      * @param target the target registry to copy to
185      */

186     protected void copyDefaultEditorsTo(PropertyEditorRegistrySupport target) {
187         target.defaultEditors = this.defaultEditors;
188     }
189
190
191     //---------------------------------------------------------------------
192
// Management of custom editors
193
//---------------------------------------------------------------------
194

195     public void registerCustomEditor(Class JavaDoc requiredType, PropertyEditor JavaDoc propertyEditor) {
196         registerCustomEditor(requiredType, null, propertyEditor);
197     }
198
199     public void registerCustomEditor(Class JavaDoc requiredType, String JavaDoc propertyPath, PropertyEditor JavaDoc propertyEditor) {
200         if (requiredType == null && propertyPath == null) {
201             throw new IllegalArgumentException JavaDoc("Either requiredType or propertyPath is required");
202         }
203         if (this.customEditors == null) {
204             this.customEditors = CollectionFactory.createLinkedMapIfPossible(16);
205         }
206         if (propertyPath != null) {
207             this.customEditors.put(propertyPath, new CustomEditorHolder(propertyEditor, requiredType));
208         }
209         else {
210             this.customEditors.put(requiredType, propertyEditor);
211             this.customEditorCache = null;
212         }
213     }
214
215     public PropertyEditor JavaDoc findCustomEditor(Class JavaDoc requiredType, String JavaDoc propertyPath) {
216         if (this.customEditors == null) {
217             return null;
218         }
219         Class JavaDoc requiredTypeToUse = requiredType;
220         if (propertyPath != null) {
221             // Check property-specific editor first.
222
PropertyEditor JavaDoc editor = getCustomEditor(propertyPath, requiredType);
223             if (editor == null) {
224                 List JavaDoc strippedPaths = new LinkedList JavaDoc();
225                 addStrippedPropertyPaths(strippedPaths, "", propertyPath);
226                 for (Iterator JavaDoc it = strippedPaths.iterator(); it.hasNext() && editor == null;) {
227                     String JavaDoc strippedPath = (String JavaDoc) it.next();
228                     editor = getCustomEditor(strippedPath, requiredType);
229                 }
230             }
231             if (editor != null) {
232                 return editor;
233             }
234             else if (requiredType == null) {
235                 requiredTypeToUse = getPropertyType(propertyPath);
236             }
237         }
238         // No property-specific editor -> check type-specific editor.
239
return getCustomEditor(requiredTypeToUse);
240     }
241
242     /**
243      * Determine whether this registry contains a custom editor
244      * for the specified array/collection element.
245      * @param elementType the target type of the element
246      * @param propertyPath the property path (typically of the array/collection)
247      * @return whether a matching custom editor has been found
248      */

249     public boolean hasCustomEditorForElement(Class JavaDoc elementType, String JavaDoc propertyPath) {
250         if (this.customEditors == null) {
251             return false;
252         }
253         for (Iterator JavaDoc it = this.customEditors.entrySet().iterator(); it.hasNext();) {
254             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
255             if (entry.getKey() instanceof String JavaDoc) {
256                 String JavaDoc regPath = (String JavaDoc) entry.getKey();
257                 if (PropertyAccessorUtils.matchesProperty(regPath, propertyPath)) {
258                     CustomEditorHolder editorHolder = (CustomEditorHolder) entry.getValue();
259                     if (editorHolder.getPropertyEditor(elementType) != null) {
260                         return true;
261                     }
262                 }
263             }
264         }
265         // No property-specific editor -> check type-specific editor.
266
return this.customEditors.containsKey(elementType);
267     }
268
269     /**
270      * Determine the property type for the given property path.
271      * Called by <code>findCustomEditor</code> if no required type has been specified,
272      * to be able to find a type-specific editor even if just given a property path.
273      * <p>Default implementation always returns <code>null</code>.
274      * BeanWrapperImpl overrides this with the standard <code>getPropertyType</code>
275      * method as defined by the BeanWrapper interface.
276      * @param propertyPath the property path to determine the type for
277      * @return the type of the property, or <code>null</code> if not determinable
278      * @see BeanWrapper#getPropertyType(String)
279      */

280     protected Class JavaDoc getPropertyType(String JavaDoc propertyPath) {
281         return null;
282     }
283
284     /**
285      * Get custom editor that has been registered for the given property.
286      * @return the custom editor, or <code>null</code> if none specific for this property
287      */

288     private PropertyEditor JavaDoc getCustomEditor(String JavaDoc propertyName, Class JavaDoc requiredType) {
289         CustomEditorHolder holder = (CustomEditorHolder) this.customEditors.get(propertyName);
290         return (holder != null ? holder.getPropertyEditor(requiredType) : null);
291     }
292
293     /**
294      * Get custom editor for the given type. If no direct match found,
295      * try custom editor for superclass (which will in any case be able
296      * to render a value as String via <code>getAsText</code>).
297      * @return the custom editor, or <code>null</code> if none found for this type
298      * @see java.beans.PropertyEditor#getAsText()
299      */

300     private PropertyEditor JavaDoc getCustomEditor(Class JavaDoc requiredType) {
301         if (requiredType == null) {
302             return null;
303         }
304         // Check directly registered editor for type.
305
PropertyEditor JavaDoc editor = (PropertyEditor JavaDoc) this.customEditors.get(requiredType);
306         if (editor == null) {
307             // Check cached editor for type, registered for superclass or interface.
308
if (this.customEditorCache != null) {
309                 editor = (PropertyEditor JavaDoc) this.customEditorCache.get(requiredType);
310             }
311             if (editor == null) {
312                 // Find editor for superclass or interface.
313
for (Iterator JavaDoc it = this.customEditors.keySet().iterator(); it.hasNext() && editor == null;) {
314                     Object JavaDoc key = it.next();
315                     if (key instanceof Class JavaDoc && ((Class JavaDoc) key).isAssignableFrom(requiredType)) {
316                         editor = (PropertyEditor JavaDoc) this.customEditors.get(key);
317                         // Cache editor for search type, to avoid the overhead
318
// of repeated assignable-from checks.
319
if (this.customEditorCache == null) {
320                             this.customEditorCache = new HashMap JavaDoc();
321                         }
322                         this.customEditorCache.put(requiredType, editor);
323                     }
324                 }
325             }
326         }
327         return editor;
328     }
329
330     /**
331      * Guess the property type of the specified property from the registered
332      * custom editors (provided that they were registered for a specific type).
333      * @param propertyName the name of the property
334      * @return the property type, or <code>null</code> if not determinable
335      */

336     protected Class JavaDoc guessPropertyTypeFromEditors(String JavaDoc propertyName) {
337         if (this.customEditors != null) {
338             CustomEditorHolder editorHolder = (CustomEditorHolder) this.customEditors.get(propertyName);
339             if (editorHolder == null) {
340                 List JavaDoc strippedPaths = new LinkedList JavaDoc();
341                 addStrippedPropertyPaths(strippedPaths, "", propertyName);
342                 for (Iterator JavaDoc it = strippedPaths.iterator(); it.hasNext() && editorHolder == null;) {
343                     String JavaDoc strippedName = (String JavaDoc) it.next();
344                     editorHolder = (CustomEditorHolder) this.customEditors.get(strippedName);
345                 }
346             }
347             if (editorHolder != null) {
348                 return editorHolder.getRegisteredType();
349             }
350         }
351         return null;
352     }
353
354     /**
355      * Copy the custom editors registered in this instance to the given target registry.
356      * @param target the target registry to copy to
357      * @param nestedProperty the nested property path of the target registry, if any.
358      * If this is non-null, only editors registered for a path below this nested property
359      * will be copied.
360      */

361     protected void copyCustomEditorsTo(PropertyEditorRegistry target, String JavaDoc nestedProperty) {
362         String JavaDoc actualPropertyName =
363                 (nestedProperty != null ? PropertyAccessorUtils.getPropertyName(nestedProperty) : null);
364         if (this.customEditors != null) {
365             for (Iterator JavaDoc it = this.customEditors.entrySet().iterator(); it.hasNext();) {
366                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
367                 if (entry.getKey() instanceof Class JavaDoc) {
368                     Class JavaDoc requiredType = (Class JavaDoc) entry.getKey();
369                     PropertyEditor JavaDoc editor = (PropertyEditor JavaDoc) entry.getValue();
370                     target.registerCustomEditor(requiredType, editor);
371                 }
372                 else if (entry.getKey() instanceof String JavaDoc & nestedProperty != null) {
373                     String JavaDoc editorPath = (String JavaDoc) entry.getKey();
374                     int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(editorPath);
375                     if (pos != -1) {
376                         String JavaDoc editorNestedProperty = editorPath.substring(0, pos);
377                         String JavaDoc editorNestedPath = editorPath.substring(pos + 1);
378                         if (editorNestedProperty.equals(nestedProperty) || editorNestedProperty.equals(actualPropertyName)) {
379                             CustomEditorHolder editorHolder = (CustomEditorHolder) entry.getValue();
380                             target.registerCustomEditor(
381                                     editorHolder.getRegisteredType(), editorNestedPath, editorHolder.getPropertyEditor());
382                         }
383                     }
384                 }
385             }
386         }
387     }
388
389
390     /**
391      * Add property paths with all variations of stripped keys and/or indexes.
392      * Invokes itself recursively with nested paths.
393      * @param strippedPaths the result list to add to
394      * @param nestedPath the current nested path
395      * @param propertyPath the property path to check for keys/indexes to strip
396      */

397     private void addStrippedPropertyPaths(List JavaDoc strippedPaths, String JavaDoc nestedPath, String JavaDoc propertyPath) {
398         int startIndex = propertyPath.indexOf(PropertyAccessor.PROPERTY_KEY_PREFIX_CHAR);
399         if (startIndex != -1) {
400             int endIndex = propertyPath.indexOf(PropertyAccessor.PROPERTY_KEY_SUFFIX_CHAR);
401             if (endIndex != -1) {
402                 String JavaDoc prefix = propertyPath.substring(0, startIndex);
403                 String JavaDoc key = propertyPath.substring(startIndex, endIndex + 1);
404                 String JavaDoc suffix = propertyPath.substring(endIndex + 1, propertyPath.length());
405                 // Strip the first key.
406
strippedPaths.add(nestedPath + prefix + suffix);
407                 // Search for further keys to strip, with the first key stripped.
408
addStrippedPropertyPaths(strippedPaths, nestedPath + prefix, suffix);
409                 // Search for further keys to strip, with the first key not stripped.
410
addStrippedPropertyPaths(strippedPaths, nestedPath + prefix + key, suffix);
411             }
412         }
413     }
414
415
416     /**
417      * Holder for a registered custom editor with property name.
418      * Keeps the PropertyEditor itself plus the type it was registered for.
419      */

420     private static class CustomEditorHolder {
421
422         private final PropertyEditor JavaDoc propertyEditor;
423
424         private final Class JavaDoc registeredType;
425
426         private CustomEditorHolder(PropertyEditor JavaDoc propertyEditor, Class JavaDoc registeredType) {
427             this.propertyEditor = propertyEditor;
428             this.registeredType = registeredType;
429         }
430
431         private PropertyEditor JavaDoc getPropertyEditor() {
432             return this.propertyEditor;
433         }
434
435         private Class JavaDoc getRegisteredType() {
436             return this.registeredType;
437         }
438
439         private PropertyEditor JavaDoc getPropertyEditor(Class JavaDoc requiredType) {
440             // Special case: If no required type specified, which usually only happens for
441
// Collection elements, or required type is not assignable to registered type,
442
// which usually only happens for generic properties of type Object -
443
// then return PropertyEditor if not registered for Collection or array type.
444
// (If not registered for Collection or array, it is assumed to be intended
445
// for elements.)
446
if (this.registeredType == null ||
447                     (requiredType != null &&
448                     (ClassUtils.isAssignable(this.registeredType, requiredType) ||
449                     ClassUtils.isAssignable(requiredType, this.registeredType))) ||
450                     (requiredType == null &&
451                     (!Collection JavaDoc.class.isAssignableFrom(this.registeredType) && !this.registeredType.isArray()))) {
452                 return this.propertyEditor;
453             }
454             else {
455                 return null;
456             }
457         }
458     }
459
460 }
461
Popular Tags