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.activemq.util;
018    
019    import java.net.URI;
020    import java.net.URISyntaxException;
021    import java.util.Date;
022    import java.util.HashMap;
023    import java.util.Map;
024    
025    import org.apache.activemq.command.ActiveMQDestination;
026    
027    /**
028     * Type conversion support for ActiveMQ.
029     */
030    public final class TypeConversionSupport {
031    
032        private static class ConversionKey {
033            final Class from;
034            final Class to;
035            final int hashCode;
036    
037            public ConversionKey(Class from, Class to) {
038                this.from = from;
039                this.to = to;
040                this.hashCode = from.hashCode() ^ (to.hashCode() << 1);
041            }
042    
043            public boolean equals(Object o) {
044                ConversionKey x = (ConversionKey)o;
045                return x.from == from && x.to == to;
046            }
047    
048            public int hashCode() {
049                return hashCode;
050            }
051        }
052    
053        public interface Converter {
054            Object convert(Object value);
055        }
056    
057        private static final Map<ConversionKey, Converter> CONVERSION_MAP = new HashMap<ConversionKey, Converter>();
058        static {
059            Converter toStringConverter = new Converter() {
060                public Object convert(Object value) {
061                    return value.toString();
062                }
063            };
064            CONVERSION_MAP.put(new ConversionKey(Boolean.class, String.class), toStringConverter);
065            CONVERSION_MAP.put(new ConversionKey(Byte.class, String.class), toStringConverter);
066            CONVERSION_MAP.put(new ConversionKey(Short.class, String.class), toStringConverter);
067            CONVERSION_MAP.put(new ConversionKey(Integer.class, String.class), toStringConverter);
068            CONVERSION_MAP.put(new ConversionKey(Long.class, String.class), toStringConverter);
069            CONVERSION_MAP.put(new ConversionKey(Float.class, String.class), toStringConverter);
070            CONVERSION_MAP.put(new ConversionKey(Double.class, String.class), toStringConverter);
071    
072            CONVERSION_MAP.put(new ConversionKey(String.class, Boolean.class), new Converter() {
073                public Object convert(Object value) {
074                    return Boolean.valueOf((String)value);
075                }
076            });
077            CONVERSION_MAP.put(new ConversionKey(String.class, Byte.class), new Converter() {
078                public Object convert(Object value) {
079                    return Byte.valueOf((String)value);
080                }
081            });
082            CONVERSION_MAP.put(new ConversionKey(String.class, Short.class), new Converter() {
083                public Object convert(Object value) {
084                    return Short.valueOf((String)value);
085                }
086            });
087            CONVERSION_MAP.put(new ConversionKey(String.class, Integer.class), new Converter() {
088                public Object convert(Object value) {
089                    return Integer.valueOf((String)value);
090                }
091            });
092            CONVERSION_MAP.put(new ConversionKey(String.class, Long.class), new Converter() {
093                public Object convert(Object value) {
094                    return Long.valueOf((String)value);
095                }
096            });
097            CONVERSION_MAP.put(new ConversionKey(String.class, Float.class), new Converter() {
098                public Object convert(Object value) {
099                    return Float.valueOf((String)value);
100                }
101            });
102            CONVERSION_MAP.put(new ConversionKey(String.class, Double.class), new Converter() {
103                public Object convert(Object value) {
104                    return Double.valueOf((String)value);
105                }
106            });
107    
108            Converter longConverter = new Converter() {
109                public Object convert(Object value) {
110                    return Long.valueOf(((Number)value).longValue());
111                }
112            };
113            CONVERSION_MAP.put(new ConversionKey(Byte.class, Long.class), longConverter);
114            CONVERSION_MAP.put(new ConversionKey(Short.class, Long.class), longConverter);
115            CONVERSION_MAP.put(new ConversionKey(Integer.class, Long.class), longConverter);
116            CONVERSION_MAP.put(new ConversionKey(Date.class, Long.class), new Converter() {
117                public Object convert(Object value) {
118                    return Long.valueOf(((Date)value).getTime());
119                }
120            });
121    
122            Converter intConverter = new Converter() {
123                public Object convert(Object value) {
124                    return Integer.valueOf(((Number)value).intValue());
125                }
126            };
127            CONVERSION_MAP.put(new ConversionKey(Byte.class, Integer.class), intConverter);
128            CONVERSION_MAP.put(new ConversionKey(Short.class, Integer.class), intConverter);
129    
130            CONVERSION_MAP.put(new ConversionKey(Byte.class, Short.class), new Converter() {
131                public Object convert(Object value) {
132                    return Short.valueOf(((Number)value).shortValue());
133                }
134            });
135    
136            CONVERSION_MAP.put(new ConversionKey(Float.class, Double.class), new Converter() {
137                public Object convert(Object value) {
138                    return new Double(((Number)value).doubleValue());
139                }
140            });
141            CONVERSION_MAP.put(new ConversionKey(String.class, ActiveMQDestination.class), new Converter() {
142                public Object convert(Object value) {
143                    return ActiveMQDestination.createDestination((String)value, ActiveMQDestination.QUEUE_TYPE);
144                }
145            });
146            CONVERSION_MAP.put(new ConversionKey(String.class, URI.class), new Converter() {
147                public Object convert(Object value) {
148                    String text = value.toString();
149                    try {
150                        return new URI(text);
151                    } catch (URISyntaxException e) {
152                        throw new RuntimeException(e);
153                    }
154                }
155            });
156        }
157    
158        private TypeConversionSupport() {
159        }
160    
161        public static Object convert(Object value, Class to) {
162            if (value == null) {
163                // lets avoid NullPointerException when converting to boolean for null values
164                if (boolean.class.isAssignableFrom(to)) {
165                    return Boolean.FALSE;
166                }
167                return null;
168            }
169    
170            // eager same instance type test to avoid the overhead of invoking the type converter
171            // if already same type
172            if (to.isInstance(value)) {
173                return to.cast(value);
174            }
175    
176            // lookup converter
177            Converter c = lookupConverter(value.getClass(), to);
178            if (c != null) {
179                return c.convert(value);
180            } else {
181                return null;
182            }
183        }
184    
185        public static Converter lookupConverter(Class from, Class to) {
186            // use wrapped type for primitives
187            if (from.isPrimitive()) {
188                from = convertPrimitiveTypeToWrapperType(from);
189            }
190            if (to.isPrimitive()) {
191                to = convertPrimitiveTypeToWrapperType(to);
192            }
193    
194            return CONVERSION_MAP.get(new ConversionKey(from, to));
195        }
196    
197        /**
198         * Converts primitive types such as int to its wrapper type like
199         * {@link Integer}
200         */
201        private static Class<?> convertPrimitiveTypeToWrapperType(Class<?> type) {
202            Class<?> rc = type;
203            if (type.isPrimitive()) {
204                if (type == int.class) {
205                    rc = Integer.class;
206                } else if (type == long.class) {
207                    rc = Long.class;
208                } else if (type == double.class) {
209                    rc = Double.class;
210                } else if (type == float.class) {
211                    rc = Float.class;
212                } else if (type == short.class) {
213                    rc = Short.class;
214                } else if (type == byte.class) {
215                    rc = Byte.class;
216                } else if (type == boolean.class) {
217                    rc = Boolean.class;
218                }
219            }
220            return rc;
221        }
222    
223    }