/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.util; import java.math.BigDecimal; import java.math.BigInteger; import java.text.NumberFormat; import java.util.HashMap; import org.geotools.factory.Hints; /** * ConverterFactory which converts between the "standard" numeric types. * <p> * Supported types: * <ul> * <li>{@link java.lang.Long} * <li>{@link java.lang.Integer} * <li>{@link java.lang.Short} * <li>{@link java.lang.Byte} * <li>{@link java.lang.BigInteger} * <li>{@link java.lang.Double} * <li>{@link java.lang.Float} * <li>{@link java.lang.BigDecimal} * </ul> * </p> * * @author Justin Deoliveira, The Open Planning Project * @since 2.4 * * * @source $URL$ */ public class NumericConverterFactory implements ConverterFactory { public Converter createConverter(Class source, Class target, Hints hints) { //convert to non-primitive class source = primitiveToWrapperClass(source); target = primitiveToWrapperClass(target); // check if source is a number or a string. We can't convert to a number // from anything else. if (!(Number.class.isAssignableFrom(source)) && !(String.class.isAssignableFrom(source))) return null; // check if target is one of supported if (Long.class.equals(target) || Integer.class.equals(target) || Short.class.equals(target) || Byte.class.equals(target) || BigInteger.class.equals(target) || BigDecimal.class.equals(target) || Double.class.equals(target) || Float.class.equals(target) || Number.class.equals(target)) { //check if teh safe conversion flag was set and if so only allow save conversions if (hints != null){ Object safeConversion = hints.get(ConverterFactory.SAFE_CONVERSION); if (safeConversion instanceof Boolean && ((Boolean)safeConversion).booleanValue()) { return new SafeNumericConverter(); } } return new NumericConverter(); } return null; } class SafeNumericConverter implements Converter { public <T> T convert(Object source, Class<T> target) throws Exception { target = primitiveToWrapperClass(target); if (source instanceof Number){ Number number = (Number) source; Class c = number.getClass(); if (BigDecimal.class.equals(target)){ return (T) new BigDecimal(source.toString()); } else if (Double.class.equals(target)){ if (c != BigDecimal.class && c != BigInteger.class){ if ( c == Float.class ) { //this is done to avoid coordinate drift return (T) new Double(number.toString()); } return (T) Double.valueOf(number.doubleValue()); //return (T) new Double(source.toString()); } } else if (Float.class.equals(target)){ if (c == Float.class || c == Integer.class || c == Short.class || c == Byte.class ) { return (T) Float.valueOf( number.floatValue() ); //return (T) new Float(source.toString()); } } else if (BigInteger.class.equals(target)){ if ( BigInteger.class.isAssignableFrom(c) || c == Long.class || c == Integer.class || c == Short.class || c == Byte.class ) { return (T) new BigInteger( number.toString() ); //return (T) new BigInteger(source.toString()); } } else if (Long.class.equals(target)){ if (c == Long.class || c == Integer.class || c == Short.class || c == Byte.class) { return (T) Long.valueOf( number.longValue() ); //return (T) new Long(source.toString()); } } else if (Integer.class.equals(target)){ if (c == Integer.class || c == Short.class || c == Byte.class ) { return (T) Integer.valueOf( number.intValue() ); //return (T) new Integer(source.toString()); } } else if (Short.class.equals(target)){ if (c == Short.class || c == Byte.class ) { return (T) Short.valueOf(number.shortValue()); //return (T) new Short(source.toString()); } } else if (Byte.class.equals(target)){ if ( c == Byte.class ) { return (T) Byte.valueOf(number.byteValue()); //return (T) new Byte(source.toString()); } } } else if (source instanceof String){ String src = (String) source; try { if (BigDecimal.class.isAssignableFrom( target ) ) { return (T) new BigDecimal(src); //if (x.toString().equals(src)) // return (T) x; } else if (target == Double.class) { Double x = new Double(src); if (x.toString().equals(src)) return (T) x; } else if (target == Float.class) { Float x = new Float(src); if (x.toString().equals(src)) return (T) x; } else if (BigInteger.class.isAssignableFrom(target)) { BigInteger x = new BigInteger(src); if (x.toString().equals(src)) return (T) x; } else if (target == Long.class) { Long x = new Long(src); if (x.toString().equals(src)) return (T) x; } else if (target == Integer.class) { Integer x = new Integer(src); if (x.toString().equals(src)) return (T) x; } else if (target == Short.class) { Short x = new Short(src); if (x.toString().equals(src)) return (T) x; } else if (target == Byte.class) { Byte x = new Byte(src); if (x.toString().equals(src)) return (T) x; } } catch (Exception ex) { return null; } } return null; } } class NumericConverter implements Converter { public Object convert(Object source, Class target) throws Exception { target = primitiveToWrapperClass(target); if (source instanceof Number) { Number s = (Number) source; // integral if (Long.class.equals(target)) { return new Long(s.longValue()); } if (Integer.class.equals(target)) { return new Integer(s.intValue()); } if (Short.class.equals(target)) { return new Short(s.shortValue()); } if (Byte.class.equals(target)) { return new Byte(s.byteValue()); } if (BigInteger.class.equals(target)) { return BigInteger.valueOf(s.longValue()); } // floating point // JD: we use the string reprensentation to avoid coordinate // drift due to precision issues, there could be some // performance issues with this. if (Double.class.equals(target)) { return new Double(s.toString()); } if (Float.class.equals(target)) { return new Float(s.toString()); } if (BigDecimal.class.equals(target)) { return new BigDecimal(s.toString()); } if (Number.class.equals(target)) { try { return new Integer(s.toString()); } catch (Exception e) { } try { return new BigInteger(s.toString()); } catch (Exception e) { } try { return new Double(s.toString()); } catch (Exception e) { } try { return new BigDecimal(s.toString()); } catch (Exception e) { } } } else if (source instanceof String) { String s = (String) source; // ensure we trim any space off the string s = s.trim(); String integral = toIntegral(s); // floating point if (Double.class.equals(target)) { return new Double(s); } if (Float.class.equals(target)) { return new Float(s); } if (BigDecimal.class.equals(target)) { return new BigDecimal(s); } // textual if (Long.class.equals(target)) { return new Long(integral); } if (Integer.class.equals(target)) { return new Integer(integral); } if (Short.class.equals(target)) { return new Short(integral); } if (Byte.class.equals(target)) { return new Byte(integral); } if (BigInteger.class.equals(target)) { return new BigInteger(integral); } // fallback. If you ask for Number, you get our 'best guess' if (Number.class.equals(target)) { if (integral.equals(s)) { // we think this is an integer of some sort try { return new Integer(integral); } catch (Exception e) { } try { return new BigInteger(integral); } catch (Exception e) { } } try { return new Double(s); } catch (Exception e) { } try { return new BigDecimal(s); } catch (Exception e) { } } } // nothing matched. Return null. return null; } } /** * Extract the integral part out of a decimal format string. * * @param s * @return integral component of decimal representation */ static String toIntegral(String s) { // NumberFormat format = NumberFormat.getInstance(); int radex = -1; // last non numeric character to account for "." vs "," seperators for (int i = s.length() - 1; i > 0; i--) { char ch = s.charAt(i); if (!Character.isDigit(ch) && '-' != ch) { radex = i; break; } } if (radex != -1) { // text is formatted in decimal but floating point format supplied return s.substring(0, radex); } else { return s; } } static HashMap<Class, Class> primitiveToWrapper = new HashMap(); static { primitiveToWrapper.put(Byte.TYPE, Byte.class); primitiveToWrapper.put(Short.TYPE, Short.class); primitiveToWrapper.put(Integer.TYPE, Integer.class); primitiveToWrapper.put(Long.TYPE, Long.class); primitiveToWrapper.put(Float.TYPE, Float.class); primitiveToWrapper.put(Double.TYPE, Double.class); primitiveToWrapper.put(Boolean.TYPE, Boolean.class); } static Class primitiveToWrapperClass(Class primitive) { if (primitive.isPrimitive()) { Class wrapper = primitiveToWrapper.get(primitive); primitive = wrapper != null ? wrapper : primitive; } return primitive; } }