001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.xbean.propertyeditor;
018    
019    import java.beans.PropertyEditor;
020    import java.beans.PropertyEditorManager;
021    import java.util.Collections;
022    import java.util.HashMap;
023    import java.util.Map;
024    
025    /**
026     * The property editor manager.  This orchestrates Geronimo usage of
027     * property editors, allowing additional search paths to be added and
028     * specific editors to be registered.
029     *
030     * @version $Rev: 6687 $
031     */
032    public class PropertyEditors {
033        private static final Map registry = Collections.synchronizedMap(new ReferenceIdentityMap());
034        private static final Map PRIMITIVE_TO_WRAPPER;
035        private static final Map WRAPPER_TO_PRIMITIVE;
036    
037        /**
038         * Register all of the built in converters
039         */
040        static {
041            Map map = new HashMap();
042            map.put(boolean.class, Boolean.class);
043            map.put(char.class, Character.class);
044            map.put(byte.class, Byte.class);
045            map.put(short.class, Short.class);
046            map.put(int.class, Integer.class);
047            map.put(long.class, Long.class);
048            map.put(float.class, Float.class);
049            map.put(double.class, Double.class);
050            PRIMITIVE_TO_WRAPPER = Collections.unmodifiableMap(map);
051    
052    
053            map = new HashMap();
054            map.put(Boolean.class, boolean.class);
055            map.put(Character.class, char.class);
056            map.put(Byte.class, byte.class);
057            map.put(Short.class, short.class);
058            map.put(Integer.class, int.class);
059            map.put(Long.class, long.class);
060            map.put(Float.class, float.class);
061            map.put(Double.class, double.class);
062            WRAPPER_TO_PRIMITIVE = Collections.unmodifiableMap(map);
063    
064            // Explicitly register the types
065            registerConverter(new ArrayListEditor());
066            registerConverter(new BigDecimalEditor());
067            registerConverter(new BigIntegerEditor());
068            registerConverter(new BooleanEditor());
069            registerConverter(new ByteEditor());
070            registerConverter(new CharacterEditor());
071            registerConverter(new ClassEditor());
072            registerConverter(new DateEditor());
073            registerConverter(new DoubleEditor());
074            registerConverter(new FileEditor());
075            registerConverter(new FloatEditor());
076            registerConverter(new HashMapEditor());
077            registerConverter(new HashtableEditor());
078            registerConverter(new IdentityHashMapEditor());
079            registerConverter(new Inet4AddressEditor());
080            registerConverter(new Inet6AddressEditor());
081            registerConverter(new InetAddressEditor());
082            registerConverter(new IntegerEditor());
083            registerConverter(new LinkedHashMapEditor());
084            registerConverter(new LinkedHashSetEditor());
085            registerConverter(new LinkedListEditor());
086            registerConverter(new ListEditor());
087            registerConverter(new LongEditor());
088            registerConverter(new MapEditor());
089            registerConverter(new ObjectNameEditor());
090            registerConverter(new PropertiesEditor());
091            registerConverter(new SetEditor());
092            registerConverter(new ShortEditor());
093            registerConverter(new SortedMapEditor());
094            registerConverter(new SortedSetEditor());
095            registerConverter(new StringEditor());
096            registerConverter(new TreeMapEditor());
097            registerConverter(new TreeSetEditor());
098            registerConverter(new URIEditor());
099            registerConverter(new URLEditor());
100            registerConverter(new VectorEditor());
101            registerConverter(new WeakHashMapEditor());
102        }
103    
104        public static void registerConverter(Converter converter) {
105            if (converter == null) throw new NullPointerException("editor is null");
106            Class type = converter.getType();
107            registry.put(type, converter);
108            PropertyEditorManager.registerEditor(type, converter.getClass());
109    
110            if (PRIMITIVE_TO_WRAPPER.containsKey(type)) {
111                Class wrapperType = (Class) PRIMITIVE_TO_WRAPPER.get(type);
112                registry.put(wrapperType, converter);
113                PropertyEditorManager.registerEditor(wrapperType, converter.getClass());
114            } else if (WRAPPER_TO_PRIMITIVE.containsKey(type)) {
115                Class primitiveType = (Class) WRAPPER_TO_PRIMITIVE.get(type);
116                registry.put(primitiveType, converter);
117                PropertyEditorManager.registerEditor(primitiveType, converter.getClass());
118            }
119        }
120    
121        public static boolean canConvert(String type, ClassLoader classLoader) {
122            if (type == null) throw new NullPointerException("type is null");
123            if (classLoader == null) throw new NullPointerException("classLoader is null");
124    
125            // load using the ClassLoading utility, which also manages arrays and primitive classes.
126            Class typeClass = null;
127            try {
128                typeClass = Class.forName(type, true, classLoader);
129            } catch (ClassNotFoundException e) {
130                throw new PropertyEditorException("Type class could not be found: " + type);
131            }
132    
133            return canConvert(typeClass);
134    
135        }
136    
137        public static boolean canConvert(Class type) {
138            Converter converter = findConverter(type);
139            if (converter != null) {
140                return true;
141            }
142    
143            // fall back to a property editor
144            PropertyEditor editor = findEditor(type);
145            if (editor != null) {
146                return true;
147            }
148    
149            return false;
150        }
151    
152        public static String toString(Object value) throws PropertyEditorException {
153            if (value == null) throw new NullPointerException("value is null");
154    
155            // get an editor for this type
156            Class type = value.getClass();
157    
158            // try to get a converter from our registry as they are way faster and easier to use
159            Converter converter = findConverter(type);
160            if (converter != null) {
161                return converter.toString(value);
162            }
163    
164            // fall back to a property editor
165            PropertyEditor editor = findEditor(type);
166            if (editor == null) {
167                throw new PropertyEditorException("Unable to find PropertyEditor for " + type.getSimpleName());
168            }
169    
170            // create the string value
171            editor.setValue(value);
172            String textValue = null;
173            try {
174                textValue = editor.getAsText();
175            } catch (Exception e) {
176                throw new PropertyEditorException("Error while converting a \"" + type.getSimpleName() + "\" to text " +
177                        " using the property editor " + editor.getClass().getSimpleName(), e);
178            }
179            return textValue;
180        }
181    
182        public static Object getValue(String type, String value, ClassLoader classLoader) throws PropertyEditorException {
183            if (type == null) throw new NullPointerException("type is null");
184            if (value == null) throw new NullPointerException("value is null");
185            if (classLoader == null) throw new NullPointerException("classLoader is null");
186    
187            // load using the ClassLoading utility, which also manages arrays and primitive classes.
188            Class typeClass = null;
189            try {
190                typeClass = Class.forName(type, true, classLoader);
191            } catch (ClassNotFoundException e) {
192                throw new PropertyEditorException("Type class could not be found: " + type);
193            }
194    
195            return getValue(typeClass, value);
196    
197        }
198    
199        public static Object getValue(Class type, String value) throws PropertyEditorException {
200            if (type == null) throw new NullPointerException("type is null");
201            if (value == null) throw new NullPointerException("value is null");
202    
203            // try to get a converter from our registry as they are way faster and easier to use
204            Converter converter = findConverter(type);
205            if (converter != null) {
206                return converter.toObject(value);
207            }
208    
209            // fall back to a property editor
210            PropertyEditor editor = findEditor(type);
211            if (editor == null) {
212                throw new PropertyEditorException("Unable to find PropertyEditor for " + type.getSimpleName());
213            }
214    
215            // create the object value
216            editor.setAsText(value);
217            Object objectValue = null;
218            try {
219                objectValue = editor.getValue();
220            } catch (Exception e) {
221                throw new PropertyEditorException("Error while converting \"" + value + "\" to a " + type.getSimpleName() +
222                        " using the property editor " + editor.getClass().getSimpleName(), e);
223            }
224            return objectValue;
225        }
226    
227        private static Converter findConverter(Class type) {
228            if (type == null) throw new NullPointerException("type is null");
229    
230            Converter converter = (Converter) registry.get(type);
231    
232            // we're outta here if we got one.
233            if (converter != null) {
234                return converter;
235            }
236    
237            Class[] declaredClasses = type.getDeclaredClasses();
238            for (int i = 0; i < declaredClasses.length; i++) {
239                Class declaredClass = declaredClasses[i];
240                if (Converter.class.isAssignableFrom(declaredClass)) {
241                    try {
242                        converter = (Converter) declaredClass.newInstance();
243                        registerConverter(converter);
244    
245                        // try to get the converter from the registry... the converter
246                        // created above may have been for another class
247                        converter = (Converter) registry.get(type);
248                        if (converter != null) {
249                            return converter;
250                        }
251                    } catch (Exception e) {
252                    }
253    
254                }
255            }
256    
257            // it's possible this was a request for an array class.  We might not
258            // recognize the array type directly, but the component type might be
259            // resolvable
260            if (type.isArray() && !type.getComponentType().isArray()) {
261                // do a recursive lookup on the base type
262                converter = findConverter(type.getComponentType());
263                // if we found a suitable editor for the base component type,
264                // wrapper this in an array adaptor for real use
265                if (converter != null) {
266                    return new ArrayConverter(type, converter);
267                }
268            }
269    
270            // nothing found
271            return null;
272        }
273    
274        /**
275         * Locate a property editor for qiven class of object.
276         *
277         * @param type The target object class of the property.
278         * @return The resolved editor, if any.  Returns null if a suitable editor
279         *         could not be located.
280         */
281        private static PropertyEditor findEditor(Class type) {
282            if (type == null) throw new NullPointerException("type is null");
283    
284            // try to locate this directly from the editor manager first.
285            PropertyEditor editor = PropertyEditorManager.findEditor(type);
286    
287            // we're outta here if we got one.
288            if (editor != null) {
289                return editor;
290            }
291    
292            // it's possible this was a request for an array class.  We might not
293            // recognize the array type directly, but the component type might be
294            // resolvable
295            if (type.isArray() && !type.getComponentType().isArray()) {
296                // do a recursive lookup on the base type
297                editor = findEditor(type.getComponentType());
298                // if we found a suitable editor for the base component type,
299                // wrapper this in an array adaptor for real use
300                if (editor != null) {
301                    return new ArrayConverter(type, editor);
302                }
303            }
304    
305            // nothing found
306            return null;
307        }
308    }