/* * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.amazonaws.hal.client; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.xml.bind.DatatypeConverter; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Date; import java.util.List; import java.util.Map; class ConversionUtil { //------------------------------------------------------------- // Variables - Private //------------------------------------------------------------- private static final Log log = LogFactory.getLog(ConversionUtil.class); //------------------------------------------------------------- // Constructors //------------------------------------------------------------- private ConversionUtil() { } //------------------------------------------------------------- // Methods - Package //------------------------------------------------------------- static Type getCollectionType(Type type, int index, Class defaultClass) { if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; return parameterizedType.getActualTypeArguments()[index]; } else { return defaultClass; } } static String getPropertyName(String original) { StringBuilder propertyName = new StringBuilder(original.substring("get".length())); propertyName.setCharAt(0, Character.toLowerCase(propertyName.charAt(0))); return propertyName.toString(); } static Object convert(Type type, Object value) { if (value == null) { return convertFromNull(type); } else if (value instanceof Number) { return convertFromNumber((Class) type, (Number) value); } else if (value instanceof Boolean) { return value; } else if (value instanceof String) { return convertFromString((Class) type, (String) value); } else if (value instanceof Map) { return convertFromMap(type, (Map) value); } else if (value instanceof List) { return convertFromList(type, (List) value); } else { throw new RuntimeException("Not sure how to convert " + value + " to a " + type); } } //------------------------------------------------------------- // Methods - Private //------------------------------------------------------------- private static Object convertFromNull(Type type) { if (!(type instanceof Class)) { return null; } Class<?> clazz = (Class) type; if (!clazz.isPrimitive()) { return null; } if (int.class.isAssignableFrom(clazz)) { return 0; } else if (long.class.isAssignableFrom(clazz)) { return 0L; } else if (short.class.isAssignableFrom(clazz)) { return 0; } else if (double.class.isAssignableFrom(clazz)) { return 0.0; } else if (float.class.isAssignableFrom(clazz)) { return 0.0F; } else if (boolean.class.isAssignableFrom(clazz)) { return Boolean.FALSE; } else if (char.class.isAssignableFrom(clazz)) { return 0; } else if (byte.class.isAssignableFrom(clazz)) { return 0; } else { throw new RuntimeException("Unexpected primitive type: " + clazz.getSimpleName()); } } private static Object convertFromNumber(Class<?> clazz, Number value) { if (String.class.isAssignableFrom(clazz)) { return value.toString(); } else if (int.class.isAssignableFrom(clazz) || Integer.class.isAssignableFrom(clazz)) { return value.intValue(); } else if (long.class.isAssignableFrom(clazz) || Long.class.isAssignableFrom(clazz)) { return value.longValue(); } else if (short.class.isAssignableFrom(clazz) || Short.class.isAssignableFrom(clazz)) { return value.shortValue(); } else if (double.class.isAssignableFrom(clazz) || Double.class.isAssignableFrom(clazz)) { return value.doubleValue(); } else if (float.class.isAssignableFrom(clazz) || Float.class.isAssignableFrom(clazz)) { return value.floatValue(); } else if (boolean.class.isAssignableFrom(clazz) || Boolean.class.isAssignableFrom(clazz)) { return Boolean.valueOf(value.toString()); } else if (char.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz)) { if (value.longValue() <= 255) { return (char)value.longValue(); } else { throw new RuntimeException("Not sure how to convert " + value + " to a " + clazz.getSimpleName()); } } else if (byte.class.isAssignableFrom(clazz) || Byte.class.isAssignableFrom(clazz)) { return value.byteValue(); } else if (BigDecimal.class.isAssignableFrom(clazz)) { return new BigDecimal(value.toString()); } else if (BigInteger.class.isAssignableFrom(clazz)) { // Necessary because BigInteger(long) is a private method and we need to convert the Number to a long to // prevent the constructor from throwing a NumberFormatException Example: BigInteger(1.2) return new BigInteger(String.valueOf(value.longValue())); } else if (Date.class.isAssignableFrom(clazz)) { return new Date(value.longValue()); } else if (clazz.isEnum()) { try { //noinspection unchecked return Enum.valueOf((Class<Enum>) clazz, value.toString()); } catch (IllegalArgumentException e) { log.error(String.format("'%s' is not a recognized enum value for %s. Returning default of %s instead.", value, clazz.getName(), clazz.getEnumConstants()[0])); return clazz.getEnumConstants()[0]; } } else { throw new RuntimeException("Not sure how to convert " + value + " to a " + clazz.getSimpleName()); } } private static Object convertFromString(Class<?> clazz, String value) { if (String.class.isAssignableFrom(clazz)) { return value; } else if (int.class.isAssignableFrom(clazz) || Integer.class.isAssignableFrom(clazz)) { return new Integer(value); } else if (long.class.isAssignableFrom(clazz) || Long.class.isAssignableFrom(clazz)) { return new Long(value); } else if (short.class.isAssignableFrom(clazz) || Short.class.isAssignableFrom(clazz)) { return new Short(value); } else if (double.class.isAssignableFrom(clazz) || Double.class.isAssignableFrom(clazz)) { return new Double(value); } else if (float.class.isAssignableFrom(clazz) || Float.class.isAssignableFrom(clazz)) { return new Float(value); } else if (boolean.class.isAssignableFrom(clazz) || Boolean.class.isAssignableFrom(clazz)) { return Boolean.valueOf(value); } else if (char.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz)) { return value.charAt(0); } else if (byte.class.isAssignableFrom(clazz) || Byte.class.isAssignableFrom(clazz)) { return new Byte(value); } else if (BigDecimal.class.isAssignableFrom(clazz)) { return new BigDecimal(value); } else if (BigInteger.class.isAssignableFrom(clazz)) { return new BigInteger(value); } else if (Date.class.isAssignableFrom(clazz)) { try { return new Date(Long.parseLong(value)); } catch (NumberFormatException e) { try { return DatatypeConverter.parseDateTime(value).getTime(); } catch (IllegalArgumentException e1) { throw new RuntimeException("Unexpected date format: " + value + ". We currently parse xsd:datetime and milliseconds."); } } } else if (clazz.isEnum()) { try { //noinspection unchecked return Enum.valueOf((Class<Enum>) clazz, value); } catch (IllegalArgumentException e) { log.error(String.format("'%s' is not a recognized enum value for %s. Returning default of %s instead.", value, clazz.getName(), clazz.getEnumConstants()[0])); return clazz.getEnumConstants()[0]; } } else { throw new RuntimeException("Not sure how to convert " + value + " to a " + clazz.getSimpleName()); } } private static Object convertFromMap(Type type, Map value) { if (type instanceof Class && !Map.class.isAssignableFrom((Class) type)) { Class typeClass = (Class) type; return Proxy.newProxyInstance(typeClass.getClassLoader(), new Class<?>[] { typeClass }, new MapBackedInvocationHandler(type, value)); } else { return new ConvertingMap(getCollectionType(type, 1, Object.class), value); } } private static Object convertFromList(Type type, List value) { return new ConvertingList(getCollectionType(type, 0, Object.class), value); } }