py.una.pol.karaku.services.ReflectionConverter.java Source code

Java tutorial

Introduction

Here is the source code for py.una.pol.karaku.services.ReflectionConverter.java

Source

/*-
 * Copyright (c)
 *
 *       2012-2014, Facultad Politcnica, Universidad Nacional de Asuncin.
 *       2012-2014, Facultad de Ciencias Mdicas, Universidad Nacional de Asuncin.
 *       2012-2013, Centro Nacional de Computacin, Universidad Nacional de Asuncin.
 *
 * 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; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 */
package py.una.pol.karaku.services;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
import py.una.pol.karaku.exception.KarakuRuntimeException;
import py.una.pol.karaku.replication.DTO;
import py.una.pol.karaku.replication.EntityNotFoundException;
import py.una.pol.karaku.replication.Shareable;

/**
 * {@link Converter} que utiliza reflexin.
 * 
 * <p>
 * Este converter es el converter por defecto de cualquier entidad, y su uso no
 * es muy recomendado ya que no implementa una lgica compleja y absolutamente
 * todo es compartido.
 * </p>
 * 
 * @author Arturo Volpe
 * @since 2.2.8
 * @version 1.0 Nov 12, 2013
 * 
 */
public abstract class ReflectionConverter<E extends Shareable, T extends DTO> implements Converter<E, T> {

    /**
    * 
    */
    private static final String RAWTYPES = "rawtypes";
    /**
    * 
    */
    private static final String UNCHECKED = "unchecked";
    private final Class<T> dtoClass;
    private final Class<E> entityClass;

    public ReflectionConverter(Class<T> dtoClass, Class<E> entityClass) {

        this.dtoClass = dtoClass;
        this.entityClass = entityClass;
    }

    @Override
    public Class<T> getDtoType() {

        return dtoClass;
    }

    @Override
    public Class<E> getEntityType() {

        return entityClass;
    }

    @Override
    @SuppressWarnings(UNCHECKED)
    public T toDTO(E entity, int depth) {

        return (T) map(entity, getDtoType(), depth, false);
    }

    @Override
    @SuppressWarnings(UNCHECKED)
    public E toEntity(T dto) {

        return (E) map(dto, getEntityType(), Integer.MAX_VALUE, true);
    }

    /**
     * 
     * @param source
     * @param targetClass
     * @param depth
     * @param dtoToEntity
     * @return
     */
    protected Object map(final Object source, final Class<?> targetClass, final int depth,
            final boolean dtoToEntity) {

        if (source == null) {
            return null;
        }
        try {
            final Object toRet = targetClass.newInstance();
            ReflectionUtils.doWithFields(source.getClass(), new FieldCallback() {

                @Override
                public void doWith(Field field) throws IllegalAccessException {

                    mapField(source, targetClass, depth, dtoToEntity, toRet, field);
                }

            }, ReflectionUtils.COPYABLE_FIELDS);
            return toRet;
        } catch (Exception e) {
            throw new KarakuRuntimeException(
                    String.format("Can't copy from %s to %s", source.getClass(), targetClass), e);
        }
    }

    /**
     * @param source
     * @param targetClass
     * @param depth
     * @param dtoToEntity
     * @param bean
     * @param field
     * @throws IllegalAccessException
     */
    private void mapField(final Object source, final Class<?> targetClass, final int depth,
            final boolean dtoToEntity, final Object bean, Field field) throws IllegalAccessException {

        Field toSet = ReflectionUtils.findField(targetClass, field.getName());
        if (toSet == null) {
            return;
        }
        field.setAccessible(true);
        toSet.setAccessible(true);
        Class<?> s = field.getType();
        Class<?> t = toSet.getType();

        if (Collection.class.isAssignableFrom(s) && Collection.class.isAssignableFrom(t)) {
            mapCollection(source, depth, dtoToEntity, bean, field, toSet, t);
        } else if (s.isAssignableFrom(t)) {
            Object o = field.get(source);
            toSet.set(bean, o);
        } else if (checkMappable(s, t)) {
            Object o = convert(s, t, field.get(source), depth - 1, dtoToEntity);
            toSet.set(bean, o);
        } else {
            throw new KarakuRuntimeException(String.format("Cant copy from field %s to field %s", field, toSet));
        }
    }

    /**
     * @param source
     * @param target
     * @return
     */
    private boolean checkMappable(Class<?> source, Class<?> target) {

        return (Shareable.class.isAssignableFrom(source) && DTO.class.isAssignableFrom(target))
                || (Shareable.class.isAssignableFrom(target) && DTO.class.isAssignableFrom(source));
    }

    /**
     * @param source
     * @param depth
     * @param dtoToEntity
     * @param bean
     * @param field
     * @param toSet
     * @param targetClass
     * @throws IllegalAccessException
     */
    @SuppressWarnings({ RAWTYPES, UNCHECKED })
    private void mapCollection(final Object source, final int depth, final boolean dtoToEntity, final Object bean,
            Field field, Field toSet, Class<?> targetClass) throws IllegalAccessException {

        if (depth > 0) {
            Collection colTarget = getNewCollection(targetClass);
            Collection colSource = (Collection) field.get(source);
            if (colSource == null) {
                return;
            }
            Class targetRowType = GenericCollectionTypeResolver.getCollectionFieldType(toSet);
            for (Object row : colSource) {
                if (row instanceof Shareable) {
                    colTarget.add(convert(row.getClass(), targetRowType, row, depth - 1, dtoToEntity));
                } else {
                    colTarget.add(map(row, targetRowType, depth - 1, dtoToEntity));
                }
            }
            toSet.set(bean, colTarget);
        }
    }

    @SuppressWarnings({ UNCHECKED, RAWTYPES })
    private Object convert(Class fromClass, Class toClass, Object object, int depth, boolean dtoToEntity) {

        if (dtoToEntity) {
            try {
                return getConverter(toClass, fromClass).toEntity((DTO) object);
            } catch (EntityNotFoundException enf) {
                // TODO ver la forma de propagar
                throw new KarakuRuntimeException("Can't convert", enf);
            }
        }
        return getConverter(fromClass, toClass).toDTO((Shareable) object, depth);
    }

    @SuppressWarnings(RAWTYPES)
    private Collection getNewCollection(Class collectionClass) {

        if (List.class.isAssignableFrom(collectionClass)) {
            return new ArrayList();
        }
        if (Set.class.isAssignableFrom(collectionClass)) {
            return new HashSet();
        }
        throw new KarakuRuntimeException("Unsupported type of collection " + collectionClass);
    }

    public abstract <Y extends Shareable, O extends DTO> Converter<Y, O> getConverter(Class<Y> entity,
            Class<O> dto);

    @Override
    public T beforeToDTO(E entity, int depth) {

        // TODO Auto-generated method stub
        return toDTO(entity, depth);
    }

}