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 */
017package org.apache.commons.math4.util;
018
019import java.io.Serializable;
020import java.util.Collection;
021import java.util.HashMap;
022import java.util.Map;
023import java.util.Set;
024
025import org.apache.commons.math4.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 */
033public class TransformerMap implements NumberTransformer, Serializable {
034
035    /** Serializable version identifier */
036    private static final long serialVersionUID = 4605318041528645258L;
037
038    /**
039     * A default Number Transformer for Numbers and numeric Strings.
040     */
041    private NumberTransformer defaultTransformer;
042
043    /**
044     * The internal Map.
045     */
046    private Map<Class<?>, NumberTransformer> map;
047
048    /**
049     * Build a map containing only the default transformer.
050     */
051    public TransformerMap() {
052        map = new HashMap<>();
053        defaultTransformer = new DefaultTransformer();
054    }
055
056    /**
057     * Tests if a Class is present in the TransformerMap.
058     * @param key Class to check
059     * @return true|false
060     */
061    public boolean containsClass(Class<?> key) {
062        return map.containsKey(key);
063    }
064
065    /**
066     * Tests if a NumberTransformer is present in the TransformerMap.
067     * @param value NumberTransformer to check
068     * @return true|false
069     */
070    public boolean containsTransformer(NumberTransformer value) {
071        return map.containsValue(value);
072    }
073
074    /**
075     * Returns the Transformer that is mapped to a class
076     * if mapping is not present, this returns null.
077     * @param key The Class of the object
078     * @return the mapped NumberTransformer or null.
079     */
080    public NumberTransformer getTransformer(Class<?> key) {
081        return map.get(key);
082    }
083
084    /**
085     * Sets a Class to Transformer Mapping in the Map. If
086     * the Class is already present, this overwrites that
087     * mapping.
088     * @param key The Class
089     * @param transformer The NumberTransformer
090     * @return the replaced transformer if one is present
091     */
092    public NumberTransformer putTransformer(Class<?> key, NumberTransformer transformer) {
093        return map.put(key, transformer);
094    }
095
096    /**
097     * Removes a Class to Transformer Mapping in the Map.
098     * @param key The Class
099     * @return the removed transformer if one is present or
100     * null if none was present.
101     */
102    public NumberTransformer removeTransformer(Class<?> key) {
103        return map.remove(key);
104    }
105
106    /**
107     * Clears all the Class to Transformer mappings.
108     */
109    public void clear() {
110        map.clear();
111    }
112
113    /**
114     * Returns the Set of Classes used as keys in the map.
115     * @return Set of Classes
116     */
117    public Set<Class<?>> classes() {
118        return map.keySet();
119    }
120
121    /**
122     * Returns the Set of NumberTransformers used as values
123     * in the map.
124     * @return Set of NumberTransformers
125     */
126    public Collection<NumberTransformer> transformers() {
127        return map.values();
128    }
129
130    /**
131     * Attempts to transform the Object against the map of
132     * NumberTransformers. Otherwise it returns Double.NaN.
133     *
134     * @param o the Object to be transformed.
135     * @return the double value of the Object.
136     * @throws MathIllegalArgumentException if the Object can not be
137     * transformed into a Double.
138     * @see org.apache.commons.math4.util.NumberTransformer#transform(java.lang.Object)
139     */
140    @Override
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}