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 }