1 /*
2 * Copyright 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.convert;
17
18 import java.util.HashMap;
19 import java.util.Map;
20
21 import org.apache.commons.convert.conversion.ClassToStringConversion;
22 import org.apache.commons.convert.conversion.ObjectToStringConversionFactory;
23 import org.apache.commons.convert.conversion.TimeZoneToStringConversionFactory;
24
25 /**
26 * ConversionRegistry manages the <code>Conversion</code> and
27 * <code>ConversionFactory</code> objects.
28 * <p>
29 * This class allows conversions to be added, removed and looked up.
30 * This implementation is fully synchronized.
31 * <pre>
32 * Converter converter = new Converter();
33 * converter.getRegistry().addDefaultConversions();
34 * converter.getRegistry().addConversion(new MyNewConversion());
35 * </pre>
36 *
37 * @author Stephen Colebourne
38 * @version $Id: ConversionRegistry.java 155441 2005-02-26 13:19:22Z dirkv $
39 * @since 1.0
40 */
41 public class ConversionRegistry {
42
43 /** Map of from class to conversion */
44 protected Map iConversions = new HashMap();
45 /** Array of conversion factories */
46 protected ConversionFactory[] iFactories = new ConversionFactory[0];
47 /** Object to synchronize on for factories */
48 protected final Object iFactoryLock = new Object();
49
50 /**
51 * Restricted constructor, use Converter.
52 */
53 protected ConversionRegistry() {
54 super();
55 }
56
57 //-----------------------------------------------------------------------
58 /**
59 * Add the default set of conversions to the registry.
60 */
61 public void addDefaultConversions() {
62 synchronized (iConversions) {
63 addConversion(ClassToStringConversion.INSTANCE);
64 }
65 synchronized (iFactoryLock) {
66 addConversionFactory(ObjectToStringConversionFactory.INSTANCE);
67 addConversionFactory(TimeZoneToStringConversionFactory.INSTANCE);
68 }
69 }
70
71 //-----------------------------------------------------------------------
72 /**
73 * Adds a Conversion to the map of known conversions.
74 * Any previous conversion for this from-to pair is replaced.
75 *
76 * @param conv the conversion to add
77 */
78 public void addConversion(Conversion conv) {
79 if (conv != null) {
80 synchronized (iConversions) {
81 Map map = (Map) iConversions.get(conv.getFromType());
82 if (map == null) {
83 map = new HashMap();
84 iConversions.put(conv.getFromType(), map);
85 }
86 map.put(conv.getToType(), conv);
87 }
88 }
89 }
90
91 /**
92 * Adds a ConversionFactory to the set of known factories.
93 * Any previous factory that matches by <code>equals()</code> is replaced.
94 *
95 * @param factory the factory to add
96 */
97 public void addConversionFactory(ConversionFactory factory) {
98 if (factory != null) {
99 synchronized (iFactoryLock) {
100 ConversionFactory[] oldFactories = iFactories;
101 for (int i = 0; i < oldFactories.length; i++) {
102 if (oldFactories[i].equals(factory)) {
103 iFactories[i] = factory;
104 return;
105 }
106 }
107 ConversionFactory[] newFactories = new ConversionFactory[oldFactories.length + 1];
108 System.arraycopy(oldFactories, 0, newFactories, 0, oldFactories.length);
109 newFactories[oldFactories.length] = factory;
110 iFactories = newFactories;
111 }
112 }
113 }
114
115 //-----------------------------------------------------------------------
116 /**
117 * Gets the conversion object that best matches the from and to types.
118 * <p>
119 * The lookup first examines the known conversions. If none is found, the
120 * factories are used to search for and create a conversion. If no suitable
121 * factory is found then <code>null</code> is returned.
122 *
123 * @param value the value that will be converted, read only and not to be stored
124 * @param fromType the type to convert from
125 * @param toType the type to convert to
126 * @return the best matching conversion, null if no match
127 */
128 public Conversion getConversion(Object value, Class fromType, Class toType) {
129 // try known conversions
130 synchronized (iConversions) {
131 Map map = (Map) iConversions.get(fromType);
132 if (map != null) {
133 Conversion conv = (Conversion) map.get(toType);
134 if (conv != null) {
135 return conv;
136 }
137 }
138 }
139
140 // try factories
141 int max = 0;
142 ConversionFactory maxFactory = null;
143 synchronized (iFactoryLock) {
144 ConversionFactory[] factories = iFactories;
145 for (int i = 0; i < factories.length; i++) {
146 int match = factories[i].getMatchPercent(value, fromType, toType);
147 if (match > max) {
148 max = match;
149 maxFactory = factories[i];
150 }
151 }
152 }
153 if (maxFactory != null) {
154 Conversion conv = maxFactory.getInstance(value, fromType, toType);
155 addConversion(conv);
156 return conv;
157 }
158
159 // no match
160 return null;
161 }
162
163 //-----------------------------------------------------------------------
164 /**
165 * Returns a string describing this object.
166 *
167 * @return a string describing this object
168 */
169 public String toString() {
170 return "ConverterRegistry";
171 }
172
173 }