View Javadoc

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 }