001    /*
002     *  Copyright 2003-2004 The Apache Software Foundation
003     *
004     *  Licensed under the Apache License, Version 2.0 (the "License");
005     *  you may not use this file except in compliance with the License.
006     *  You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     *  Unless required by applicable law or agreed to in writing, software
011     *  distributed under the License is distributed on an "AS IS" BASIS,
012     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     *  See the License for the specific language governing permissions and
014     *  limitations under the License.
015     */
016    package org.apache.commons.convert1;
017    
018    
019    import java.util.Iterator;
020    import java.util.Map;
021    
022    import org.apache.commons.convert1.util.ClassMap;
023    import org.apache.commons.convert1.util.Inheritor;
024    
025    /**
026     * Stores Converters under a dual-key system of fromClass to toClass.
027     * 
028     * @author Henri Yandell
029     * @version $Id: ConvertRegistry.java 155441 2005-02-26 13:19:22Z dirkv $
030     * @since 0.1
031     */
032    
033    public class ConvertRegistry {
034    
035        // ------------------------------------------------------- Class Methods
036    
037        // ------------------------------------------------------- Variables
038    
039    
040        /**
041         * A ClassMap of Class to ClassMap. This then contains 
042         * a Class to {@link Converter} mapping.
043         */
044        private ClassMap converters = new ClassMap();
045    
046        // ------------------------------------------------------- Constructors
047    
048        // TODO: Allow the Inheritor's used by the ClassMap to be set by 
049        //       the user so that different ways of looking up may be used
050        public ConvertRegistry() {
051        }
052    
053        // --------------------------------------------------------- Public Methods
054        
055        // ------------------------------------------------------ Static Properties
056    
057    
058        /**
059         * Convert the specified value into a String.  If the specified value
060         * is an array, the first element (converted to a String) will be
061         * returned.  The registered {@link Converter} for the
062         * <code>java.lang.String</code> class will be used, which allows
063         * applications to customize Object->String conversions (the default
064         * implementation simply uses toString()).
065         *
066         * @param value Value to be converted (may be null)
067         */
068        /*
069        // This whole method appears to be a waste of time. 
070        // It's a way to plugin a StringConverter, but why treat Strings 
071        // in a special way?
072        public String convert(Object value) {
073    
074            if (value == null) {
075                return ((String) null);
076            } else if (value.getClass().isArray()) {
077    
078                //H? This seems bad. String[0] becomes null.
079                //   Why not return String[]??
080                if (Array.getLength(value) < 1) {
081                    return (null);
082                }
083                value = Array.get(value, 0);
084                if (value == null) {
085                    return ((String) null);
086                } else {
087                    Converter converter = lookup(String.class);
088                    return ((String) converter.convert(String.class, value));
089                }
090            } else {
091                Converter converter = lookup(String.class);
092                return ((String) converter.convert(String.class, value));
093            }
094    
095        }
096        */
097    
098    
099        /**
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    }