/************************************************************************ * Copyright (c) 2014-2016 IoT-Solutions e.U. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License 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 iot.jcypher.domain.mapping; import iot.jcypher.domain.internal.DomainAccess.InternalDomainAccess; import iot.jcypher.query.values.JcBoolean; import iot.jcypher.query.values.JcNumber; import iot.jcypher.query.values.JcPrimitive; import iot.jcypher.query.values.JcString; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.sql.Time; import java.sql.Timestamp; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; public class MappingUtil { public static ThreadLocal<InternalDomainAccess> internalDomainAccess = new ThreadLocal<InternalDomainAccess>(); private static SimpleDateFormat simpleDateFormat; public static String dateToString(Date date) { return getSimpleDateFormat().format(date); } public static Date stringToDate(String date, Class<?> dateType) { try { Date dat = getSimpleDateFormat().parse(date); if (dateType.equals(java.sql.Date.class)) return new java.sql.Date(dat.getTime()); else if (dateType.equals(Time.class)) return new Time(dat.getTime()); else if (dateType.equals(Timestamp.class)) return new Timestamp(dat.getTime()); return dat; } catch (ParseException e) { throw new RuntimeException(e); } } public static long dateToLong(Date date) { return date.getTime(); } public static Date longToDate(long millis, Class<?> dateType) { if (dateType.equals(java.sql.Date.class)) return new java.sql.Date(millis); else if (dateType.equals(Time.class)) return new Time(millis); else if (dateType.equals(Timestamp.class)) return new Timestamp(millis); return new Date(millis); } public static boolean mapsToProperty(Class<?> type) { return isSimpleType(type); } public static boolean isSimpleType(Class<?> type) { return type.isPrimitive() || String.class.isAssignableFrom(type) || Number.class.isAssignableFrom(type) || Boolean.class.isAssignableFrom(type) || Date.class.isAssignableFrom(type) || Enum.class.isAssignableFrom(type) || type.isArray() && type.getComponentType().isPrimitive(); // TODO arrays containing Date types ? } @SuppressWarnings("rawtypes") public static Object convertToProperty(Object value) { if (value != null) { if (Date.class.isAssignableFrom(value.getClass())) { return dateToLong((Date) value); } else if (Enum.class.isAssignableFrom(value.getClass())) { return ((Enum<?>)value).name(); } else if (value instanceof Map) { Map map = (Map)value; if (map.isEmpty()) { // empty maps are mapped to empty lists return Collections.EMPTY_LIST; } } else if (value instanceof Collection<?>) { List<Object> ret = new ArrayList<Object>(); Iterator<?> it = ((Collection<?>)value).iterator(); while(it.hasNext()) { ret.add(convertToProperty(it.next())); } return ret; } } return value; } /** * @param value * @param targetType * @return */ public static Object convertFromProperty(Object value, Class<?> targetType) { return MappingUtil.convertFromProperty(value, targetType, null, null); } @SuppressWarnings({ "unchecked", "rawtypes" }) public static <T extends Collection, S extends Collection> T convertCollection(S source, String collType) { if (!source.getClass().getName().equals(collType)) { // need to convert try { Class<T> collCls = (Class<T>) Class.forName(collType); T coll = collCls.newInstance(); for(Object val : source) { coll.add(val); } return coll; } catch (Throwable e) { if (e instanceof RuntimeException) throw (RuntimeException)e; else throw new RuntimeException(e); } } return (T) source; } /** * @param value * @param targetType * @param componentType may be null in case when targetType is not a collection, * or when the listComponentType cannot be determined * @param concreteFieldType * @return */ @SuppressWarnings("unchecked") public static Object convertFromProperty(Object value, Class<?> targetType, Class<?>componentType, Class<?> concreteFieldType) { if (value != null) { if (Date.class.isAssignableFrom(targetType) && value instanceof Number) { return longToDate(((Number)value).longValue(), targetType); } else if (Enum.class.isAssignableFrom(targetType)) { Object[] enums=getEnumValues((Class<? extends Enum<?>>) targetType); for (int i = 0; i < enums.length; i++) { if (((Enum<?>)enums[i]).name().equals(value.toString())) return enums[i]; } return value; } else if (Collection.class.isAssignableFrom(targetType)) { if (componentType != null) { List<Object> converted = new ArrayList<>(); Collection<Object> coll = (Collection<Object>)value; for (Object elem : coll) { converted.add(convertFromProperty(elem, componentType, null, null)); } try { coll.clear(); coll.addAll(converted); } catch (UnsupportedOperationException e) { return converted; } } } else if (targetType.isArray()) { if (componentType != null) { if (value instanceof Collection<?>) { List<Object> converted = new ArrayList<>(); Collection<Object> coll = (Collection<Object>)value; for (Object elem : coll) { converted.add(convertFromProperty(elem, componentType, null, null)); } if (componentType.isPrimitive()) { Object array = Array.newInstance(componentType, coll.size()); for (int i = 0; i < converted.size(); i++) { Array.set(array, i, converted.get(i)); } return array; } else { try { coll.clear(); coll.addAll(converted); } catch (UnsupportedOperationException e) { return converted; } } } } } else if (Map.class.isAssignableFrom(targetType)) { // only possible for empty maps if (concreteFieldType != null) { try { return concreteFieldType.newInstance(); } catch (Throwable e) { throw new RuntimeException(e); } } } else if (targetType.equals(value.getClass())) { return value; } else if (targetType.isPrimitive()) { return convertToPrimitive(value, targetType); } else if (targetType.isAssignableFrom(value.getClass())) { return targetType.cast(value); } else if (Number.class.isAssignableFrom(targetType) && value instanceof Number) { return convertToDistinctNumber(value, targetType); } } return value; } /** * Answer an appropriate instance of a JcPrimitive for the given simple-type and name. * E.g. given a type java.lang.String, a JcString instance will be returned. * @param type * @param name * @return */ public static JcPrimitive fromType(Class<?> type, String name) { // TODO what about dates and arrays if (type.equals(String.class)) return new JcString(name); else if (type.equals(Number.class)) return new JcNumber(name); else if (type.equals(Boolean.class)) return new JcBoolean(name); return null; } /** * finds enum values in normal enum classes and in dynamically created ones. * @param clazz * @return */ public static Object[] getEnumValues(Class<? extends Enum<?>> clazz) { Object[] enums=clazz.getEnumConstants(); if (enums == null) { Method[] mthds = clazz.getDeclaredMethods(); Method mthd = null; for (Method mth : mthds) { if (mth.getName().equals("values")) { mthd = mth; break; } } if (mthd != null) try { enums = (Object[]) mthd.invoke(null); } catch (Throwable e) { throw new RuntimeException(e); } } return enums; } private static Object convertToPrimitive(Object value, Class<?> targetType) { if (targetType.equals(Short.TYPE) && value instanceof Number) return ((Number)value).shortValue(); else if (targetType.equals(Integer.TYPE) && value instanceof Number) return ((Number)value).intValue(); else if (targetType.equals(Long.TYPE) && value instanceof Number) return ((Number)value).longValue(); else if (targetType.equals(Float.TYPE) && value instanceof Number) return ((Number)value).floatValue(); else if (targetType.equals(Double.TYPE) && value instanceof Number) return ((Number)value).doubleValue(); else if (targetType.equals(Boolean.TYPE) && value instanceof Boolean) return ((Boolean)value).booleanValue(); return value; } private static Object convertToDistinctNumber(Object value, Class<?> targetType) { if (targetType.equals(Short.class)) return ((Number)value).shortValue(); else if (targetType.equals(Integer.class)) return ((Number)value).intValue(); else if (targetType.equals(Long.class)) return ((Number)value).longValue(); else if (targetType.equals(Float.class)) return ((Number)value).floatValue(); else if (targetType.equals(Double.class)) return ((Number)value).doubleValue(); return value; } private static SimpleDateFormat getSimpleDateFormat() { if (simpleDateFormat == null) { simpleDateFormat = new SimpleDateFormat("dd.MM.yyyy-HH:mm:ss:SSS", new Locale("de", "AT")); } return simpleDateFormat; } }