001package org.andromda.utils.beans;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.Collections;
006import java.util.List;
007
008import org.andromda.core.common.ExceptionUtils;
009import org.andromda.utils.beans.SortCriteria.Ordering;
010import org.andromda.utils.beans.comparators.BeanComparator;
011import org.apache.commons.collections.comparators.ComparatorChain;
012
013/**
014 * Provides bean sorting capabilities.
015 *
016 * <p>
017 * Sorts passed in Collections and returns
018 * sorted Lists.  Performs sorting for any
019 * Class that has an associated Comparator implementation defined.  If
020 * the Collection that is passed in, is not an instance of List, it will create
021 * a new ArrayList with the contents of the passed in Collection, before
022 * sorting occurs (since sorting can only be done on a java.util.List).
023 * </p>
024 *
025 * @author Chad Brandon
026 */
027public class BeanSorter
028{
029    /**
030     * Performs sorting of the collection by one property to
031     * sort with more than one property see {@link #sort(java.util.Collection, SortCriteria[])}.
032     *
033     * @see #sort(java.util.Collection, SortCriteria[])
034     *
035     * @param <T> Collection type
036     * @param beans the Collection of PersistentObjects to sort
037     * @param sortBy the property to sort by (i.e. firstName, etc). Can
038     *        be a nested property such as 'person.address.street'.
039     * @param ordering the ordering of the sorting (either {@link Ordering#ASCENDING}
040     *        or {@link Ordering#DESCENDING})
041     * @return the sorted List
042     */
043    public static <T> List<T> sort(
044        final Collection<T> beans,
045        final String sortBy,
046        final Ordering ordering)
047    {
048        return sort(
049            beans,
050            new SortCriteria[] {new SortCriteria(
051                    sortBy,
052                    ordering)});
053    }
054
055    /**
056     * Performs sorting of the collection by one property to
057     * sort with more than one property see {@link #sort(java.util.Collection, SortCriteria[])}.
058     *
059     * @see #sort(java.util.Collection, SortCriteria[])
060     *
061     * @param <T> Collection type
062     * @param beans the Collection of PersistentObjects to sort
063     * @param sortBy the property to sort by (i.e. firstName, etc). Can
064     *        be a nested property such as 'person.address.street'.
065     * @param nullsFirst a flag indicating whether or not null values should be sorted to the beginning
066     *        or the ending of the list.
067     * @return the sorted List
068     */
069    public static <T> List<T> sort(
070        final Collection<T> beans,
071        final String sortBy,
072        final boolean nullsFirst)
073    {
074        return sort(
075            beans,
076            new SortCriteria[] {new SortCriteria(
077                    sortBy,
078                    nullsFirst)});
079    }
080
081    /**
082     * Performs sorting of the collection by one property to
083     * sort with more than one property see {@link #sort(java.util.Collection, SortCriteria[])}.
084     *
085     * @see #sort(java.util.Collection, SortCriteria[])
086     *
087     * @param <T> Collection type
088     * @param beans the Collection of PersistentObjects to sort
089     * @param sortBy the property to sort by (i.e. firstName, etc). Can
090     *        be a nested property such as 'person.address.street'.
091     * @param ordering the ordering of the sorting (either {@link Ordering#ASCENDING}
092     *        or {@link Ordering#DESCENDING})
093     * @param nullsFirst a flag indicating whether or not null values should be sorted to the beginning
094     *        or the ending of the list.
095     * @return the sorted List
096     */
097    public static <T> List<T> sort(
098        final Collection<T> beans,
099        final String sortBy,
100        final Ordering ordering,
101        final boolean nullsFirst)
102    {
103        return sort(
104            beans,
105            new SortCriteria[] {new SortCriteria(
106                    sortBy,
107                    ordering,
108                    nullsFirst)});
109    }
110
111    /**
112     * <p>
113     * Sorts the passed in Collection and returns
114     * a sorted List.  Performs SQL like sorting for any
115     * Class that has an associated Comparator implementation defined.  If
116     * the Collection that is passed in, is not an instance of List, it will create
117     * a new ArrayList with the contents of the passed in Collection, before
118     * sorting occurs.  Since sorting can only be done on a java.util.List.  If
119     * it is a list, then in-line sorting will occur of the list.
120     * </p>
121     *
122     * @param <T> Collection type
123     * @param beans the Collection of PersistentObjects to sort
124     * @param sortBy an array of SortCriteria.  This array of SortCriteria
125     * specifies which attributes to sort by.
126     * Attributes to sort by, MUST be simple attributes
127     * (i.e. name, type, etc, they can not be complex objects, but the properties can be
128     * nested simple types within associated beans).
129     *
130     * @return List the sorted List
131     */
132    public static <T> List<T> sort(
133        final Collection<T> beans,
134        final SortCriteria[] sortBy)
135    {
136        ExceptionUtils.checkNull(
137            "beans",
138            beans);
139        ExceptionUtils.checkNull(
140            "sortBy",
141            sortBy);
142
143        if (sortBy.length == 0)
144        {
145            throw new IllegalArgumentException("sortBy must contain at least one value by which to sort");
146        }
147
148        List<T> sorted = null;
149
150        // IMPORTANT: do not replace this logic with new list creation, this allows for sorting of the passed
151        // in collection of beans (i.e. it allows this method to do "in-line" sorting), with the caller not  having
152        // to get the result of this method to get the sorted result, only if the collection is not a List instance
153        // will it need to use the result of this method to get the sorted collection.
154        if (!(beans instanceof List))
155        {
156            sorted = new ArrayList<T>(beans);
157        }
158        else
159        {
160            sorted = (List)beans;
161        }
162
163        int sortByNum = sortBy.length;
164
165        // - use the Comparator chain to provide SQL like sorting of properties
166        final ComparatorChain chain = new ComparatorChain();
167        for (int ctr = 0; ctr < sortByNum; ctr++)
168        {
169            final SortCriteria orderBy = sortBy[ctr];
170            chain.addComparator(new BeanComparator(orderBy));
171        }
172
173        Collections.sort(
174            sorted,
175            chain);
176
177        return sorted;
178    }
179}