001: package net.sf.saxon.sort;
002:
003: import net.sf.saxon.expr.XPathContext;
004: import net.sf.saxon.value.AtomicValue;
005: import net.sf.saxon.value.CalendarValue;
006: import net.sf.saxon.value.StringValue;
007: import net.sf.saxon.value.UntypedAtomicValue;
008: import net.sf.saxon.Configuration;
009:
010: import java.text.Collator;
011: import java.util.Comparator;
012:
013: /**
014: * A Comparator used for comparing atomic values of arbitrary item types. It encapsulates
015: * a Collator that is used when the values to be compared are strings. It also supports
016: * a separate method for testing equality of items, which can be used for data types that
017: * are not ordered.
018: *
019: * @author Michael H. Kay
020: *
021: */
022:
023: public class AtomicComparer implements Comparator, java.io.Serializable {
024:
025: // TODO: create specialized AtomicComparers (and/or AtomicSortComparers) for particular data types
026:
027: private Comparator collator;
028: private XPathContext conversionContext;
029:
030: /**
031: * Create an AtomicComparer
032: * @param collator the collation to be used
033: * @param conversion a context, used when converting untyped atomic values to the target type.
034: */
035:
036: public AtomicComparer(Comparator collator, XPathContext conversion) {
037: this .collator = collator;
038: if (collator == null) {
039: this .collator = CodepointCollator.getInstance();
040: }
041: this .conversionContext = conversion;
042: }
043:
044: public AtomicComparer(Comparator collator, Configuration config) {
045: this .collator = collator;
046: if (collator == null) {
047: this .collator = CodepointCollator.getInstance();
048: }
049: this .conversionContext = config.getConversionContext();
050: }
051:
052: /**
053: * Compare two AtomicValue objects according to the rules for their data type. UntypedAtomic
054: * values are compared as if they were strings; if different semantics are wanted, the conversion
055: * must be done by the caller.
056: * @param a the first object to be compared. It is intended that this should be an instance
057: * of AtomicValue, though this restriction is not enforced. If it is a StringValue, the
058: * collator is used to compare the values, otherwise the value must implement the java.util.Comparable
059: * interface.
060: * @param b the second object to be compared. This must be comparable with the first object: for
061: * example, if one is a string, they must both be strings.
062: * @return <0 if a<b, 0 if a=b, >0 if a>b
063: * @throws ClassCastException if the objects are not comparable
064: */
065:
066: public int compare(Object a, Object b) {
067:
068: // System.err.println("Comparing " + a.getClass() + "(" + a + ") with " + b.getClass() + "(" + b + ") using " + collator);
069:
070: if (a instanceof AtomicValue
071: && !((AtomicValue) a).hasBuiltInType()) {
072: a = ((AtomicValue) a).getPrimitiveValue();
073: }
074: if (b instanceof AtomicValue
075: && !((AtomicValue) b).hasBuiltInType()) {
076: b = ((AtomicValue) b).getPrimitiveValue();
077: }
078:
079: if (a instanceof UntypedAtomicValue) {
080: return ((UntypedAtomicValue) a).compareTo(b, collator,
081: conversionContext);
082: } else if (b instanceof UntypedAtomicValue) {
083: return -((UntypedAtomicValue) b).compareTo(a, collator,
084: conversionContext);
085: } else if (a instanceof CalendarValue
086: && b instanceof CalendarValue) {
087: return ((CalendarValue) a).compareTo((CalendarValue) b,
088: conversionContext);
089: } else if (a instanceof Comparable) {
090: return ((Comparable) a).compareTo(b);
091: } else if (a instanceof StringValue) {
092: return collator.compare(((StringValue) a).getStringValue(),
093: ((StringValue) b).getStringValue());
094: } else {
095: throw new ClassCastException("Objects are not comparable ("
096: + a.getClass() + ", " + b.getClass() + ')');
097: }
098: }
099:
100: /**
101: * Compare two AtomicValue objects for equality according to the rules for their data type. UntypedAtomic
102: * values are compared by converting to the type of the other operand.
103: * @param a the first object to be compared. It is intended that this should be an instance
104: * of AtomicValue, though this restriction is not enforced. If it is a StringValue, the
105: * collator is used to compare the values, otherwise the value must implement the equals() method.
106: * @param b the second object to be compared. This must be comparable with the first object: for
107: * example, if one is a string, they must both be strings.
108: * @return <0 if a<b, 0 if a=b, >0 if a>b
109: * @throws ClassCastException if the objects are not comparable
110: */
111:
112: public boolean comparesEqual(Object a, Object b) {
113: // System.err.println("Comparing " + a.getClass() + ": " + a + " with " + b.getClass() + ": " + b);
114:
115: if (a instanceof AtomicValue
116: && !((AtomicValue) a).hasBuiltInType()) {
117: a = ((AtomicValue) a).getPrimitiveValue();
118: }
119: if (b instanceof AtomicValue
120: && !((AtomicValue) b).hasBuiltInType()) {
121: b = ((AtomicValue) b).getPrimitiveValue();
122: }
123:
124: if (a instanceof UntypedAtomicValue) {
125: return ((UntypedAtomicValue) a).compareTo(b, collator,
126: conversionContext) == 0;
127: } else if (b instanceof UntypedAtomicValue) {
128: return ((UntypedAtomicValue) b).compareTo(a, collator,
129: conversionContext) == 0;
130: } else if (a instanceof StringValue) {
131: return collator.compare(((StringValue) a).getStringValue(),
132: ((StringValue) b).getStringValue()) == 0;
133: } else if (a instanceof CalendarValue
134: && b instanceof CalendarValue) {
135: return ((CalendarValue) a).compareTo((CalendarValue) b,
136: conversionContext) == 0;
137: } else if (a instanceof String) {
138: return collator.compare(a, b) == 0;
139: } else {
140: return a.equals(b);
141: }
142: }
143:
144: /**
145: * Get a comparison key for an object. This must satisfy the rule that if two objects are equal,
146: * then their comparison keys are equal, and vice versa. There is no requirement that the
147: * comparison keys should reflect the ordering of the underlying objects.
148: */
149:
150: public Object getComparisonKey(Object a) {
151:
152: if (a instanceof AtomicValue
153: && !((AtomicValue) a).hasBuiltInType()) {
154: a = ((AtomicValue) a).getPrimitiveValue();
155: }
156:
157: if (a instanceof StringValue) {
158: if (collator instanceof Collator) {
159: return ((Collator) collator)
160: .getCollationKey(((StringValue) a)
161: .getStringValue());
162: } else {
163: return ((StringValue) a).getStringValue();
164: }
165: } else {
166: return a;
167: }
168: }
169:
170: }
171:
172: //
173: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
174: // you may not use this file except in compliance with the License. You may obtain a copy of the
175: // License at http://www.mozilla.org/MPL/
176: //
177: // Software distributed under the License is distributed on an "AS IS" basis,
178: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
179: // See the License for the specific language governing rights and limitations under the License.
180: //
181: // The Original Code is: all this file.
182: //
183: // The Initial Developer of the Original Code is Michael H. Kay
184: //
185: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
186: //
187: // Contributor(s): none
188: //
|