1 /*
2 * Copyright (C) The Spice Group. All rights reserved.
3 *
4 * This software is published under the terms of the Spice
5 * Software License version 1.1, a copy of which has been included
6 * with this distribution in the LICENSE.txt file.
7 */
8 package org.codehaus.spice.converter;
9
10 import java.util.ArrayList;
11 import java.util.HashMap;
12 import java.util.Map;
13 import org.codehaus.spice.salt.i18n.Resources;
14 import org.codehaus.spice.salt.i18n.ResourceManager;
15
16 /***
17 * This is a Converter implementation that is capable of converting between
18 * many different source and destination types, by delegating delegates to
19 * other converters that do the actual work.
20 *
21 * <p>To use this class you must subclass it, and register some converters
22 * using the (@link #registerConverter} method.</p>
23 *
24 * @author Peter Donald
25 * @version $Revision: 1.1 $ $Date: 2003/12/02 08:37:56 $
26 */
27 public abstract class AbstractMasterConverter
28 implements Converter
29 {
30 /***
31 * i18n resources accessor.
32 */
33 private static final Resources REZ =
34 ResourceManager.getPackageResources( AbstractMasterConverter.class );
35
36 /***
37 * Cache of converter instances. This is a map from ConverterFactory to
38 * a Converter instance created by that factory.
39 */
40 private final Map m_converters = new HashMap();
41
42 /***
43 * This holds the mapping between source/destination and ConverterFactory.
44 */
45 private final HashMap m_mapping = new HashMap();
46
47 /***
48 * Convert object to destination type.
49 *
50 * @param destination the destination type
51 * @param original the original object
52 * @param context the context in which to convert
53 * @return the converted object
54 * @throws ConverterException if an error occurs
55 */
56 public Object convert( final Class destination,
57 final Object original,
58 final Object context )
59 throws ConverterException
60 {
61 final Class originalClass = original.getClass();
62
63 if( destination.isAssignableFrom( originalClass ) )
64 {
65 return original;
66 }
67
68 try
69 {
70 // Determine which converter to use
71 final Converter converter = findConverter( originalClass, destination );
72
73 // Convert
74 final Object object = converter.convert( destination, original, context );
75 if( destination.isInstance( object ) )
76 {
77 return object;
78 }
79
80 final String message =
81 REZ.format( "bad-return-type.error",
82 object.getClass().getName(),
83 destination.getName() );
84 throw new ConverterException( message );
85 }
86 catch( final Exception e )
87 {
88 final String message =
89 REZ.format( "convert.error",
90 originalClass.getName(),
91 destination.getName() );
92 throw new ConverterException( message, e );
93 }
94 }
95
96 /***
97 * Returns the Converter instance to use to convert between a particular
98 * pair of classes.
99 */
100 private Converter findConverter( final Class originalClass,
101 final Class destination )
102 throws Exception
103 {
104 // Locate the factory to use
105 final ConverterFactory factory = findConverterFactory( originalClass, destination );
106
107 // Create the converter
108 Converter converter = (Converter)m_converters.get( factory );
109 if( converter == null )
110 {
111 converter = factory.createConverter();
112 m_converters.put( factory, converter );
113 }
114 return converter;
115 }
116
117 /***
118 * Register a converter
119 *
120 * @param factory the factory to use to create converter instances.
121 * @param source the source classname
122 * @param destination the destination classname
123 */
124 protected void registerConverter( final ConverterFactory factory,
125 final String source,
126 final String destination )
127 {
128 HashMap map = (HashMap)m_mapping.get( source );
129 if( null == map )
130 {
131 map = new HashMap();
132 m_mapping.put( source, map );
133 }
134
135 map.put( destination, factory );
136
137 //Remove instance of converter if it has already been created
138 m_converters.remove( factory );
139 }
140
141 /***
142 * Determine the type of converter (represented as a ConverterFactory) to
143 * use to convert between original and destination classes.
144 */
145 private ConverterFactory findConverterFactory( final Class originalClass,
146 final Class destination )
147 throws ConverterException
148 {
149 //TODO: Maybe we should search the destination classes hierarchy as well
150
151 // Recursively iterate over the super-types of the original class,
152 // looking for a converter from source type -> destination type.
153 // If more than one is found, choose the most specialised.
154
155 Class bestSrcMatch = null;
156 ConverterFactory matchFactory = null;
157 ArrayList queue = new ArrayList();
158 queue.add( originalClass );
159
160 while( !queue.isEmpty() )
161 {
162 final Class clazz = (Class)queue.remove( 0 );
163
164 // Add superclass and all interfaces
165 if( clazz.getSuperclass() != null )
166 {
167 queue.add( clazz.getSuperclass() );
168 }
169 final Class[] interfaces = clazz.getInterfaces();
170 for( int i = 0; i < interfaces.length; i++ )
171 {
172 queue.add( interfaces[ i ] );
173 }
174
175 // Check if we can convert from current class to destination
176 final ConverterFactory factory =
177 getConverterFactory( clazz.getName(), destination.getName() );
178 if( factory == null )
179 {
180 continue;
181 }
182
183 // Choose the more specialised source class
184 if( bestSrcMatch == null || bestSrcMatch.isAssignableFrom( clazz ) )
185 {
186 bestSrcMatch = clazz;
187 matchFactory = factory;
188 }
189 else if( clazz.isAssignableFrom( bestSrcMatch ) )
190 {
191 continue;
192 }
193 else
194 {
195 // Duplicate
196 final String message = REZ.getString( "ambiguous-converter.error" );
197 throw new ConverterException( message );
198 }
199 }
200
201 // TODO - should cache the (src, dest) -> converter mapping
202 if( bestSrcMatch != null )
203 {
204 return matchFactory;
205 }
206
207 // Could not find a converter
208 final String message = REZ.getString( "no-converter.error" );
209 throw new ConverterException( message );
210 }
211
212 /***
213 * Retrieve factory for the converter that converts from source to destination.
214 */
215 private ConverterFactory getConverterFactory( final String source,
216 final String destination )
217 {
218 final HashMap map = (HashMap)m_mapping.get( source );
219 if( null == map )
220 {
221 return null;
222 }
223 return (ConverterFactory)map.get( destination );
224 }
225 }
This page was automatically generated by Maven