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    
018    package org.apache.geronimo.common.propertyeditor;
019    
020    import java.beans.PropertyEditor;
021    import java.beans.PropertyEditorManager;
022    import java.util.ArrayList;
023    import java.util.Arrays;
024    import java.util.List;
025    
026    import org.apache.geronimo.kernel.ClassLoading;
027    
028    /**
029     * The property editor manager.  This orchestrates Geronimo usage of
030     * property editors, allowing additional search paths to be added and
031     * specific editors to be registered.
032     *
033     * @version $Rev: 476049 $
034     */
035    public class PropertyEditors {
036        /**
037         * We need to register the standard register seach path and explicitly
038         * register a boolean editor to make sure ours overrides.
039         */
040        static {
041            // Append the geronimo propertyeditors package to the global search path.
042            appendEditorSearchPath("org.apache.geronimo.common.propertyeditor");
043            // and explicitly register the Boolean editor.
044            PropertyEditorManager.registerEditor(Boolean.class, BooleanEditor.class);
045            PropertyEditorManager.registerEditor(Integer.class, IntegerEditor.class);
046        }
047    
048        /**
049         * Locate an editor for qiven class of object.
050         *
051         * @param type The target object class of the property.
052         * @return The resolved editor, if any.  Returns null if a suitable editor
053         *         could not be located.
054         */
055        public static PropertyEditor findEditor(Class type) {
056            // explicit argument checking is required.
057            if (type == null) {
058                throw new IllegalArgumentException("type is null");
059            }
060    
061    
062            // try to locate this directly from the editor manager first.
063            PropertyEditor editor = PropertyEditorManager.findEditor(type);
064    
065            // we're outta here if we got one.
066            if (editor != null) {
067                return editor;
068            }
069    
070            // it's possible this was a request for an array class.  We might not
071            // recognize the array type directly, but the component type might be
072            // resolvable
073            if (type.isArray()) {
074                // do a recursive lookup on the base type
075                editor = findEditor(type.getComponentType());
076                // if we found a suitable editor for the base component type,
077                // wrapper this in an array adaptor for real use
078                if (editor != null) {
079                    return new ArrayPropertyEditorAdapter(type.getComponentType(), editor);
080                }
081            }
082            // nothing found
083            return null;
084        }
085    
086        /**
087         * Locate an editor for qiven class of object, resolved within the context of
088         * a specific ClassLoader instance.
089         *
090         * @param typeName The type name of target property class.
091         * @param loader The source ClassLoader instance.
092         * @return The resolved editor, if any.  Returns null if a suitable editor
093         *         could not be located.
094         * @throws ClassNotFoundException Thrown if unable to resolve an appropriate editor class.
095         */
096        public static PropertyEditor findEditor(String typeName, ClassLoader loader) throws ClassNotFoundException {
097            // explicit argument checking is required.
098            if (typeName == null) {
099                throw new IllegalArgumentException("typeName is null");
100            }
101    
102            Class type = null;
103            // load using the ClassLoading utility, which also manages arrays and primitive classes.
104            try {
105                type = ClassLoading.loadClass(typeName, loader);
106            } catch (ClassNotFoundException e) {
107                // We also support anonymous inner class nesting of property editors.  In that situation,
108                // the package/class names are the same, but add on the inner class specifier.
109                // If this one fails, we jump directly out with the ClassNotFoundException.
110                type = ClassLoading.loadClass(typeName + "$PropertyEditor", loader);
111            }
112    
113            // The PropertyEditorManager class uses the context class loader for all of its resolution
114            // steps.  We need force PropertyManagerEditor to use our loader, so we override the
115            // current context loader.
116            ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
117            try {
118                Thread.currentThread().setContextClassLoader(loader);
119                // now call the base findEditor() method that works directly from the property type.
120                return findEditor(type);
121            } finally {
122                // make sure we restore the context....this will happen even if findEditor()
123                // results in an exception.
124                Thread.currentThread().setContextClassLoader(oldLoader);
125            }
126        }
127    
128        /**
129         * Get a property editor for a given property type.  This is like
130         * findEditor, but throws an exception if the property is not found.
131         *
132         * @param type The target object class of the property.
133         * @return The resolved editor, if any.  Throws an exception if this cannot
134         *         be resolved.
135         * @throws PropertyEditorException Unable to find a suitable editor for this class.
136         */
137        public static PropertyEditor getEditor(Class type) {
138            // just call the non-exceptional lookup
139            PropertyEditor editor = findEditor(type);
140            // this one throws an exception if not found.
141            if (editor == null) {
142                throw new PropertyEditorException("No property editor for type: " + type);
143            }
144            return editor;
145        }
146    
147        /**
148         * Explicity register an editor class for a given target class.
149         *
150         * @param type The property class.
151         * @param editorType The editor class matched up with this type.
152         */
153        public static void registerEditor(Class type, Class editorType) {
154            // explicit argument checking is required.
155            if (type == null) {
156                throw new IllegalArgumentException("type is null");
157            }
158    
159            // explicit argument checking is required.
160            if (editorType == null) {
161                throw new IllegalArgumentException("editorType is null");
162            }
163    
164            PropertyEditorManager.registerEditor(type, editorType);
165        }
166    
167        /**
168         * Explicity register a property/editor class pair by class name.
169         *
170         * @param typeName The classname of the property.
171         * @param editorName The classname of the property editor.
172         * @throws ClassNotFoundException Thrown if unable to resolve either the type or the editor from their names.
173         */
174        public static void registerEditor(String typeName, String editorName) throws ClassNotFoundException {
175            // explicit argument checking is required.
176            if (typeName == null) {
177                throw new IllegalArgumentException("typeName is null");
178            }
179    
180            // explicit argument checking is required.
181            if (editorName == null) {
182                throw new IllegalArgumentException("editorTypeName is null");
183            }
184            // we use the current context loader for this
185            ClassLoader loader = Thread.currentThread().getContextClassLoader();
186    
187            // load both of these loaders using our ClassLoading support.
188            Class type = ClassLoading.loadClass(typeName, loader);
189            Class editor = ClassLoading.loadClass(editorName, loader);
190    
191            // we have resolved classes, so register the class information.
192            registerEditor(type, editor);
193        }
194    
195        /**
196         * Get a list containing all of the packages in the editor search path.
197         *
198         * @return a List object containing all of the registered search paths.
199         */
200        public static List getEditorSearchPath() {
201            // grrrr, Arrays.asList() returns a readonly List item, which makes it difficult
202            // to append additional items.  This means we have to do this manually.
203    
204            // start by getting the list from the editor manager, which is returned as an
205            // array of Strings.
206            String[] paths = PropertyEditorManager.getEditorSearchPath();
207    
208            // get a list matching the initial size...we don't always request this with the intend to append.
209            List pathList = new ArrayList(paths.length);
210    
211            // now MANUALLY add each of the items in the array.
212            for (int i = 0; i < paths.length; i++) {
213                pathList.add(paths[i]);
214            }
215    
216            return pathList;
217        }
218    
219        /**
220         * Sets the search order used for property editor resolution.
221         *
222         * @param path The serach path.
223         */
224        public static void setEditorSearchPath(List path) {
225            // explicit argument checking is required.
226            if (path == null) {
227                throw new IllegalArgumentException("path is null");
228            }
229    
230            // we deal in Lists, PropertyEditorManager does arrays, so we need to
231            // extract the elements into a array of Strings.
232            String[] elements = (String[]) path.toArray(new String[path.size()]);
233            PropertyEditorManager.setEditorSearchPath(elements);
234        }
235    
236        /**
237         * Append additional package names to the property editor search path.
238         *
239         * @param names The package names to append.
240         */
241        public static void appendEditorSearchPath(List newNames) {
242            // explicit argument checking is required.
243            if (newNames == null) {
244                throw new IllegalArgumentException("names is null");
245            }
246    
247            // if there's nothing to do, then do nothing :-)
248            if (newNames.isEmpty()) {
249                return;
250            }
251    
252            // append to the current names list, and set ammended list back as the current
253            // search order.
254            List currentPath = getEditorSearchPath();
255            currentPath.addAll(newNames);
256    
257            setEditorSearchPath(currentPath);
258        }
259    
260        /**
261         * Append an array of package names to the editor search path.
262         *
263         * @param names A string array containing the added names.
264         */
265        public static void appendEditorSearchPath(String[] newNames) {
266            // explicit argument checking is required.
267            if (newNames == null) {
268                throw new IllegalArgumentException("names is null");
269            }
270    
271            // only bother continuing if the array contains something.
272            if (newNames.length != 0) {
273                // just convert this to a list and add as normal.
274                appendEditorSearchPath(Arrays.asList(newNames));
275            }
276        }
277    
278        /**
279         * Append a single package name to the editor search path.
280         *
281         * @param name The new path name.
282         */
283        public static void appendEditorSearchPath(String newName) {
284            // explicit argument checking is required.
285            if (newName == null) {
286                throw new IllegalArgumentException("name is null");
287            }
288    
289            // append to the current names list, and set ammended list back as the current
290            // search order.
291            List currentPath = getEditorSearchPath();
292            currentPath.add(newName);
293    
294            setEditorSearchPath(currentPath);
295        }
296    }