1 /*
2 * Copyright 2003-2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.apache.commons.convert1;
17
18
19 import java.util.Iterator;
20 import java.util.Map;
21
22 import org.apache.commons.convert1.util.ClassMap;
23 import org.apache.commons.convert1.util.Inheritor;
24
25 /**
26 * Stores Converters under a dual-key system of fromClass to toClass.
27 *
28 * @author Henri Yandell
29 * @version $Id: ConvertRegistry.java 155441 2005-02-26 13:19:22Z dirkv $
30 * @since 0.1
31 */
32
33 public class ConvertRegistry {
34
35 // ------------------------------------------------------- Class Methods
36
37 // ------------------------------------------------------- Variables
38
39
40 /**
41 * A ClassMap of Class to ClassMap. This then contains
42 * a Class to {@link Converter} mapping.
43 */
44 private ClassMap converters = new ClassMap();
45
46 // ------------------------------------------------------- Constructors
47
48 // TODO: Allow the Inheritor's used by the ClassMap to be set by
49 // the user so that different ways of looking up may be used
50 public ConvertRegistry() {
51 }
52
53 // --------------------------------------------------------- Public Methods
54
55 // ------------------------------------------------------ Static Properties
56
57
58 /**
59 * Convert the specified value into a String. If the specified value
60 * is an array, the first element (converted to a String) will be
61 * returned. The registered {@link Converter} for the
62 * <code>java.lang.String</code> class will be used, which allows
63 * applications to customize Object->String conversions (the default
64 * implementation simply uses toString()).
65 *
66 * @param value Value to be converted (may be null)
67 */
68 /*
69 // This whole method appears to be a waste of time.
70 // It's a way to plugin a StringConverter, but why treat Strings
71 // in a special way?
72 public String convert(Object value) {
73
74 if (value == null) {
75 return ((String) null);
76 } else if (value.getClass().isArray()) {
77
78 //H? This seems bad. String[0] becomes null.
79 // Why not return String[]??
80 if (Array.getLength(value) < 1) {
81 return (null);
82 }
83 value = Array.get(value, 0);
84 if (value == null) {
85 return ((String) null);
86 } else {
87 Converter converter = lookup(String.class);
88 return ((String) converter.convert(String.class, value));
89 }
90 } else {
91 Converter converter = lookup(String.class);
92 return ((String) converter.convert(String.class, value));
93 }
94
95 }
96 */
97
98
99 /**
100 * Convert the specified value to an object of the specified class (if
101 * possible). Otherwise, return a String representation of the value.
102 *
103 * @param value Value to be converted (may be null)
104 * @param clazz Java class to be converted to
105 *
106 * @exception ConversionException if thrown by an underlying Converter
107 */
108 public Object convert(Object value, Class toClass) {
109 //H? If value == null, return null??
110 if(value == null) {
111 return null;
112 }
113
114 Class fromClass = value.getClass();
115
116 Converter converter = lookup(value.getClass(), toClass);
117
118 if (converter == null) {
119 //H? If value.getClass() == toClass, do we auto-return?
120 if(value.getClass() == toClass) {
121 return value;
122 }
123
124 Inheritor inh = converters.getInheritor();
125
126 Iterator itr = inh.iterator();
127
128 while( itr.hasNext() && converter == null ) {
129 converter = lookup( value.getClass(), toClass );
130 }
131
132 //H? Try to treat converter as a Collection concept
133 // Or should this be a converter of its own?
134 // How would it be mapped? To every possible
135 // collective type? primitve[], Object[], Collection,
136 // Map?
137 // Yes, this should be a converter.
138
139 //H? Throw exception? return null? Treat as String?
140 //H? How about returning value? Should this be a user choice?
141 if( converter == null ) {
142 converter = new IdentityConverter();
143 }
144 }
145
146 //H? Should this check if the returned value is
147 // the correct class?
148 return converter.convert(toClass, value);
149
150 }
151
152
153 /**
154 * Convert an array of specified values to an array of objects of the
155 * specified class (if possible). If the specified Java class is itself
156 * an array class, this class will be the type of the returned value.
157 * Otherwise, an array will be constructed whose component type is the
158 * specified class.
159 *
160 * @param value Value to be converted (may be null)
161 * @param clazz Java array or element class to be converted to
162 *
163 * @exception ConversionException if thrown by an underlying Converter
164 */
165 //H? This method is too specific. It needs to become convertCollective
166 /*
167 public Object convert(String values[], Class clazz) {
168
169 Class type = clazz;
170 if (clazz.isArray()) {
171 type = clazz.getComponentType();
172 }
173 Converter converter = lookup(type);
174 if (converter == null) {
175 converter = lookup(String.class);
176 }
177 Object array = Array.newInstance(type, values.length);
178 for (int i = 0; i < values.length; i++) {
179 Array.set(array, i, converter.convert(type, values[i]));
180 }
181 return (array);
182
183 }
184 */
185
186
187 /**
188 * Remove all registered {@link Converter}s.
189 */
190 public void clear() {
191
192 converters.clear();
193
194 }
195
196 /**
197 * Remove any registered {@link Converter} for the specified destination
198 * <code>Class</code>.
199 *
200 * @param clazz Class for which to remove a registered Converter
201 */
202 public void deregister(Class fromClass, Class toClass) {
203
204 Map map = (Map) converters.get(fromClass);
205 map.remove(toClass);
206
207 }
208 public void fromClassDeregister(Class fromClass) {
209
210 converters.remove(fromClass);
211
212 }
213 public void toClassDeregister(Class toClass) {
214
215 // loop over every fromClass and remove
216 // a dual indexed map will improve speed
217 Iterator itr = converters.keySet().iterator();
218 while( itr.hasNext() ) {
219 Map map = (Map) itr.next();
220 map.remove(toClass);
221 }
222 }
223
224 /**
225 * Look up and return any registered {@link Converter} for the specified
226 * destination class; if there is no registered Converter, return
227 * <code>null</code>.
228 *
229 * @param clazz Class for which to return a registered Converter
230 */
231 //H? Should this return an IdentityConverter?
232 public Converter lookup(Class fromClass, Class toClass) {
233
234 Map map = (Map) converters.get(fromClass);
235
236 if(map == null) {
237 return null;
238 }
239
240 Object obj = map.get(toClass);
241
242 while( obj instanceof ConverterFactory ) {
243 obj = ( (ConverterFactory) obj).create( fromClass, toClass );
244 }
245
246 //H? check obj is a Converter, or is ClassNotFound good enough?
247
248 return (Converter) obj;
249
250 }
251
252 //H? What to do here? Return a registry with a default fromClass?
253 //H? rename: lookupFromClassRegistry
254 //H? add: lookupToClassRegistry
255
256
257 /**
258 * Register a custom {@link Converter} for the specified destination
259 * <code>Class</code>, replacing any previously registered Converter.
260 *
261 * @param converter Converter to be registered
262 * @param clazz Destination class for conversions performed by this
263 * Converter
264 */
265 public void register(Converter converter, Class fromClass, Class toClass) {
266
267 Map map = (Map) converters.get(fromClass);
268
269 if( map == null) {
270 map = new ClassMap();
271 converters.put( fromClass, map );
272 }
273
274 map.put(toClass, converter);
275
276 }
277
278 //H? refactor: registerForFromClass(ConverterRegistry, Class fromClass)
279 //H? refactor: registerForToClass(ConverterRegistry, Class toClass)
280 }