001 /* 002 * Copyright 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.convert; 017 018 import java.util.HashMap; 019 import java.util.Map; 020 021 import org.apache.commons.convert.conversion.ClassToStringConversion; 022 import org.apache.commons.convert.conversion.ObjectToStringConversionFactory; 023 import org.apache.commons.convert.conversion.TimeZoneToStringConversionFactory; 024 025 /** 026 * ConversionRegistry manages the <code>Conversion</code> and 027 * <code>ConversionFactory</code> objects. 028 * <p> 029 * This class allows conversions to be added, removed and looked up. 030 * This implementation is fully synchronized. 031 * <pre> 032 * Converter converter = new Converter(); 033 * converter.getRegistry().addDefaultConversions(); 034 * converter.getRegistry().addConversion(new MyNewConversion()); 035 * </pre> 036 * 037 * @author Stephen Colebourne 038 * @version $Id: ConversionRegistry.java 155441 2005-02-26 13:19:22Z dirkv $ 039 * @since 1.0 040 */ 041 public class ConversionRegistry { 042 043 /** Map of from class to conversion */ 044 protected Map iConversions = new HashMap(); 045 /** Array of conversion factories */ 046 protected ConversionFactory[] iFactories = new ConversionFactory[0]; 047 /** Object to synchronize on for factories */ 048 protected final Object iFactoryLock = new Object(); 049 050 /** 051 * Restricted constructor, use Converter. 052 */ 053 protected ConversionRegistry() { 054 super(); 055 } 056 057 //----------------------------------------------------------------------- 058 /** 059 * Add the default set of conversions to the registry. 060 */ 061 public void addDefaultConversions() { 062 synchronized (iConversions) { 063 addConversion(ClassToStringConversion.INSTANCE); 064 } 065 synchronized (iFactoryLock) { 066 addConversionFactory(ObjectToStringConversionFactory.INSTANCE); 067 addConversionFactory(TimeZoneToStringConversionFactory.INSTANCE); 068 } 069 } 070 071 //----------------------------------------------------------------------- 072 /** 073 * Adds a Conversion to the map of known conversions. 074 * Any previous conversion for this from-to pair is replaced. 075 * 076 * @param conv the conversion to add 077 */ 078 public void addConversion(Conversion conv) { 079 if (conv != null) { 080 synchronized (iConversions) { 081 Map map = (Map) iConversions.get(conv.getFromType()); 082 if (map == null) { 083 map = new HashMap(); 084 iConversions.put(conv.getFromType(), map); 085 } 086 map.put(conv.getToType(), conv); 087 } 088 } 089 } 090 091 /** 092 * Adds a ConversionFactory to the set of known factories. 093 * Any previous factory that matches by <code>equals()</code> is replaced. 094 * 095 * @param factory the factory to add 096 */ 097 public void addConversionFactory(ConversionFactory factory) { 098 if (factory != null) { 099 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 }