my code stock.com

napoleonit76

Grails Deep Custom Transformer
by napoleonit76

A custom transformer class to use in Grails Criteria Builder which has projections on association properties. This will help you transform data to the root entity and even association objects, then gather them to collection inside the root one.

Snippet options

Download: Download snippet as grails-deep-custom-transformer.java.
Copy snippet: For this you need a free my code stock.com account.
Embed code : You will find the embed code for this snippet at the end of the page, if you want to embed it into a website or a blog!

package util;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.PredicateUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.reflect.FieldUtils;
import org.hibernate.HibernateException;
import org.hibernate.transform.ResultTransformer;

/**
 * 
 * A util that helps transforming a projected entity deeply back to its own class of definition. This aims to reduce the
 * time needed for writting another DTO for re-mapping the result.
 * 
 * @author chau.dinh
 * 
 */
public class DeepAliasToBeanResultTransformer implements ResultTransformer {

    private static final long serialVersionUID = 1L;

    private Set<String> associationPaths = new HashSet<String>();

    private boolean distinctRoot;

    @SuppressWarnings("rawtypes")
    private final Class resultClass;

    /**
     * Constructor.
     * 
     * @param resultClass
     *            Class to be mapped back for each returned tuple.
     * @param associationPaths
     *            Association paths used to query data, will be used in [email protected] #transformList(List)}.
     * @param distinctRoot
     *            Specifies whether a distinction on root entity should be done on the result list.
     */
    @SuppressWarnings({ "rawtypes" })
    public DeepAliasToBeanResultTransformer(Class resultClass, Set<String> associationPaths, boolean distinctRoot) {

        if (resultClass == null) {
            throw new IllegalArgumentException("resultClass cannot be null");
        }
        this.resultClass = resultClass;
        this.distinctRoot = distinctRoot;

        this.associationPaths = associationPaths;
        Set<String> toBeRemovedPaths = new HashSet<String>();
        for (String path : this.associationPaths) {
            for (String anotherPath : this.associationPaths) {
                if (!path.equals(anotherPath) && StringUtils.startsWith(path, anotherPath)) {
                    toBeRemovedPaths.add(anotherPath);
                }
            }
        }
        this.associationPaths.removeAll(toBeRemovedPaths);
    }

