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 }