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 }