    /**
     * [email protected]}
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Object transformTuple(Object[] tuple, String[] aliases) {
        Object result;

        try {
            result = resultClass.newInstance();
            if (aliases == null || aliases.length == 0 || aliases[0] == null) {
                aliases = this.associationPaths.toArray(new String[this.associationPaths.size()]);
            }

            for (int i = 0; i < aliases.length; i++) {
                // No needs to initializes any object on the path if the return tuple item is NULL.
                if (tuple[i] == null) {
                    continue;
                }

                String alias = aliases[i];
                if (alias != null) {
                    if (alias.indexOf(".") == -1) {
                        Field field = FieldUtils.getField(resultClass, alias, true);
                        field.set(result, tuple[i]);
                    } else {
                        String fullPath = alias.substring(0, StringUtils.lastIndexOf(alias, "."));
                        String prop = alias.substring(StringUtils.lastIndexOf(alias, ".") + 1);

                        String[] paths = StringUtils.split(fullPath, '.');

                        Class lastClazz = resultClass;
                        Object lastObj = result;
                        for (String path : paths) {
                            Field field = FieldUtils.getField(lastClazz, path, true);
                            lastClazz = field.getType();

                            if (field.getType().isAssignableFrom(Set.class)
                                    || field.getType().isAssignableFrom(SortedSet.class)
                                    || field.getType().isAssignableFrom(List.class)) {
                                Collection collection = (Collection) field.get(lastObj);
                                if (collection.isEmpty()) {
                                    collection.add(newCollectionItem(field));
                                }

                                lastClazz = (Class) ((ParameterizedType) field.getGenericType())
                                        .getActualTypeArguments()[0];
                                lastObj = collection.iterator().next();
                            } else {
                                if (field.get(lastObj) == null) {
                                    field.set(lastObj, lastClazz.newInstance());
                                }
                                lastObj = field.get(lastObj);
                            }
                        }

                        if (lastClazz != null) {
                            FieldUtils.getField(lastClazz, prop, true).set(lastObj, tuple[i]);
                        }
                    }
                }
            }
        } catch (InstantiationException e) {
            throw new HibernateException("Could not instantiate resultclass: " + resultClass.getName() + ". Detail: "
                    + e.getMessage());
        } catch (IllegalAccessException e) {
            throw new HibernateException("Could not instantiate resultclass: " + resultClass.getName() + ". Detail: "
                    + e.getMessage());
        }

        return result;
    }

    @SuppressWarnings("rawtypes")
    private Object newCollectionItem(Field f) throws InstantiationException, IllegalAccessException {
        return ((Class) ((ParameterizedType) f.getGenericType()).getActualTypeArguments()[0]).newInstance();
    }

    /**
     * [email protected]}
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public List transformList(List list) {
        if (!distinctRoot) {
            return list;
        }
        List distinctResult = new ArrayList();
        for (Object newEntity : list) {
            if (!distinctResult.contains(newEntity)) {
                distinctResult.add(newEntity);
            } else {
                Object existingEntity = distinctResult.get(distinctResult.indexOf(newEntity));
                for (String associationPath : associationPaths) {
                    String[] parts = StringUtils.split(associationPath, ".");
                    merge(existingEntity, newEntity, parts);
                }
            }
        }
        return distinctResult;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private void merge(Object existingEntity, Object newEntity, String[] parts) {
        Object lastExistingObj = existingEntity;
        Object lastNewObj = newEntity;

        if (lastExistingObj instanceof Collection) {
            Collection lastExistingCollection = (Collection) lastExistingObj;
            Collection lastNewCollection = (Collection) lastNewObj;
            lastExistingCollection.addAll(lastNewCollection);
        }

        if (parts.length > 0 && lastExistingObj != null && lastNewObj != null && lastExistingObj.equals(lastNewObj)) {
            try {
                String part = parts[0];
                lastExistingObj = PropertyUtils.getProperty(lastExistingObj, part);
                lastNewObj = PropertyUtils.getProperty(lastNewObj, part);
                if (lastExistingObj != null && lastNewObj != null) {
                    if (!(lastExistingObj instanceof Collection)) {
                        if (lastExistingObj.equals(lastNewObj)) {
                            merge(lastExistingObj, lastNewObj, (String[]) ArrayUtils.remove(parts, 0));
                        }
                    } else {
                        Collection lastExistingCollection = (Collection) lastExistingObj;
                        Collection lastNewCollection = (Collection) lastNewObj;

                        if (lastExistingCollection instanceof SortedSet) {
                            lastExistingCollection = new TreeSet(lastExistingCollection);
                        } else {
                            lastExistingCollection = new HashSet(lastExistingCollection);
                        }

                        lastExistingCollection.addAll(lastNewCollection);
                        PropertyUtils.setProperty(existingEntity, part, lastExistingCollection);

                        for (Object lastExistingItem : lastExistingCollection) {
                            Object lastNewItem = CollectionUtils.find(lastNewCollection, new Predicate() {
                                @Override
                                public boolean evaluate(Object rhs) {
                                    return PredicateUtils.equalPredicate(rhs).evaluate(rhs);
                                }
                            });
                            if (lastNewItem != null) {
                                merge(lastExistingItem, lastNewItem, (String[]) ArrayUtils.remove(parts, 0));
                            }
                        }
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException("Error during transforming list in Hibernate", e);
            }
        }
    }
}

Create a free my code stock.com account now.

my code stok.com is a free service, which allows you to save and manage code snippes of any kind and programming language. We provide many advantages for your daily work with code-snippets, also for your teamwork. Give it a try!

Find out more and register now

You can customize the height of iFrame-Codes as needed! You can find more infos in our API Reference for iframe Embeds.