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    }