View Javadoc
1   /*
2    * Copyright (c) 2002-2022, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.util.sort;
35  
36  import fr.paris.lutece.portal.service.util.AppLogService;
37  
38  import java.io.Serializable;
39  
40  import java.lang.reflect.InvocationTargetException;
41  import java.lang.reflect.Method;
42  
43  import java.util.Comparator;
44  import java.util.Locale;
45  
46  /**
47   * This class provide Attribute Comparator
48   */
49  public class AttributeComparator implements Comparator<Object>, Serializable
50  {
51      private static final long serialVersionUID = 8552197766086300259L;
52      private String _strSortedAttribute;
53      private boolean _bIsASC;
54  
55      /**
56       * Constructor
57       * 
58       * @param strSortedAttribute
59       *            the name of the attribute on which the sort will be made
60       * @param bIsASC
61       *            true for the ASC order, false for the DESC order
62       */
63      public AttributeComparator( String strSortedAttribute, boolean bIsASC )
64      {
65          _strSortedAttribute = strSortedAttribute;
66          _bIsASC = bIsASC;
67      }
68  
69      /**
70       * Constructor
71       * 
72       * @param strSortedAttribute
73       *            the name of the attribute on which the sort will be made
74       */
75      public AttributeComparator( String strSortedAttribute )
76      {
77          _strSortedAttribute = strSortedAttribute;
78          _bIsASC = true;
79      }
80  
81      /**
82       * Compare two objects o1 and o2.
83       * 
84       * @param o1
85       *            Object
86       * @param o2
87       *            Object
88       * @return &lt; 0 if o1 is before o2 in the alphabetical order 0 if o1 equals o2 &gt; 0 if o1 is after o2
89       */
90      @Override
91      public int compare( Object o1, Object o2 )
92      {
93          int nStatus = 0;
94  
95          Method method1 = getMethod( o1 );
96          Method method2 = getMethod( o2 );
97  
98          if ( method1 == null || method2 == null || method1.getReturnType( ) != method2.getReturnType( ) )
99          {
100             return 0;
101         }
102 
103         try
104         {
105             nStatus = compareReturnTypes( o1, o2, method1, method2 );
106         }
107         catch( IllegalArgumentException | IllegalAccessException | InvocationTargetException e )
108         {
109             AppLogService.error( e.getMessage( ), e );
110         }
111 
112         if ( !_bIsASC )
113         {
114             nStatus = nStatus * ( -1 );
115         }
116 
117         return nStatus;
118     }
119 
120     private int compareReturnTypes( Object o1, Object o2, Method method1, Method method2 ) throws IllegalAccessException, InvocationTargetException
121     {
122         int nStatus = 0;
123         Object oRet1 = method1.invoke( o1 );
124         Object oRet2 = method2.invoke( o2 );
125 
126         String strReturnType = method1.getReturnType( ).getName( );
127         Class<?> returnType = method1.getReturnType( );
128 
129         if ( oRet1 == null )
130         {
131             if ( oRet2 == null )
132             {
133                 nStatus = 0;
134             }
135             else
136             {
137                 nStatus = -1;
138             }
139         }
140         else
141         {
142             if ( oRet2 == null )
143             {
144                 nStatus = 1;
145             }
146             else
147             {
148                 if ( strReturnType.equals( "java.lang.String" ) )
149                 {
150                     nStatus = ( (String) oRet1 ).toLowerCase( Locale.ENGLISH ).compareTo( ( (String) oRet2 ).toLowerCase( Locale.ENGLISH ) );
151                 }
152                 else
153                     if ( returnType.isPrimitive( ) || isComparable( returnType ) )
154                     {
155                         nStatus = ( (Comparable) oRet1 ).compareTo( (Comparable) oRet2 );
156                     }
157                     else
158                         if ( returnType.isEnum( ) )
159                         {
160                             nStatus = oRet1.toString( ).compareTo( oRet2.toString( ) );
161                         }
162             }
163         }
164 
165         return nStatus;
166     }
167 
168     /**
169      * Return the getter method of the object obj for the attribute _strSortedAttribute
170      * 
171      * @param obj
172      *            the object
173      * @return method Method of the object obj for the attribute _strSortedAttribute
174      */
175     private Method getMethod( Object obj )
176     {
177         Method method = null;
178         String strFirstLetter = _strSortedAttribute.substring( 0, 1 ).toUpperCase( );
179 
180         String strMethodName = "get" + strFirstLetter + _strSortedAttribute.substring( 1, _strSortedAttribute.length( ) );
181 
182         try
183         {
184             method = obj.getClass( ).getMethod( strMethodName );
185         }
186         catch( Exception e )
187         {
188             AppLogService.error( e.getMessage( ), e );
189         }
190 
191         return method;
192     }
193 
194     /**
195      * Returns <code>true</code> if the class implements {@link Comparable} or extends a super class that implements {@link Comparable}, <code>false</code>
196      * otherwise.
197      * 
198      * @param clazz
199      *            the class
200      * @return <code>true</code> if the class implements {@link Comparable}, <code>false</code> otherwise.
201      */
202     private boolean isComparable( Class<?> clazz )
203     {
204         for ( Class<?> interfac : clazz.getInterfaces( ) )
205         {
206             if ( interfac.equals( Comparable.class ) )
207             {
208                 return true;
209             }
210         }
211 
212         // The class might be extending a super class that implements {@link Comparable}
213         Class<?> superClass = clazz.getSuperclass( );
214 
215         if ( superClass != null )
216         {
217             return isComparable( superClass );
218         }
219 
220         return false;
221     }
222 }