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    }