View Javadoc
1   package org.andromda.utils.beans.comparators;
2   
3   import java.io.InputStream;
4   import java.io.Serializable;
5   import java.lang.reflect.InvocationTargetException;
6   import java.net.URL;
7   import java.util.Comparator;
8   import java.util.Properties;
9   
10  import org.andromda.core.common.ClassUtils;
11  import org.andromda.core.common.ExceptionUtils;
12  import org.andromda.core.common.Introspector;
13  import org.andromda.utils.beans.BeanSorter;
14  import org.andromda.utils.beans.SortCriteria;
15  import org.apache.commons.lang.StringUtils;
16  
17  /**
18   * Used by BeanSorter to provide sorting capabilities for
19   * beans.  Currently supports sorting by all simple properties
20   *
21   * @see BeanSorter
22   *
23   * @author Chad Brandon
24   */
25  public class BeanComparator
26      implements Comparator,
27          Serializable
28  {
29      private static final long serialVersionUID = 34L;
30  
31      /**
32       * Stores the comparator mappings.
33       */
34      private static final Properties comparators = new Properties();
35  
36      static
37      {
38          InputStream stream = null;
39          try
40          {
41              final String comparatorsFile = "/Comparators.properties";
42              final URL comparatorsUri = BeanComparator.class.getResource(comparatorsFile);
43              if (comparatorsUri == null)
44              {
45                  throw new BeanComparatorException("The comparators resource '" + comparatorsFile +
46                      " could not be loaded");
47              }
48              stream = comparatorsUri.openStream();
49              comparators.load(stream);
50          }
51          catch (final Throwable throwable)
52          {
53              throw new RuntimeException(throwable);
54          }
55          finally
56          {
57              if (stream != null) try {stream.close();} catch (Exception ex) {}
58              stream = null;
59          }
60      }
61  
62      private Comparator comparator = null;
63      private SortCriteria sortCriteria;
64  
65      /**
66       * @param sortCriteria
67       */
68      public BeanComparator(SortCriteria sortCriteria)
69      {
70          ExceptionUtils.checkNull(
71              "sortCriteria",
72              sortCriteria);
73          this.sortCriteria = sortCriteria;
74      }
75  
76      /**
77       * @see java.util.Comparator#compare(Object, Object)
78       */
79      @Override
80      public int compare(
81          final Object objectA,
82          Object objectB)
83      {
84          ExceptionUtils.checkNull(
85              "objectA",
86              objectA);
87          ExceptionUtils.checkNull(
88              "objectB",
89              objectB);
90  
91          try
92          {
93              Object aValue;
94              Object bValue;
95              if (this.sortCriteria.getOrdering().equals(SortCriteria.Ordering.DESCENDING))
96              {
97                  // - since its descending switch the objects we are getting the values from,
98                  //   so the order will be reversed
99                  aValue = getProperty(
100                         objectB,
101                         sortCriteria.getSortBy());
102                 bValue = getProperty(
103                         objectA,
104                         sortCriteria.getSortBy());
105             }
106             else
107             {
108                 // - otherwise we assume its ascending
109                 aValue = getProperty(
110                         objectA,
111                         sortCriteria.getSortBy());
112                 bValue = getProperty(
113                         objectB,
114                         sortCriteria.getSortBy());
115             }
116 
117             //we default result to zero (zero result would mean aValue and bValue equal each other)
118             int result = 0;
119 
120             //first sort for null values, null values will always come last
121             if (aValue != null || bValue != null)
122             {
123                 if (aValue == null)
124                 {
125                     if (sortCriteria.isNullsFirst())
126                     {
127                         result = -1;
128                     }
129                     else
130                     {
131                         result = 1;
132                     }
133                 }
134                 else if (bValue == null)
135                 {
136                     if (sortCriteria.isNullsFirst())
137                     {
138                         result = 1;
139                     }
140                     else
141                     {
142                         result = -1;
143                     }
144                 }
145                 else
146                 {
147                     result = this.getComparator(aValue.getClass()).compare(
148                         aValue,
149                         bValue);
150                 }
151             }
152             return result;
153         }
154         catch (final Throwable throwable)
155         {
156             throw new ComparatorException(throwable);
157         }
158     }
159 
160     /**
161      * Gets the property specified by propertyName from the bean.
162      * Checks each nested parent to see if its null and if it is
163      * returns null.
164      * @param bean
165      * @param propertyName
166      * @return
167      */
168     private Object getProperty(
169         final Object bean,
170         String propertyName)
171         throws IllegalAccessException, InvocationTargetException, NoSuchMethodException
172     {
173         Object value = null;
174         if (bean != null && StringUtils.isNotBlank(propertyName))
175         {
176             int index = getNestedIndex(propertyName);
177             if (index != -1)
178             {
179                 String simpleProp = propertyName.substring(
180                         0,
181                         index);
182                 value = Introspector.instance().getProperty(
183                         bean,
184                         simpleProp);
185                 if (value != null)
186                 {
187                     if (getNestedIndex(propertyName) != -1)
188                     {
189                         propertyName = propertyName.substring(
190                                 index + 1,
191                                 propertyName.length());
192                         value = getProperty(
193                                 value,
194                                 propertyName);
195                     }
196                 }
197             }
198             else
199             {
200                 value = Introspector.instance().getProperty(
201                         bean,
202                         propertyName);
203             }
204         }
205         return value;
206     }
207 
208     /**
209      * Gets the index of the given <code>propertyName</code> if
210      * its a nested property (nested meaning names separated by
211      * a '.').
212      * @param propertyName the name of the nested property.
213      * @return the index.
214      */
215     private final int getNestedIndex(final String propertyName)
216     {
217         int index = -1;
218         if (StringUtils.isNotBlank(propertyName))
219         {
220             index = propertyName.indexOf('.');
221         }
222         return index;
223     }
224 
225     /**
226      * Retrieves the associated Comparator for the type
227      * @param type the class of which to retrieve the comparator.
228      * @return appropriate comparator or null if one wasn't defined.
229      */
230     private final Comparator getComparator(final Class type)
231     {
232         try
233         {
234             if (this.comparator == null)
235             {
236                 final String comparatorName = findComparatorName(type);
237                 if (StringUtils.isNotBlank(comparatorName))
238                 {
239                     this.comparator = (Comparator)ClassUtils.loadClass(comparatorName).newInstance();
240                 }
241                 else
242                 {
243                     throw new ComparatorException("No comparator defined for the given type '" + type.getName() + '\'');
244                 }
245             }
246             return this.comparator;
247         }
248         catch (final Throwable throwable)
249         {
250             throw new ComparatorException(throwable);
251         }
252     }
253 
254     private String findComparatorName(final Class type)
255     {
256         String comparatorName = comparators.getProperty(type.getName());
257         if (StringUtils.isEmpty(comparatorName) && type.getSuperclass() != null)
258         {
259             comparatorName = findComparatorName(type.getSuperclass());
260         }
261         return comparatorName;
262     }
263 
264     /**
265      * Returns the current sortCriteria value
266      * @return String
267      */
268     public SortCriteria getSortCriteria()
269     {
270         return this.sortCriteria;
271     }
272 
273     /**
274      * Sets the new sortCriteria value
275      * @param sortCriteria
276      */
277     public void setSortCriteria(final SortCriteria sortCriteria)
278     {
279         ExceptionUtils.checkNull(
280             "sortCriteria",
281             sortCriteria);
282         this.sortCriteria = sortCriteria;
283     }
284 }