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 }