net.firejack.platform.core.utils.Factory.java Source code

Java tutorial

Introduction

Here is the source code for net.firejack.platform.core.utils.Factory.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 net.firejack.platform.core.utils;

import net.firejack.platform.core.annotation.Property;
import net.firejack.platform.core.domain.AbstractDTO;
import net.firejack.platform.core.model.AbstractModel;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.apache.log4j.Logger;
import org.hibernate.Hibernate;
import org.hibernate.collection.PersistentCollection;
import org.hibernate.proxy.HibernateProxy;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import java.lang.reflect.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Factory extends PropertyUtilsBean {
    protected static final Logger logger = Logger.getLogger(Factory.class);

    private final Pattern pattern = Pattern.compile("\\b[a-z][A-Z]");
    private final ThreadLocal<Map> cache = new InheritableThreadLocal<Map>();
    private final Map<Class, List<FieldInfo>> fields = new HashMap<Class, List<FieldInfo>>();
    private final Map<String, XmlAdapter> adapters = new HashMap<String, XmlAdapter>();

    public void setAdapters(List<XmlAdapter> adapters) {
        for (XmlAdapter adapter : adapters) {
            addAdapter(adapter);
        }
    }

    public void addAdapter(XmlAdapter adapter) {
        Class<? extends XmlAdapter> adapterClass = adapter.getClass();
        Type[] typeArguments = ((ParameterizedTypeImpl) adapterClass.getGenericSuperclass())
                .getActualTypeArguments();
        String marshal = ((Class) typeArguments[0]).getName() + ((Class) typeArguments[1]).getName();
        String unmarshal = ((Class) typeArguments[1]).getName() + ((Class) typeArguments[0]).getName();
        adapters.put(marshal, adapter);
        adapters.put(unmarshal, adapter);
    }

    public <T extends AbstractDTO> List<T> convertTo(Class<T> clazz, List entities) {
        List<T> list = new ArrayList<T>();
        if (entities != null) {
            for (Object entity : entities) {
                T dto = convertTo(clazz, entity);
                list.add(dto);
            }
        }
        return list;
    }

    public <T> List<T> convertFrom(Class<T> clazz, List dtos) {
        List<T> list = new ArrayList<T>();
        if (dtos != null) {
            for (Object dto : dtos) {
                T entity = convertFrom(clazz, dto);
                list.add(entity);
            }
        }
        return list;
    }

    public <T extends AbstractDTO> T convertTo(Class<T> clazz, Object entity) {
        try {
            return convertTo0(clazz, entity);
        } finally {
            cache.remove();
        }
    }

    public <T> T convertFrom(Class<T> clazz, Object dto) {
        try {
            return convertFrom0(clazz, dto);
        } finally {
            cache.remove();
        }
    }

    private <T extends AbstractDTO> T convertTo0(Class<?> clazz, Object entity) {
        if (entity == null)
            return null;

        if (entity instanceof HibernateProxy)
            entity = ((HibernateProxy) entity).getHibernateLazyInitializer().getImplementation();

        Object dto = null;
        try {
            dto = clazz.newInstance();
            List<FieldInfo> infos = fields.get(clazz);
            if (infos == null) {
                infos = getAllField(clazz, clazz, new ArrayList<FieldInfo>());
                fields.put(clazz, infos);
            }
            for (FieldInfo info : infos) {
                Class<?> type = info.getType();
                String fieldName = info.getField().getName();

                Object value = get(entity, info.name(), type);
                if (value != null) {
                    if (value instanceof AbstractModel
                            || value.getClass().isAnnotationPresent(XmlAccessorType.class)) {
                        if (contains(value)) {
                            Object convert = get(value);
                            set(dto, fieldName, convert);
                        } else {
                            add(value, null);
                            Object convert = convertTo0(type, value);
                            add(value, convert);
                            set(dto, fieldName, convert);
                        }
                    } else if (value instanceof Collection) {
                        Collection result = (Collection) value;
                        Class<?> arrayType = info.getGenericType();
                        if (arrayType == null && result.size() != 0) {
                            arrayType = result.iterator().next().getClass();
                            info.setGenericType(arrayType);
                        }

                        if (AbstractDTO.class.isAssignableFrom(arrayType)) {
                            try {
                                if (value instanceof PersistentCollection) {
                                    result = new ArrayList();
                                } else {
                                    result = (Collection) value.getClass().newInstance();
                                }
                            } catch (InstantiationException e) {
                                result = new ArrayList();
                            }

                            for (Object o : (Collection) value) {
                                Object convert = convertTo0(arrayType, o);
                                result.add(convert);
                            }
                        }
                        set(dto, fieldName, result);
                    } else {
                        set(dto, fieldName, value);
                    }
                }
            }
        } catch (Exception e) {
            logger.warn(e.getMessage());
        }
        return (T) dto;
    }

    private <T> T convertFrom0(Class<?> clazz, Object dto) {
        if (dto == null)
            return null;
        Object bean = null;
        try {
            bean = clazz.newInstance();
            List<FieldInfo> infos = fields.get(dto.getClass());
            if (infos == null) {
                infos = getAllField(dto.getClass(), dto.getClass(), new ArrayList<FieldInfo>());
                fields.put(dto.getClass(), infos);
            }
            for (FieldInfo info : infos) {
                if (info.readonly()) {
                    continue;
                }

                String name = info.name();
                Object entity = bean;
                clazz = entity.getClass();
                String[] lookup = name.split("\\.");

                if (lookup.length > 1) {
                    if (isNullValue(dto, info.getField().getName()))
                        continue;

                    for (int i = 0; i < lookup.length - 1; i++) {
                        Object instance = get(entity, lookup[i], null);
                        if (instance == null) {
                            FieldInfo fieldInfo = getField(clazz, lookup[i]);
                            Class<?> type = fieldInfo.getType();
                            if (!type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
                                instance = type.newInstance();
                                set(entity, lookup[i], instance);

                                entity = instance;
                                clazz = type;
                            }
                        } else {
                            entity = instance;
                            clazz = instance.getClass();
                        }
                    }
                    name = lookup[lookup.length - 1];
                }

                FieldInfo distField = getField(clazz, name);
                if (distField != null) {
                    Class<?> type = distField.getType();
                    Object value = get(dto, info.getField().getName(), type);
                    if (value != null) {
                        if (value instanceof AbstractDTO) {
                            if (contains(value)) {
                                Object convert = get(value);
                                set(entity, name, convert);
                            } else {
                                add(value, null);
                                Object convert = convertFrom0(type, value);
                                add(value, convert);
                                set(entity, name, convert);
                            }
                        } else if (value instanceof Collection) {
                            Collection result = (Collection) value;
                            Class<?> arrayType = distField.getGenericType();
                            if (AbstractModel.class.isAssignableFrom(arrayType)
                                    || arrayType.isAnnotationPresent(XmlAccessorType.class)) {
                                try {
                                    result = (Collection) value.getClass().newInstance();
                                } catch (InstantiationException e) {
                                    result = new ArrayList();
                                }

                                for (Object o : (Collection) value) {
                                    Object convert = convertFrom0(arrayType, o);
                                    result.add(convert);
                                }
                            }
                            set(entity, name, result);
                        } else {
                            set(entity, name, value);
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.warn(e.getMessage());
        }
        return (T) bean;
    }

    private FieldInfo getField(Class<?> clazz, String name) {
        List<FieldInfo> fieldInfos = fields.get(clazz);
        if (fieldInfos != null)
            for (FieldInfo info : fieldInfos) {
                if (info.getField().getName().equals(name))
                    return info;
            }

        Field field = ClassUtils.getField(clazz, name);
        FieldInfo info = null;
        if (field != null) {
            info = getField(clazz, field);
            if (fieldInfos == null) {
                fieldInfos = new ArrayList<FieldInfo>();
                fields.put(clazz, fieldInfos);
            }
            fieldInfos.add(info);
        }
        return info;
    }

    private FieldInfo getField(Class<?> clazz, Field field) {
        FieldInfo info = null;
        if (field != null) {
            info = new FieldInfo(field);
            if (field.getGenericType() instanceof TypeVariable) {
                TypeVariable genericType = (TypeVariable) field.getGenericType();
                TypeVariable<?>[] parameters = genericType.getGenericDeclaration().getTypeParameters();
                for (int i = 0; i < parameters.length; i++) {
                    if (parameters[i].getName().equals(genericType.getName())) {
                        Class parameterClass = getGenericParameterClass(clazz,
                                (Class) genericType.getGenericDeclaration(), i);
                        info.setType(parameterClass);
                        break;
                    }
                }
            }

            if (field.getGenericType() instanceof ParameterizedType) {
                ParameterizedType genericType = (ParameterizedType) field.getGenericType();

                Class<?> arrayType = null;
                if (genericType.getActualTypeArguments()[0] instanceof TypeVariable) {
                    TypeVariable genericType1 = (TypeVariable) genericType.getActualTypeArguments()[0];
                    TypeVariable<?>[] parameters = genericType1.getGenericDeclaration().getTypeParameters();
                    for (int i = 0; i < parameters.length; i++) {
                        if (parameters[i].getName().equals(genericType1.getName())) {
                            arrayType = getGenericParameterClass(clazz,
                                    (Class) genericType1.getGenericDeclaration(), i);
                            break;
                        }
                    }
                } else {
                    arrayType = (Class<?>) genericType.getActualTypeArguments()[0];
                }
                info.setGenericType(arrayType);
            }
        }
        return info;
    }

    private List<FieldInfo> getAllField(Class<?> src, Class<?> clazz, List<FieldInfo> fields) {
        if (clazz != Object.class) {
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field field : declaredFields) {
                Property property = field.getAnnotation(Property.class);
                if (property != null) {
                    fields.add(getField(src, field));
                }
            }
            getAllField(src, clazz.getSuperclass(), fields);
        }
        return fields;
    }

    private String fixPath(String name) {
        Matcher matcher = pattern.matcher(name);
        StringBuffer buffer = new StringBuffer(name.length());
        while (matcher.find())
            matcher.appendReplacement(buffer, StringUtils.capitalize(matcher.group()));
        matcher.appendTail(buffer);

        name = buffer.toString();
        if (name.startsWith("_"))
            name = name.substring(1);
        return name;
    }

    private Object get(Object bean, String name, Class valueType) {
        try {
            Object value;
            try {
                value = getInstance().getProperty(bean, name);
            } catch (NoSuchMethodException e) {
                value = getInstance().getProperty(bean, fixPath(name));
            } catch (Exception e) {
                value = null;
            }

            if (!Hibernate.isInitialized(value))
                value = null;

            if (value != null && valueType != null && value.getClass() != valueType) {
                XmlAdapter adapter = adapters.get(valueType.getName() + value.getClass().getName());
                if (adapter != null)
                    try {
                        return adapter.marshal(value);
                    } catch (ClassCastException e) {
                        return adapter.unmarshal(value);
                    }
            }

            return value;
        } catch (Exception e) {
            logger.warn(e.getMessage());
        }
        return null;
    }

    private void set(Object bean, String name, Object value) {
        if (value != null) {
            try {
                getInstance().setProperty(bean, name, value);
            } catch (NoSuchMethodException e) {
                try {
                    getInstance().setProperty(bean, fixPath(name), value);
                } catch (Exception ex) {
                    logger.warn(e.getMessage());
                }
            } catch (Exception e) {
                logger.warn(e.getMessage());
            }
        }
    }

    private boolean isNullValue(Object bean, String name) {
        return get(bean, name, null) == null;
    }

    public Class getGenericParameterClass(Class actualClass, Class genericClass, int parameterIndex) {
        if (!genericClass.isAssignableFrom(actualClass) || genericClass.equals(actualClass)) {
            throw new IllegalArgumentException(
                    "Class " + genericClass.getName() + " is not a superclass of " + actualClass.getName() + ".");
        }

        Stack<ParameterizedType> types = new Stack<ParameterizedType>();

        Class clazz = actualClass;

        while (true) {
            Type currentType = genericClass.isInterface() ? getGenericInterface(clazz, genericClass)
                    : clazz.getGenericSuperclass();

            Type rawType;
            if (currentType instanceof ParameterizedType) {
                ParameterizedType type = (ParameterizedType) currentType;
                types.push(type);
                rawType = type.getRawType();
            } else {
                types.clear();
                rawType = currentType;
            }

            if (!rawType.equals(genericClass)) {
                clazz = (Class) rawType;
            } else {
                break;
            }
        }

        if (types.isEmpty()) {
            return (Class) genericClass.getTypeParameters()[parameterIndex].getGenericDeclaration();
        }

        Type result = types.pop().getActualTypeArguments()[parameterIndex];

        while (result instanceof TypeVariable && !types.empty()) {
            int actualArgumentIndex = getParameterTypeDeclarationIndex((TypeVariable) result);
            ParameterizedType type = types.pop();
            result = type.getActualTypeArguments()[actualArgumentIndex];
        }

        if (result instanceof TypeVariable) {
            throw new IllegalStateException("Unable to resolve type variable " + result + "."
                    + " Try to replace instances of parametrized class with its non-parameterized subtype.");
        }

        if (result instanceof ParameterizedType) {
            result = ((ParameterizedType) result).getRawType();
        }

        if (result == null) {
            throw new IllegalStateException(
                    "Unable to determine actual parameter type for " + actualClass.getName() + ".");
        }

        if (!(result instanceof Class)) {
            throw new IllegalStateException(
                    "Actual parameter type for " + actualClass.getName() + " is not a Class.");
        }

        return (Class) result;
    }

    public int getParameterTypeDeclarationIndex(TypeVariable typeVariable) {
        GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
        TypeVariable[] typeVariables = genericDeclaration.getTypeParameters();
        for (int i = 0; i < typeVariables.length; i++) {
            if (typeVariables[i].equals(typeVariable)) {
                return i;
            }
        }
        throw new IllegalStateException(
                "Argument " + typeVariable.toString() + " is not found in " + genericDeclaration.toString() + ".");
    }

    public Type getGenericInterface(Class sourceClass, Class genericInterface) {
        Type[] types = sourceClass.getGenericInterfaces();
        for (Type type : types) {
            if (type instanceof Class) {
                if (genericInterface.isAssignableFrom((Class) type)) {
                    return type;
                }
            } else if (type instanceof ParameterizedType) {
                if (genericInterface.isAssignableFrom((Class) ((ParameterizedType) type).getRawType())) {
                    return type;
                }
            }
        }
        return null;
    }

    private void add(Object key, Object value) {
        cache.get().put(key, value);
    }

    private Object get(Object key) {
        return cache.get().get(key);
    }

    private boolean contains(Object key) {
        Map map = cache.get();
        if (map == null) {
            map = new HashMap();
            cache.set(map);
        }
        return map.containsKey(key);
    }
}