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 }