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 }