001 /*
002 * JBoss DNA (http://www.jboss.org/dna)
003 * See the COPYRIGHT.txt file distributed with this work for information
004 * regarding copyright ownership. Some portions may be licensed
005 * to Red Hat, Inc. under one or more contributor license agreements.
006 * See the AUTHORS.txt file in the distribution for a full listing of
007 * individual contributors.
008 *
009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
010 * is licensed to you under the terms of the GNU Lesser General Public License as
011 * published by the Free Software Foundation; either version 2.1 of
012 * the License, or (at your option) any later version.
013 *
014 * JBoss DNA is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017 * Lesser General Public License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this software; if not, write to the Free
021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
023 */
024 package org.jboss.dna.graph.property;
025
026 import java.io.IOException;
027 import java.io.InputStream;
028 import java.math.BigDecimal;
029 import java.net.URI;
030 import java.util.Calendar;
031 import java.util.Comparator;
032 import java.util.Date;
033 import java.util.UUID;
034 import org.jboss.dna.common.util.SecureHash;
035 import org.jboss.dna.graph.GraphI18n;
036 import org.jboss.dna.graph.property.basic.StringValueFactory;
037
038 /**
039 * @author Randall Hauch
040 */
041 public class ValueComparators {
042
043 /**
044 * A comparator of string values.
045 */
046 public static final Comparator<String> STRING_COMPARATOR = new Comparator<String>() {
047
048 public int compare( String o1,
049 String o2 ) {
050 if (o1 == o2) return 0;
051 if (o1 == null) return -1;
052 if (o2 == null) return 1;
053 return o1.compareTo(o2);
054 }
055 };
056 /**
057 * A comparator of long values.
058 */
059 public static final Comparator<Long> LONG_COMPARATOR = new Comparator<Long>() {
060
061 public int compare( Long o1,
062 Long o2 ) {
063 if (o1 == o2) return 0;
064 if (o1 == null) return -1;
065 if (o2 == null) return 1;
066 return o1.compareTo(o2);
067 }
068 };
069 /**
070 * A comparator of double values.
071 */
072 public static final Comparator<Double> DOUBLE_COMPARATOR = new Comparator<Double>() {
073
074 public int compare( Double o1,
075 Double o2 ) {
076 if (o1 == o2) return 0;
077 if (o1 == null) return -1;
078 if (o2 == null) return 1;
079 return o1.compareTo(o2);
080 }
081 };
082 /**
083 * A comparator of decimal values.
084 */
085 public static final Comparator<BigDecimal> DECIMAL_COMPARATOR = new Comparator<BigDecimal>() {
086
087 public int compare( BigDecimal o1,
088 BigDecimal o2 ) {
089 if (o1 == o2) return 0;
090 if (o1 == null) return -1;
091 if (o2 == null) return 1;
092 return o1.compareTo(o2);
093 }
094 };
095 /**
096 * A comparator of binary values. Although {@link Binary} is {@link Comparable}, this comparator does not rely upon any
097 * particular Binary implementation. Thus, Binary implementations can use this for their {@link Comparable#compareTo(Object)}
098 * implementation.
099 */
100 public static final Comparator<Binary> BINARY_COMPARATOR = new Comparator<Binary>() {
101
102 public int compare( Binary o1,
103 Binary o2 ) {
104 if (o1 == o2) return 0;
105 if (o1 == null) return -1;
106 if (o2 == null) return 1;
107 try {
108 o1.acquire();
109 try {
110 o2.acquire();
111 final long len1 = o1.getSize();
112 final long len2 = o2.getSize();
113 if (len1 < len2) return -1;
114 if (len1 > len2) return 1;
115
116 // Compare using the hashes, if available
117 byte[] hash1 = o1.getHash();
118 byte[] hash2 = o2.getHash();
119 if (hash1.length != 0 || hash2.length != 0) {
120 assert hash1.length == hash2.length;
121 for (int i = 0; i != hash1.length; ++i) {
122 int diff = hash1[i] - hash2[i];
123 if (diff != 0) return diff;
124 }
125 return 0;
126 // If the hashes match, then we should assume that the values match.
127 // That's the whole point of using a secure hash.
128 }
129
130 // One or both of the hashes could not be generated, so we have to go compare
131 // the whole values. This is unfortunate, but should happen very rarely (if ever)
132 // as long as the BinaryValue.getHash() is always implemented
133
134 // Otherwise they are the same length ...
135 InputStream stream1 = null;
136 InputStream stream2 = null;
137 try {
138 stream1 = o1.getStream();
139 stream2 = o2.getStream();
140 byte[] buffer1 = new byte[1024];
141 byte[] buffer2 = new byte[1024];
142 while (true) {
143 int numRead1 = stream1.read(buffer1);
144 if (numRead1 < 0) break;
145 int numRead2 = stream2.read(buffer2);
146 if (numRead1 != numRead2) {
147 throw new IoException(GraphI18n.errorReadingPropertyValueBytes.text());
148 }
149 for (int i = 0; i != numRead1; ++i) {
150 int diff = buffer1[i] - buffer2[i];
151 if (diff != 0) return diff;
152 }
153 }
154 return 0;
155 } catch (IOException e) {
156 throw new IoException(GraphI18n.errorReadingPropertyValueBytes.text());
157 } finally {
158 if (stream1 != null) {
159 try {
160 stream1.close();
161 } catch (IOException e) {
162 // do nothing
163 }
164 }
165 if (stream2 != null) {
166 try {
167 stream2.close();
168 } catch (IOException e) {
169 // do nothing
170 }
171 }
172 }
173 } finally {
174 o2.release();
175 }
176 } finally {
177 o1.release();
178 }
179 }
180 };
181 /**
182 * A comparator of boolean values.
183 */
184 public static final Comparator<Boolean> BOOLEAN_COMPARATOR = new Comparator<Boolean>() {
185
186 public int compare( Boolean o1,
187 Boolean o2 ) {
188 if (o1 == o2) return 0;
189 if (o1 == null) return -1;
190 if (o2 == null) return 1;
191 return o1.compareTo(o2);
192 }
193 };
194 /**
195 * A comparator of date-time instances.
196 */
197 public static final Comparator<DateTime> DATE_TIME_COMPARATOR = new Comparator<DateTime>() {
198
199 public int compare( DateTime o1,
200 DateTime o2 ) {
201 if (o1 == o2) return 0;
202 if (o1 == null) return -1;
203 if (o2 == null) return 1;
204 return o1.compareTo(o2);
205 }
206 };
207 /**
208 * A comparator of date values.
209 */
210 public static final Comparator<Date> DATE_COMPARATOR = new Comparator<Date>() {
211
212 public int compare( Date o1,
213 Date o2 ) {
214 if (o1 == o2) return 0;
215 if (o1 == null) return -1;
216 if (o2 == null) return 1;
217 return o1.compareTo(o2);
218 }
219 };
220 /**
221 * A comparator of calendar values.
222 */
223 public static final Comparator<Calendar> CALENDAR_COMPARATOR = new Comparator<Calendar>() {
224
225 public int compare( Calendar o1,
226 Calendar o2 ) {
227 if (o1 == o2) return 0;
228 if (o1 == null) return -1;
229 if (o2 == null) return 1;
230 return o1.compareTo(o2);
231 }
232 };
233 /**
234 * A comparator of name values.
235 */
236 public static final Comparator<Name> NAME_COMPARATOR = new Comparator<Name>() {
237
238 public int compare( Name o1,
239 Name o2 ) {
240 if (o1 == o2) return 0;
241 if (o1 == null) return -1;
242 if (o2 == null) return 1;
243 return o1.compareTo(o2);
244 }
245 };
246 /**
247 * A comparator of path values.
248 */
249 public static final Comparator<Path> PATH_COMPARATOR = new Comparator<Path>() {
250
251 public int compare( Path o1,
252 Path o2 ) {
253 if (o1 == o2) return 0;
254 if (o1 == null) return -1;
255 if (o2 == null) return 1;
256 return o1.compareTo(o2);
257 }
258 };
259 /**
260 * A comparator of URI values.
261 */
262 public static final Comparator<URI> URI_COMPARATOR = new Comparator<URI>() {
263
264 public int compare( URI o1,
265 URI o2 ) {
266 if (o1 == o2) return 0;
267 if (o1 == null) return -1;
268 if (o2 == null) return 1;
269 return o1.compareTo(o2);
270 }
271 };
272 /**
273 * A comparator of UUID values.
274 */
275 public static final Comparator<UUID> UUID_COMPARATOR = new Comparator<UUID>() {
276
277 public int compare( UUID o1,
278 UUID o2 ) {
279 if (o1 == o2) return 0;
280 if (o1 == null) return -1;
281 if (o2 == null) return 1;
282 return o1.compareTo(o2);
283 }
284 };
285 /**
286 * A comparator of reference values.
287 */
288 public static final Comparator<Reference> REFERENCE_COMPARATOR = new Comparator<Reference>() {
289
290 public int compare( Reference o1,
291 Reference o2 ) {
292 if (o1 == o2) return 0;
293 if (o1 == null) return -1;
294 if (o2 == null) return 1;
295 return o1.compareTo(o2);
296 }
297 };
298 /**
299 * A comparator of other values.
300 */
301 public static final Comparator<Object> OBJECT_COMPARATOR = new Comparator<Object>() {
302
303 @SuppressWarnings( "unchecked" )
304 public int compare( Object o1,
305 Object o2 ) {
306 if (o1 == o2) return 0;
307 if (o1 == null) return -1;
308 if (o2 == null) return 1;
309 PropertyType type1 = PropertyType.discoverType(o1);
310 PropertyType type2 = PropertyType.discoverType(o2);
311
312 // Canonicalize the values ...
313 o1 = type1.getCanonicalValue(o1);
314 o2 = type2.getCanonicalValue(o2);
315
316 if (type1 != PropertyType.OBJECT && type2 != PropertyType.OBJECT) {
317 if (type1 == type2) return ((Comparator<Object>)type1.getComparator()).compare(o1, o2);
318
319 // The types are different but the classes are the same ...
320 if (type1.getValueClass().isAssignableFrom(type2.getValueClass())) {
321 return ((Comparator<Object>)type1.getComparator()).compare(o1, o2);
322 }
323 if (type2.getValueClass().isAssignableFrom(type1.getValueClass())) {
324 return ((Comparator<Object>)type2.getComparator()).compare(o1, o2);
325 }
326 }
327
328 // The types are different. See if one is a BINARY value (because we can use the secure
329 // hashes to efficiently do the comparison) ...
330 ValueFactory<String> stringFactory = getStringValueFactory();
331 String value1 = null;
332 String value2 = null;
333 if (type1 == PropertyType.BINARY || type2 == PropertyType.BINARY) {
334 try {
335 byte[] hash1 = null;
336 byte[] hash2 = null;
337 // We don't have access to a binary factory, so do this brute force.
338 // Conver the non-binary value to a string, then compute the hash of the string ...
339 if (type1 == PropertyType.BINARY) {
340 value2 = stringFactory.create(o2);
341 hash2 = SecureHash.getHash(SecureHash.Algorithm.SHA_1, value2.getBytes());
342 hash1 = ((Binary)o1).getHash();
343 } else {
344 assert type2 == PropertyType.BINARY;
345 value1 = stringFactory.create(o1);
346 hash1 = SecureHash.getHash(SecureHash.Algorithm.SHA_1, value1.getBytes());
347 hash2 = ((Binary)o2).getHash();
348 }
349 // Compute the difference in the hashes ...
350 if (hash1.length == hash2.length) {
351 for (int i = 0; i != hash1.length; ++i) {
352 int diff = hash1[i] - hash2[i];
353 if (diff != 0) return diff;
354 }
355 return 0;
356 }
357 } catch (Throwable error) {
358 // If anything went wrong, just continue with the string comparison
359 }
360 }
361
362 // The types are different and must be converted ...
363 if (value1 == null) value1 = stringFactory.create(o1);
364 if (value2 == null) value2 = stringFactory.create(o2);
365 return value1.compareTo(value2);
366 }
367 };
368
369 // This is loaded lazily so that there is not a circular dependency between PropertyType (depends on this),
370 // StringValueFactory (depends on PropertyType), and OBJECT_COMPARATOR (which depends on StringValueFactory) ...
371 private static ValueFactory<String> STRING_VALUE_FACTORY;
372
373 protected static final ValueFactory<String> getStringValueFactory() {
374 // No locking is required, because it doesn't matter if we create several instances during initialization ...
375 if (STRING_VALUE_FACTORY == null) {
376 STRING_VALUE_FACTORY = new StringValueFactory(Path.NO_OP_DECODER, Path.NO_OP_ENCODER);
377 }
378 return STRING_VALUE_FACTORY;
379 }
380 }