001: // Copyright (c) 2001, 2003, 2006 Per M.A. Bothner and Brainfood Inc.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.xquery.util;
005:
006: import gnu.mapping.*;
007: import gnu.kawa.functions.NumberCompare;
008: import gnu.math.*;
009: import gnu.expr.*;
010: import gnu.kawa.xml.*;
011: import gnu.bytecode.ClassType;
012:
013: /** Compares two values (or sequences) according to XPath semantics. */
014:
015: public class Compare extends Procedure2 implements CanInline {
016: static final int RESULT_GRT = 1;
017: static final int RESULT_EQU = 0;
018: static final int RESULT_LSS = -1;
019: static final int RESULT_NAN = -2;
020: static final int RESULT_NEQ = -3;
021:
022: // One flag bit for each of the above RESULT_XXX codes:
023: static final int TRUE_IF_GRT = 1 << (RESULT_GRT + 3);
024: static final int TRUE_IF_EQU = 1 << (RESULT_EQU + 3);
025: static final int TRUE_IF_LSS = 1 << (RESULT_LSS + 3);
026: static final int TRUE_IF_NAN = 1 << (RESULT_NAN + 3);
027: static final int TRUE_IF_NEQ = 1 << (RESULT_NEQ + 3);
028: static final int VALUE_COMPARISON = 1 << 5;
029: static final int LENIENT_COMPARISON = 1 << 6;
030: static final int LENIENT_EQ = (TRUE_IF_EQU | LENIENT_COMPARISON);
031:
032: int flags;
033:
034: public static Compare make(String name, int flags) {
035: Compare proc = new Compare();
036: proc.setName(name);
037: proc.flags = flags;
038: return proc;
039: }
040:
041: /*
042: private static Object toString(object value)
043: {
044: if (value instanceof TreeList)
045: return = ((TreeList) value).stringValue(0, sbuf);
046: else if (value instanceof SeqPosition && ! (node instanceof TreePosition))
047: {
048: SeqPosition pos = (SeqPosition) value;
049: if (pos.sequence instanceof TreeList)
050: {
051: ((TreeList) pos.sequence).stringValue(pos.ipos >> 1, sbuf);
052: return;
053: }
054: }
055: }
056: */
057:
058: public static boolean apply(int flags, Object arg1, Object arg2,
059: NamedCollator collator) {
060: if (arg1 instanceof Values) {
061: Values values1 = (Values) arg1;
062: int index = 0;
063: for (;;) {
064: int next = values1.nextDataIndex(index);
065: if (next < 0)
066: return false;
067: if (apply(flags, values1.getPosNext(index << 1), arg2,
068: collator))
069: return true;
070: index = next;
071: }
072: }
073: if (arg2 instanceof Values) {
074: Values values2 = (Values) arg2;
075: int index = 0;
076: for (;;) {
077: int next = values2.nextDataIndex(index);
078: if (next < 0)
079: return false;
080: if (apply(flags, arg1, values2.getPosNext(index << 1),
081: collator))
082: return true;
083: index = next;
084: }
085: }
086: return atomicCompare(flags, KNode.atomicValue(arg1), KNode
087: .atomicValue(arg2), collator);
088: }
089:
090: public static boolean equalityComparison(int flags) {
091: return ((flags & TRUE_IF_GRT) != 0) == ((flags & TRUE_IF_LSS) != 0);
092: }
093:
094: public static boolean atomicCompare(int flags, Object arg1,
095: Object arg2, NamedCollator collator) {
096: if (arg1 instanceof UntypedAtomic) {
097: String str = arg1.toString();
098: if ((flags & VALUE_COMPARISON) != 0)
099: arg1 = str;
100: else if (arg2 instanceof DateTime)
101: arg1 = XTimeType.parseDateTime(str, ((DateTime) arg2)
102: .components());
103: else if (arg2 instanceof Duration)
104: arg1 = Duration.parse(str, ((Duration) arg2).unit());
105: else if (arg2 instanceof Number)
106: arg1 = new DFloNum(str);
107: else if (arg2 instanceof Boolean)
108: arg1 = XDataType.booleanType.valueOf(str);
109: else
110: arg1 = str;
111: }
112: if (arg2 instanceof UntypedAtomic) {
113: String str = arg2.toString();
114: if ((flags & VALUE_COMPARISON) != 0)
115: arg2 = str;
116: else if (arg1 instanceof DateTime)
117: arg2 = XTimeType.parseDateTime(str, ((DateTime) arg1)
118: .components());
119: else if (arg1 instanceof Duration)
120: arg2 = Duration.parse(str, ((Duration) arg1).unit());
121: else if (arg1 instanceof Number)
122: arg2 = new DFloNum(str);
123: else if (arg1 instanceof Boolean)
124: arg2 = XDataType.booleanType.valueOf(str);
125: else
126: arg2 = str;
127: }
128: int comp;
129: if (arg1 instanceof Number || arg2 instanceof Number) {
130: if (arg1 instanceof Duration) {
131: if (!(arg2 instanceof Duration))
132: comp = -3;
133: else {
134: Duration d1 = (Duration) arg1;
135: Duration d2 = (Duration) arg2;
136: if ((d1.unit != d2.unit || d1.unit == Unit.duration)
137: && !equalityComparison(flags))
138: comp = -3;
139: else
140: comp = Duration.compare(d1, d2);
141: }
142: } else if (arg1 instanceof DateTime) {
143: if (!(arg2 instanceof DateTime))
144: comp = -3;
145: else {
146: DateTime d1 = (DateTime) arg1;
147: DateTime d2 = (DateTime) arg2;
148: int m1 = d1.components();
149: int m2 = d2.components();
150: if (m1 != m2)
151: comp = -3;
152: else if (!equalityComparison(flags)
153: && m1 != DateTime.TIME_MASK
154: && m1 != DateTime.DATE_MASK
155: && m1 != (DateTime.DATE_MASK | DateTime.TIME_MASK))
156: comp = -3;
157: else
158: comp = DateTime.compare(d1, d2);
159: }
160: } else if (arg2 instanceof Duration
161: || arg2 instanceof DateTime)
162: comp = -3;
163: else
164: comp = NumberCompare.compare(arg1, arg2, false);
165: if (comp == -3 && (flags & LENIENT_COMPARISON) == 0)
166: throw new IllegalArgumentException(
167: "values cannot be compared");
168: return NumberCompare.checkCompareCode(comp, flags);
169: }
170: if (arg1 instanceof Symbol) {
171: if (arg2 instanceof Symbol && equalityComparison(flags))
172: comp = arg1.equals(arg2) ? 0 : -2;
173: else
174: comp = -3;
175: } else if (arg1 instanceof Boolean) {
176: if (arg2 instanceof Boolean) {
177: boolean b1 = ((Boolean) arg1).booleanValue();
178: boolean b2 = ((Boolean) arg2).booleanValue();
179: comp = b1 == b2 ? 0 : b2 ? -1 : 1;
180: } else
181: comp = -3;
182: } else if (arg2 instanceof Boolean || arg2 instanceof Symbol)
183: comp = -3;
184: else {
185: String str1 = arg1.toString();
186: String str2 = arg2.toString();
187: /* #ifdef JAVA2 */
188: if (collator != null)
189: comp = collator.compare(str1, str2);
190: else
191: /* #endif */
192: comp = NamedCollator.codepointCompare(str1, str2);
193: comp = comp < 0 ? -1 : comp > 0 ? 1 : 0;
194: }
195: if (comp == -3 && (flags & LENIENT_COMPARISON) == 0)
196: throw new IllegalArgumentException(
197: "values cannot be compared");
198: return NumberCompare.checkCompareCode(comp, flags);
199: }
200:
201: public Object apply2(Object arg1, Object arg2) {
202: if ((flags & VALUE_COMPARISON) != 0) {
203: if (arg1 == null || arg1 == Values.empty)
204: return arg1;
205: if (arg2 == null || arg2 == Values.empty)
206: return arg2;
207: return atomicCompare(flags, KNode.atomicValue(arg1), KNode
208: .atomicValue(arg2), null) ? Boolean.TRUE
209: : Boolean.FALSE;
210: }
211: return apply(flags, arg1, arg2, null) ? Boolean.TRUE
212: : Boolean.FALSE;
213: }
214:
215: public static final Compare $Eq = make("=", TRUE_IF_EQU);
216: public static final Compare $Ex$Eq = make("!=", TRUE_IF_GRT
217: | TRUE_IF_LSS | TRUE_IF_NAN | TRUE_IF_NEQ);
218: public static final Compare $Gr = make(">", TRUE_IF_GRT);
219: public static final Compare $Gr$Eq = make(">=", TRUE_IF_GRT
220: | TRUE_IF_EQU);
221: public static final Compare $Ls = make("<", TRUE_IF_LSS);
222: public static final Compare $Ls$Eq = make("<=", TRUE_IF_LSS
223: | TRUE_IF_EQU);
224:
225: public static final Compare valEq = make("eq", TRUE_IF_EQU
226: | VALUE_COMPARISON);
227: public static final Compare valNe = make("ne", TRUE_IF_GRT
228: | TRUE_IF_LSS | TRUE_IF_NAN | TRUE_IF_NEQ
229: | VALUE_COMPARISON);
230: public static final Compare valGt = make("gt", TRUE_IF_GRT
231: | VALUE_COMPARISON);
232: public static final Compare valGe = make("ge", TRUE_IF_GRT
233: | TRUE_IF_EQU | VALUE_COMPARISON);
234: public static final Compare valLt = make("lt", TRUE_IF_LSS
235: | VALUE_COMPARISON);
236: public static final Compare valLe = make("le", TRUE_IF_LSS
237: | TRUE_IF_EQU | VALUE_COMPARISON);
238:
239: public Expression inline(ApplyExp exp, ExpWalker walker) {
240: Expression folded = exp.inlineIfConstant(this , walker);
241: if (folded != exp)
242: return folded;
243: if ((flags & VALUE_COMPARISON) != 0) {
244: } else {
245: exp = new ApplyExp(ClassType
246: .make("gnu.xquery.util.Compare").getDeclaredMethod(
247: "apply", 4), new Expression[] {
248: new QuoteExp(IntNum.make(flags)), exp.getArg(0),
249: exp.getArg(1), QuoteExp.nullExp });
250: }
251: if (exp.getTypeRaw() == null)
252: exp.setType(XDataType.booleanType);
253: return exp;
254: }
255: }
|