View Javadoc
1   /*
2    * Copyright (c) 2002-2021, 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.plugins.ctv.util.sort;
35  
36  import java.io.Serializable;
37  import java.lang.reflect.InvocationTargetException;
38  import java.lang.reflect.Method;
39  import java.util.Comparator;
40  
41  import org.apache.commons.lang3.StringUtils;
42  import org.apache.commons.lang3.tuple.Pair;
43  
44  import fr.paris.lutece.portal.service.util.AppLogService;
45  
46  public class CustomAttributeComparator implements Comparator<Object>, Serializable
47  {
48  
49      private static final long serialVersionUID = -3526872977069103365L;
50      private String sortedAttribute;
51      private boolean isASC;
52  
53      /**
54       * Constructor
55       *
56       * @param strSortedAttribute
57       *            the name of the attribute on which the sort will be made
58       * @param bIsASC
59       *            true for the ASC order, false for the DESC order
60       */
61      public CustomAttributeComparator( String strSortedAttribute, boolean bIsASC )
62      {
63          sortedAttribute = strSortedAttribute;
64          isASC = bIsASC;
65      }
66  
67      /**
68       * Constructor
69       *
70       * @param strSortedAttribute
71       *            the name of the attribute on which the sort will be made
72       */
73      public CustomAttributeComparator( String strSortedAttribute )
74      {
75          sortedAttribute = strSortedAttribute;
76          isASC = true;
77      }
78  
79      /**
80       * Compare two objects o1 and o2.
81       *
82       * @param o1
83       *            Object
84       * @param o2
85       *            Object
86       * @return < 0 if o1 is before o2 in the alphabetical order 0 if o1 equals o2 > 0 if o1 is after o2
87       */
88      @Override
89      public int compare( Object o1, Object o2 )
90      {
91          int nStatus = 0;
92  
93          Pair<Object, Method> pair1 = getMethod( o1, sortedAttribute );
94          Pair<Object, Method> pair2 = getMethod( o2, sortedAttribute );
95  
96          if ( pair1.getRight( ) != null && pair2.getRight( ) != null && pair1.getRight( ).getReturnType( ) == pair2.getRight( ).getReturnType( ) )
97          {
98              try
99              {
100                 Object oRet1 = pair1.getRight( ).invoke( pair1.getLeft( ) );
101                 Object oRet2 = pair2.getRight( ).invoke( pair2.getLeft( ) );
102 
103                 String strReturnType = pair1.getRight( ).getReturnType( ).getName( );
104                 Class<?> returnType = pair1.getRight( ).getReturnType( );
105 
106                 if ( oRet1 == null )
107                 {
108                     if ( oRet2 == null )
109                     {
110                         nStatus = 0;
111                     }
112                     else
113                     {
114                         nStatus = -1;
115                     }
116                 }
117                 else
118                 {
119                     if ( oRet2 == null )
120                     {
121                         nStatus = 1;
122                     }
123                     else
124                     {
125                         if ( strReturnType.equals( "java.lang.String" ) )
126                         {
127                             nStatus = ( (String) oRet1 ).toLowerCase( ).compareTo( ( (String) oRet2 ).toLowerCase( ) );
128                         }
129                         else
130                             if ( returnType.isPrimitive( ) || isComparable( returnType ) )
131                             {
132                                 nStatus = ( (Comparable) oRet1 ).compareTo( (Comparable) oRet2 );
133                             }
134                             else
135                                 if ( returnType.isEnum( ) )
136                                 {
137                                     nStatus = oRet1.toString( ).compareTo( oRet2.toString( ) );
138                                 }
139                     }
140                 }
141             }
142             catch( IllegalArgumentException | IllegalAccessException | InvocationTargetException e )
143             {
144                 AppLogService.error( e );
145             }
146         }
147 
148         if ( !isASC )
149         {
150             nStatus = nStatus * -1;
151         }
152 
153         return nStatus;
154     }
155 
156     /**
157      * Return the getter method of the object obj for the attribute _strSortedAttribute
158      *
159      * @param obj
160      *            the object
161      * @return method Method of the object obj for the attribute _strSortedAttribute
162      */
163     private Pair<Object, Method> getMethod( Object obj, String sortedAttribute )
164     {
165         Method method = null;
166         String strFirstLetter = sortedAttribute.substring( 0, 1 ).toUpperCase( );
167 
168         // L'attribut a trié est de la forme "objet1.objet2.objet3"
169         // On split pour récupérer le premier élément
170         String [ ] splitedSortedAttribute = StringUtils.split( sortedAttribute, ".", 2 );
171         String firstElement = splitedSortedAttribute [0];
172 
173         String strMethodName = "get" + strFirstLetter + firstElement.substring( 1, firstElement.length( ) );
174 
175         try
176         {
177             method = obj.getClass( ).getMethod( strMethodName );
178 
179             // S'il y a d'autres éléments, alors on appelle la méthode de façon
180             // récursive
181             if ( splitedSortedAttribute.length > 1 )
182             {
183                 obj = method.invoke( obj );
184                 return getMethod( obj, splitedSortedAttribute [1] );
185             }
186 
187         }
188         catch( Exception e )
189         {
190             AppLogService.error( e );
191         }
192 
193         return Pair.of( obj, method );
194     }
195 
196     /**
197      * Returns <code>true</code> if the class implements {@link Comparable} or extends a super class that implements {@link Comparable}, <code>false</code>
198      * otherwise.
199      *
200      * @param clazz
201      *            the class
202      * @return <code>true</code> if the class implements {@link Comparable}, <code>false</code> otherwise.
203      */
204     private boolean isComparable( Class<?> clazz )
205     {
206         for ( Class<?> interfac : clazz.getInterfaces( ) )
207         {
208             if ( interfac.equals( Comparable.class ) )
209             {
210                 return true;
211             }
212         }
213 
214         // The class might be extending a super class that implements {@link
215         // Comparable}
216         Class<?> superClass = clazz.getSuperclass( );
217 
218         if ( superClass != null )
219         {
220             return isComparable( superClass );
221         }
222 
223         return false;
224     }
225 
226 }