001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.math3.util;
018    
019    import java.io.Serializable;
020    import java.util.Collection;
021    import java.util.HashMap;
022    import java.util.Map;
023    import java.util.Set;
024    
025    import org.apache.commons.math3.exception.MathIllegalArgumentException;
026    
027    /**
028     * This TansformerMap automates the transformation of mixed object types.
029     * It provides a means to set NumberTransformers that will be selected
030     * based on the Class of the object handed to the Maps
031     * <code>double transform(Object o)</code> method.
032     * @version $Id: TransformerMap.java 1416643 2012-12-03 19:37:14Z tn $
033     */
034    public class TransformerMap implements NumberTransformer, Serializable {
035    
036        /** Serializable version identifier */
037        private static final long serialVersionUID = 4605318041528645258L;
038    
039        /**
040         * A default Number Transformer for Numbers and numeric Strings.
041         */
042        private NumberTransformer defaultTransformer = null;
043    
044        /**
045         * The internal Map.
046         */
047        private Map<Class<?>, NumberTransformer> map = null;
048    
049        /**
050         * Build a map containing only the default transformer.
051         */
052        public TransformerMap() {
053            map = new HashMap<Class<?>, NumberTransformer>();
054            defaultTransformer = new DefaultTransformer();
055        }
056    
057        /**
058         * Tests if a Class is present in the TransformerMap.
059         * @param key Class to check
060         * @return true|false
061         */
062        public boolean containsClass(Class<?> key) {
063            return map.containsKey(key);
064        }
065    
066        /**
067         * Tests if a NumberTransformer is present in the TransformerMap.
068         * @param value NumberTransformer to check
069         * @return true|false
070         */
071        public boolean containsTransformer(NumberTransformer value) {
072            return map.containsValue(value);
073        }
074    
075        /**
076         * Returns the Transformer that is mapped to a class
077         * if mapping is not present, this returns null.
078         * @param key The Class of the object
079         * @return the mapped NumberTransformer or null.
080         */
081        public NumberTransformer getTransformer(Class<?> key) {
082            return map.get(key);
083        }
084    
085        /**
086         * Sets a Class to Transformer Mapping in the Map. If
087         * the Class is already present, this overwrites that
088         * mapping.
089         * @param key The Class
090         * @param transformer The NumberTransformer
091         * @return the replaced transformer if one is present
092         */
093        public NumberTransformer putTransformer(Class<?> key, NumberTransformer transformer) {
094            return map.put(key, transformer);
095        }
096    
097        /**
098         * Removes a Class to Transformer Mapping in the Map.
099         * @param key The Class
100         * @return the removed transformer if one is present or
101         * null if none was present.
102         */
103        public NumberTransformer removeTransformer(Class<?> key) {
104            return map.remove(key);
105        }
106    
107        /**
108         * Clears all the Class to Transformer mappings.
109         */
110        public void clear() {
111            map.clear();
112        }
113    
114        /**
115         * Returns the Set of Classes used as keys in the map.
116         * @return Set of Classes
117         */
118        public Set<Class<?>> classes() {
119            return map.keySet();
120        }
121    
122        /**
123         * Returns the Set of NumberTransformers used as values
124         * in the map.
125         * @return Set of NumberTransformers
126         */
127        public Collection<NumberTransformer> transformers() {
128            return map.values();
129        }
130    
131        /**
132         * Attempts to transform the Object against the map of
133         * NumberTransformers. Otherwise it returns Double.NaN.
134         *
135         * @param o the Object to be transformed.
136         * @return the double value of the Object.
137         * @throws MathIllegalArgumentException if the Object can not be
138         * transformed into a Double.
139         * @see org.apache.commons.math3.util.NumberTransformer#transform(java.lang.Object)
140         */
141        public double transform(Object o) throws MathIllegalArgumentException {
142            double value = Double.NaN;
143    
144            if (o instanceof Number || o instanceof String) {
145                value = defaultTransformer.transform(o);
146            } else {
147                NumberTransformer trans = getTransformer(o.getClass());
148                if (trans != null) {
149                    value = trans.transform(o);
150                }
151            }
152    
153            return value;
154        }
155    
156        /** {@inheritDoc} */
157        @Override
158        public boolean equals(Object other) {
159            if (this == other) {
160                return true;
161            }
162            if (other instanceof TransformerMap) {
163                TransformerMap rhs = (TransformerMap) other;
164                if (! defaultTransformer.equals(rhs.defaultTransformer)) {
165                    return false;
166                }
167                if (map.size() != rhs.map.size()) {
168                    return false;
169                }
170                for (Map.Entry<Class<?>, NumberTransformer> entry : map.entrySet()) {
171                    if (! entry.getValue().equals(rhs.map.get(entry.getKey()))) {
172                        return false;
173                    }
174                }
175                return true;
176            }
177            return false;
178        }
179    
180        /** {@inheritDoc} */
181        @Override
182        public int hashCode() {
183            int hash = defaultTransformer.hashCode();
184            for (NumberTransformer t : map.values()) {
185                hash = hash * 31 + t.hashCode();
186            }
187            return hash;
188        }
189    
190    }