View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors. 
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   *
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.graph.property;
25  
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.math.BigDecimal;
29  import java.net.URI;
30  import java.util.Calendar;
31  import java.util.Comparator;
32  import java.util.Date;
33  import java.util.UUID;
34  import net.jcip.annotations.Immutable;
35  import org.modeshape.common.util.SecureHash;
36  import org.modeshape.graph.GraphI18n;
37  import org.modeshape.graph.property.basic.StringValueFactory;
38  
39  /**
40   * A set of {@link Comparator} objects for the different kinds of property values.
41   * 
42   * @see PropertyType#getComparator()
43   */
44  @Immutable
45  public class ValueComparators {
46  
47      /**
48       * A comparator of string values.
49       */
50      public static final Comparator<String> STRING_COMPARATOR = new Comparator<String>() {
51  
52          public int compare( String o1,
53                              String o2 ) {
54              if (o1 == o2) return 0;
55              if (o1 == null) return -1;
56              if (o2 == null) return 1;
57              return o1.compareTo(o2);
58          }
59      };
60      /**
61       * A comparator of long values.
62       */
63      public static final Comparator<Long> LONG_COMPARATOR = new Comparator<Long>() {
64  
65          public int compare( Long o1,
66                              Long o2 ) {
67              if (o1 == o2) return 0;
68              if (o1 == null) return -1;
69              if (o2 == null) return 1;
70              return o1.compareTo(o2);
71          }
72      };
73      /**
74       * A comparator of double values.
75       */
76      public static final Comparator<Double> DOUBLE_COMPARATOR = new Comparator<Double>() {
77  
78          public int compare( Double o1,
79                              Double o2 ) {
80              if (o1 == o2) return 0;
81              if (o1 == null) return -1;
82              if (o2 == null) return 1;
83              return o1.compareTo(o2);
84          }
85      };
86      /**
87       * A comparator of decimal values.
88       */
89      public static final Comparator<BigDecimal> DECIMAL_COMPARATOR = new Comparator<BigDecimal>() {
90  
91          public int compare( BigDecimal o1,
92                              BigDecimal o2 ) {
93              if (o1 == o2) return 0;
94              if (o1 == null) return -1;
95              if (o2 == null) return 1;
96              return o1.compareTo(o2);
97          }
98      };
99      /**
100      * A comparator of binary values. Although {@link Binary} is {@link Comparable}, this comparator does not rely upon any
101      * particular Binary implementation. Thus, Binary implementations can use this for their {@link Comparable#compareTo(Object)}
102      * implementation.
103      */
104     public static final Comparator<Binary> BINARY_COMPARATOR = new Comparator<Binary>() {
105 
106         public int compare( Binary o1,
107                             Binary o2 ) {
108             if (o1 == o2) return 0;
109             if (o1 == null) return -1;
110             if (o2 == null) return 1;
111             try {
112                 o1.acquire();
113                 try {
114                     o2.acquire();
115                     final long len1 = o1.getSize();
116                     final long len2 = o2.getSize();
117                     if (len1 < len2) return -1;
118                     if (len1 > len2) return 1;
119 
120                     // Compare using the hashes, if available
121                     byte[] hash1 = o1.getHash();
122                     byte[] hash2 = o2.getHash();
123                     if (hash1.length != 0 || hash2.length != 0) {
124                         assert hash1.length == hash2.length;
125                         for (int i = 0; i != hash1.length; ++i) {
126                             int diff = hash1[i] - hash2[i];
127                             if (diff != 0) return diff;
128                         }
129                         return 0;
130                         // If the hashes match, then we should assume that the values match.
131                         // That's the whole point of using a secure hash.
132                     }
133 
134                     // One or both of the hashes could not be generated, so we have to go compare
135                     // the whole values. This is unfortunate, but should happen very rarely (if ever)
136                     // as long as the BinaryValue.getHash() is always implemented
137 
138                     // Otherwise they are the same length ...
139                     InputStream stream1 = null;
140                     InputStream stream2 = null;
141                     try {
142                         stream1 = o1.getStream();
143                         stream2 = o2.getStream();
144                         byte[] buffer1 = new byte[1024];
145                         byte[] buffer2 = new byte[1024];
146                         while (true) {
147                             int numRead1 = stream1.read(buffer1);
148                             if (numRead1 < 0) break;
149                             int numRead2 = stream2.read(buffer2);
150                             if (numRead1 != numRead2) {
151                                 throw new IoException(GraphI18n.errorReadingPropertyValueBytes.text());
152                             }
153                             for (int i = 0; i != numRead1; ++i) {
154                                 int diff = buffer1[i] - buffer2[i];
155                                 if (diff != 0) return diff;
156                             }
157                         }
158                         return 0;
159                     } catch (IOException e) {
160                         throw new IoException(GraphI18n.errorReadingPropertyValueBytes.text());
161                     } finally {
162                         if (stream1 != null) {
163                             try {
164                                 stream1.close();
165                             } catch (IOException e) {
166                                 // do nothing
167                             }
168                         }
169                         if (stream2 != null) {
170                             try {
171                                 stream2.close();
172                             } catch (IOException e) {
173                                 // do nothing
174                             }
175                         }
176                     }
177                 } finally {
178                     o2.release();
179                 }
180             } finally {
181                 o1.release();
182             }
183         }
184     };
185     /**
186      * A comparator of boolean values.
187      */
188     public static final Comparator<Boolean> BOOLEAN_COMPARATOR = new Comparator<Boolean>() {
189 
190         public int compare( Boolean o1,
191                             Boolean o2 ) {
192             if (o1 == o2) return 0;
193             if (o1 == null) return -1;
194             if (o2 == null) return 1;
195             return o1.compareTo(o2);
196         }
197     };
198     /**
199      * A comparator of date-time instances.
200      */
201     public static final Comparator<DateTime> DATE_TIME_COMPARATOR = new Comparator<DateTime>() {
202 
203         public int compare( DateTime o1,
204                             DateTime o2 ) {
205             if (o1 == o2) return 0;
206             if (o1 == null) return -1;
207             if (o2 == null) return 1;
208             return o1.compareTo(o2);
209         }
210     };
211     /**
212      * A comparator of date values.
213      */
214     public static final Comparator<Date> DATE_COMPARATOR = new Comparator<Date>() {
215 
216         public int compare( Date o1,
217                             Date o2 ) {
218             if (o1 == o2) return 0;
219             if (o1 == null) return -1;
220             if (o2 == null) return 1;
221             return o1.compareTo(o2);
222         }
223     };
224     /**
225      * A comparator of calendar values.
226      */
227     public static final Comparator<Calendar> CALENDAR_COMPARATOR = new Comparator<Calendar>() {
228 
229         public int compare( Calendar o1,
230                             Calendar o2 ) {
231             if (o1 == o2) return 0;
232             if (o1 == null) return -1;
233             if (o2 == null) return 1;
234             return o1.compareTo(o2);
235         }
236     };
237     /**
238      * A comparator of name values.
239      */
240     public static final Comparator<Name> NAME_COMPARATOR = new Comparator<Name>() {
241 
242         public int compare( Name o1,
243                             Name o2 ) {
244             if (o1 == o2) return 0;
245             if (o1 == null) return -1;
246             if (o2 == null) return 1;
247             return o1.compareTo(o2);
248         }
249     };
250     /**
251      * A comparator of path values.
252      */
253     public static final Comparator<Path> PATH_COMPARATOR = new Comparator<Path>() {
254 
255         public int compare( Path o1,
256                             Path o2 ) {
257             if (o1 == o2) return 0;
258             if (o1 == null) return -1;
259             if (o2 == null) return 1;
260             return o1.compareTo(o2);
261         }
262     };
263     /**
264      * A comparator of path segment values.
265      */
266     public static final Comparator<Path.Segment> PATH_SEGMENT_COMPARATOR = new Comparator<Path.Segment>() {
267 
268         public int compare( Path.Segment o1,
269                             Path.Segment o2 ) {
270             if (o1 == o2) return 0;
271             if (o1 == null) return -1;
272             if (o2 == null) return 1;
273             return o1.compareTo(o2);
274         }
275     };
276     /**
277      * A comparator of path segment names, excluding same-name-sibling indexes.
278      */
279     public static final Comparator<Path.Segment> PATH_SEGMENT_NAME_COMPARATOR = new Comparator<Path.Segment>() {
280 
281         public int compare( Path.Segment o1,
282                             Path.Segment o2 ) {
283             if (o1 == o2) return 0;
284             if (o1 == null) return -1;
285             if (o2 == null) return 1;
286             return o1.getName().compareTo(o2.getName());
287         }
288     };
289     /**
290      * A comparator of URI values.
291      */
292     public static final Comparator<URI> URI_COMPARATOR = new Comparator<URI>() {
293 
294         public int compare( URI o1,
295                             URI o2 ) {
296             if (o1 == o2) return 0;
297             if (o1 == null) return -1;
298             if (o2 == null) return 1;
299             return o1.compareTo(o2);
300         }
301     };
302     /**
303      * A comparator of UUID values.
304      */
305     public static final Comparator<UUID> UUID_COMPARATOR = new Comparator<UUID>() {
306 
307         public int compare( UUID o1,
308                             UUID o2 ) {
309             if (o1 == o2) return 0;
310             if (o1 == null) return -1;
311             if (o2 == null) return 1;
312             return o1.compareTo(o2);
313         }
314     };
315     /**
316      * A comparator of reference values.
317      */
318     public static final Comparator<Reference> REFERENCE_COMPARATOR = new Comparator<Reference>() {
319 
320         public int compare( Reference o1,
321                             Reference o2 ) {
322             if (o1 == o2) return 0;
323             if (o1 == null) return -1;
324             if (o2 == null) return 1;
325             return o1.compareTo(o2);
326         }
327     };
328     /**
329      * A comparator of other values.
330      */
331     public static final Comparator<Object> OBJECT_COMPARATOR = new Comparator<Object>() {
332 
333         @SuppressWarnings( "unchecked" )
334         public int compare( Object o1,
335                             Object o2 ) {
336             if (o1 == o2) return 0;
337             if (o1 == null) return -1;
338             if (o2 == null) return 1;
339             PropertyType type1 = PropertyType.discoverType(o1);
340             PropertyType type2 = PropertyType.discoverType(o2);
341 
342             // Canonicalize the values ...
343             o1 = type1.getCanonicalValue(o1);
344             o2 = type2.getCanonicalValue(o2);
345 
346             if (type1 != PropertyType.OBJECT && type2 != PropertyType.OBJECT) {
347                 if (type1 == type2) return ((Comparator<Object>)type1.getComparator()).compare(o1, o2);
348 
349                 // The types are different but the classes are the same ...
350                 if (type1.getValueClass().isAssignableFrom(type2.getValueClass())) {
351                     return ((Comparator<Object>)type1.getComparator()).compare(o1, o2);
352                 }
353                 if (type2.getValueClass().isAssignableFrom(type1.getValueClass())) {
354                     return ((Comparator<Object>)type2.getComparator()).compare(o1, o2);
355                 }
356             }
357 
358             // The types are different. See if one is a BINARY value (because we can use the secure
359             // hashes to efficiently do the comparison) ...
360             ValueFactory<String> stringFactory = getStringValueFactory();
361             String value1 = null;
362             String value2 = null;
363             if (type1 == PropertyType.BINARY || type2 == PropertyType.BINARY) {
364                 try {
365                     byte[] hash1 = null;
366                     byte[] hash2 = null;
367                     // We don't have access to a binary factory, so do this brute force.
368                     // Conver the non-binary value to a string, then compute the hash of the string ...
369                     if (type1 == PropertyType.BINARY) {
370                         value2 = stringFactory.create(o2);
371                         hash2 = SecureHash.getHash(SecureHash.Algorithm.SHA_1, value2.getBytes());
372                         hash1 = ((Binary)o1).getHash();
373                     } else {
374                         assert type2 == PropertyType.BINARY;
375                         value1 = stringFactory.create(o1);
376                         hash1 = SecureHash.getHash(SecureHash.Algorithm.SHA_1, value1.getBytes());
377                         hash2 = ((Binary)o2).getHash();
378                     }
379                     // Compute the difference in the hashes ...
380                     if (hash1.length == hash2.length) {
381                         for (int i = 0; i != hash1.length; ++i) {
382                             int diff = hash1[i] - hash2[i];
383                             if (diff != 0) return diff;
384                         }
385                         return 0;
386                     }
387                 } catch (Throwable error) {
388                     // If anything went wrong, just continue with the string comparison
389                 }
390             }
391 
392             // The types are different and must be converted ...
393             if (value1 == null) value1 = stringFactory.create(o1);
394             if (value2 == null) value2 = stringFactory.create(o2);
395             return value1.compareTo(value2);
396         }
397     };
398 
399     // This is loaded lazily so that there is not a circular dependency between PropertyType (depends on this),
400     // StringValueFactory (depends on PropertyType), and OBJECT_COMPARATOR (which depends on StringValueFactory) ...
401     private static ValueFactory<String> STRING_VALUE_FACTORY;
402 
403     protected static final ValueFactory<String> getStringValueFactory() {
404         // No locking is required, because it doesn't matter if we create several instances during initialization ...
405         if (STRING_VALUE_FACTORY == null) {
406             STRING_VALUE_FACTORY = new StringValueFactory(Path.NO_OP_DECODER, Path.NO_OP_ENCODER);
407         }
408         return STRING_VALUE_FACTORY;
409     }
410 }