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.conversion;
017
018 import org.apache.commons.convert.Conversion;
019 import org.apache.commons.convert.ConversionFactory;
020 import org.apache.commons.convert.Converter;
021
022 /**
023 * Abstract base class for creating conversion factories.
024 * <p>
025 * This class is especially suited for creating factories as it minimises the effort
026 * required in the subclass.
027 * <p>
028 * To implement a conversion from a class and all of it subclasses, simply extend
029 * this class and implement the {@link #convertValue(Object, Converter)} method.
030 *
031 * @author Stephen Colebourne
032 * @author Oliver Heger
033 * @version $Id: AbstractConversionFactory.java 155441 2005-02-26 13:19:22Z dirkv $
034 * @since 1.0
035 */
036 public abstract class AbstractConversionFactory implements ConversionFactory {
037
038 /** Useful constant for subclass constructors */
039 protected static final Class STRING_CLASS = String.class;
040
041 /** The base type to convert from */
042 private final Class baseFromType;
043 /** The type to convert to */
044 private final Class toType;
045
046 /**
047 * Restricted constructor.
048 *
049 * @param baseFromType the base of the class hierarchy to convert from
050 * @param toType the type to convert to
051 */
052 protected AbstractConversionFactory(Class baseFromType, Class toType) {
053 super();
054 this.baseFromType = baseFromType;
055 this.toType = toType;
056 }
057
058 //-----------------------------------------------------------------------
059 /**
060 * Checks if this factory matches the request conversion.
061 * <p>
062 * This implementation returns {@link #getMatchPercent()} if the specified
063 * <code>fromType</code> is the same as, or a subclass of, the stored
064 * <code>baseFromType</code> and the specified <code>toType</code> matches
065 * the stored <code>toType</code>.
066 *
067 * @param value the value to be converted, read only, may be null
068 * @param fromType the type to convert from, may be null
069 * @param toType the type to convert to, may be null
070 * @return 60 if baseFromType or subclass matches fromType and toType matches
071 */
072 public int getMatchPercent(Object value, Class fromType, Class toType) {
073 if (toType == getToType() && getBaseFromType().isAssignableFrom(fromType)) {
074 return getMatchPercent();
075 }
076 return 0;
077 }
078
079 /**
080 * Gets the percentage to return if a match occurs.
081 * <p>
082 * This implementation returns <code>60</code>.
083 * Subclasses may wish to override and return a different value.
084 *
085 * @return 60
086 */
087 protected int getMatchPercent() {
088 return 60;
089 }
090
091 /**
092 * Create a new conversion object for the specified from and to types.
093 * <p>
094 * This implementation returns a <code>Conversion</code> instance that
095 * refers back to this class for its implementation of the actual conversion
096 * via {@link #convertValue(Object, Converter)}.
097 *
098 * @param value the value to be converted, read only, may be null
099 * @param fromType the type to convert from, may be null
100 * @param toType the type to convert to, may be null
101 * @return a Conversion object for repeatedly performing conversions
102 */
103 public Conversion getInstance(Object value, Class fromType, Class toType) {
104 return new FactoryConversion(fromType, toType, this);
105 }
106
107 /**
108 * Convert the non-null value to another type.
109 * <p>
110 * This method is implemented by subclases to perform the actual conversion.
111 *
112 * @param value the input value to be converted, pre-checked to not be null
113 * @param converter the converter being used, not null
114 * @return the converted value
115 * @throws Exception if conversion fails, use ConversionException if creating
116 * a new exception, otherwise just allow exceptions to be thrown
117 */
118 protected abstract Object convertValue(Object value, Converter converter) throws Exception;
119
120 //-----------------------------------------------------------------------
121 /**
122 * The base type to convert from.
123 * This type and its subclasses will be matched by this factory.
124 *
125 * @return the Class object representing the class to convert to
126 */
127 public Class getBaseFromType() {
128 return baseFromType;
129 }
130
131 /**
132 * The type to convert to.
133 *
134 * @return the Class object representing the class to convert from
135 */
136 public Class getToType() {
137 return toType;
138 }
139
140 //-----------------------------------------------------------------------
141 /**
142 * Gets a suitable debugging string.
143 *
144 * @return a debugging string
145 */
146 public String toString() {
147 String from = convertClassToName(getBaseFromType());
148 String to = convertClassToName(getToType());
149 return "ConversionFactory[" + from + "->" + to + "]";
150 }
151
152 /**
153 * Converts a class to a string name for debugging.
154 *
155 * @param cls the class to convert
156 * @return the class name
157 */
158 private String convertClassToName(Class cls) {
159 if (cls == null) {
160 return "null";
161 }
162 String str = cls.getName();
163 int pos = str.lastIndexOf('.');
164 if (str.substring(0, pos).equals("java.lang")) {
165 str = str.substring(pos + 1);
166 }
167 if (str.substring(0, pos).equals("java.util")) {
168 str = str.substring(pos + 1);
169 }
170 return str;
171 }
172
173 //-----------------------------------------------------------------------
174 /**
175 * Generic conversion implementation that delegates back to the factory.
176 */
177 class FactoryConversion extends AbstractConversion {
178
179 /** The factory instance that has the actual conversion code */
180 private final AbstractConversionFactory factory;
181
182 /**
183 * Constructs a Conversion.
184 *
185 * @param fromType the type to convert from
186 * @param toType the type to convert to
187 * @param factory the factory instance that actually performs the conversion
188 */
189 FactoryConversion(Class fromType, Class toType, AbstractConversionFactory factory) {
190 super(fromType, toType);
191 this.factory = factory;
192 }
193
194 /**
195 * Convert the non-null value to another type.
196 * <p>
197 * This implementation delegates to the factory method
198 * {@link AbstractConversionFactory#convertValue(Object, Converter)}.
199 *
200 * @param value the input value to be converted, pre-checked to not be null
201 * @param converter the converter being used, not null
202 * @return the converted value
203 * @throws Exception if conversion fails, use ConversionException if creating
204 * a new exception, otherwise just allow exceptions to be thrown
205 */
206 protected Object convertValue(Object value, Converter converter) throws Exception {
207 return factory.convertValue(value, converter);
208 }
209 }
210
211 }