001: // Copyright (c) 2001, 2004, 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.Values;
007: import gnu.mapping.*;
008: import gnu.lists.*;
009: import gnu.xml.NodeTree;
010: import gnu.kawa.xml.*;
011: import gnu.math.DFloNum;
012:
013: /* #ifdef use:org.w3c.dom.Node */
014: // import org.w3c.dom.Node;
015: /* #endif */
016:
017: public class SequenceUtils {
018: public static boolean isZeroOrOne(Object arg) {
019: // Assumes arg is normalized.
020: return !(arg instanceof Values) || ((Values) arg).isEmpty();
021: }
022:
023: static Object coerceToZeroOrOne(Object arg, String functionName,
024: int iarg) {
025: if (isZeroOrOne(arg))
026: return arg;
027: throw new WrongType(functionName, iarg, arg, "xs:item()?");
028: }
029:
030: public static Object zeroOrOne(Object arg) {
031: return coerceToZeroOrOne(arg, "zero-or-one", 1);
032: }
033:
034: public static Object oneOrMore(Object arg) {
035: if (arg instanceof Values && ((Values) arg).isEmpty())
036: throw new IllegalArgumentException();
037: return arg;
038: }
039:
040: public static Object exactlyOne(Object arg) {
041: // Assumes arg is normalized.
042: if (arg instanceof Values)
043: throw new IllegalArgumentException();
044: return arg;
045: }
046:
047: public static boolean isEmptySequence(Object arg) {
048: return arg instanceof Values && ((Values) arg).isEmpty();
049: }
050:
051: public static boolean exists(Object arg) {
052: return !(arg instanceof Values && ((Values) arg).isEmpty());
053: }
054:
055: public static void insertBefore$X(Object target, long position,
056: Object inserts, CallContext ctx) {
057: Consumer out = ctx.consumer;
058: boolean written = false;
059: if (position <= 0)
060: position = 1;
061: if (target instanceof Values) {
062: Values values = (Values) target;
063: int ipos = 0;
064: long i = 0;
065: for (;;) {
066: int next = values.nextPos(ipos);
067: if ((next == 0 && !written) || ++i == position) {
068: Values.writeValues(inserts, out);
069: written = true;
070: }
071: if (next == 0)
072: break;
073: values.consumePosRange(ipos, next, out);
074: ipos = next;
075: }
076: } else {
077: if (position <= 1)
078: Values.writeValues(inserts, out);
079: out.writeObject(target);
080: if (position > 1)
081: Values.writeValues(inserts, out);
082: }
083: }
084:
085: public static void remove$X(Object arg, long position,
086: CallContext ctx) {
087: Consumer out = ctx.consumer;
088: if (arg instanceof Values) {
089: Values values = (Values) arg;
090: int ipos = 0;
091: long i = 0;
092: for (;;) {
093: int next = values.nextPos(ipos);
094: if (next == 0)
095: break;
096: if (++i != position)
097: values.consumePosRange(ipos, next, out);
098: ipos = next;
099: }
100: } else if (position != 1)
101: out.writeObject(arg);
102: }
103:
104: /** Implements the standard XQuery function {@code reverse}. */
105: public static void reverse$X(Object arg, CallContext ctx) {
106: Consumer out = ctx.consumer;
107: if (!(arg instanceof Values)) {
108: out.writeObject(arg);
109: return;
110: }
111: Values vals = (Values) arg;
112: int ipos = 0;
113: int[] poses = new int[100];
114: int n = 0;
115: for (;;) {
116: if (n >= poses.length) {
117: int[] t = new int[2 * n];
118: System.arraycopy(poses, 0, t, 0, n);
119: poses = t;
120: }
121: poses[n++] = ipos;
122: ipos = vals.nextPos(ipos);
123: if (ipos == 0)
124: break;
125: }
126: for (int i = n - 1; --i >= 0;)
127: vals.consumePosRange(poses[i], poses[i + 1], out);
128: }
129:
130: public static void indexOf$X(Object seqParam, Object srchParam,
131: NamedCollator collator, CallContext ctx) {
132: Consumer out = ctx.consumer;
133: if (seqParam instanceof Values) {
134: Values vals = (Values) seqParam;
135: int ipos = vals.startPos();
136: int i = 1;
137: for (; (ipos = vals.nextPos(ipos)) != 0; i++)
138: if (Compare.apply(Compare.LENIENT_EQ, vals
139: .getPosPrevious(ipos), srchParam, collator))
140: out.writeInt(i);
141: } else if (Compare.apply(Compare.LENIENT_EQ, seqParam,
142: srchParam, collator))
143: out.writeInt(1);
144: }
145:
146: public static final NodeType textOrElement = new NodeType(
147: "element-or-text", NodeType.ELEMENT_OK | NodeType.TEXT_OK);
148:
149: public static boolean deepEqualChildren(NodeTree seq1, int ipos1,
150: NodeTree seq2, int ipos2, NamedCollator collator) {
151: NodeType filter = textOrElement;
152: int child1 = seq1.firstChildPos(ipos1, filter);
153: int child2 = seq2.firstChildPos(ipos2, filter);
154: for (;;) {
155: if (child1 == 0 || child2 == 0)
156: return child1 == child2;
157: if (!deepEqual(seq1, child1, seq2, child2, collator))
158: return false;
159: child1 = seq1.nextMatching(child1, filter, -1, false);
160: child2 = seq2.nextMatching(child2, filter, -1, false);
161: }
162: }
163:
164: public static boolean deepEqual(NodeTree seq1, int ipos1,
165: NodeTree seq2, int ipos2, NamedCollator collator) {
166: int kind1 = seq1.getNextKind(ipos1);
167: int kind2 = seq2.getNextKind(ipos2);
168: switch (kind1) {
169: case Sequence.ELEMENT_VALUE:
170: if (kind1 != kind2)
171: return false;
172: // Assumes local-name and namespace-URI are interned.
173: String loc1 = seq1.posLocalName(ipos1);
174: String loc2 = seq2.posLocalName(ipos2);
175: if (loc1 != loc2)
176: return false;
177: String ns1 = seq1.posNamespaceURI(ipos1);
178: String ns2 = seq2.posNamespaceURI(ipos2);
179: if (ns1 != ns2)
180: return false;
181: int attr1 = seq1.firstAttributePos(ipos1);
182: int nattr1 = 0;
183: for (;;) {
184: if (attr1 == 0
185: || seq1.getNextKind(attr1) != Sequence.ATTRIBUTE_VALUE)
186: break;
187: nattr1++;
188: String local = seq1.posLocalName(attr1);
189: String ns = seq1.posNamespaceURI(attr1);
190: int attr2 = seq2.getAttributeI(ipos2, ns, local);
191: if (attr2 == 0)
192: return false;
193: String aval1 = KNode.getNodeValue(seq1, attr1);
194: String aval2 = KNode.getNodeValue(seq2, attr2);
195: if (!deepEqualItems(aval1, aval2, collator))
196: return false;
197: attr1 = seq1.nextPos(attr1);
198: }
199: int nattr2 = seq2.getAttributeCount(ipos2);
200: if (nattr1 != nattr2)
201: return false;
202: /* ... fall through ... */
203: case Sequence.DOCUMENT_VALUE:
204: return deepEqualChildren(seq1, ipos1, seq2, ipos2, collator);
205: case Sequence.ATTRIBUTE_VALUE:
206: if (seq1.posLocalName(ipos1) != seq2.posLocalName(ipos2)
207: || seq1.posNamespaceURI(ipos1) != seq2
208: .posNamespaceURI(ipos2))
209: return false;
210: return deepEqualItems(KAttr.getObjectValue(seq1, ipos1),
211: KAttr.getObjectValue(seq2, ipos2), collator);
212: case Sequence.PROCESSING_INSTRUCTION_VALUE:
213: if (!seq1.posTarget(ipos1).equals(seq2.posTarget(ipos2)))
214: return false;
215: return KNode.getNodeValue(seq1, ipos1).equals(
216: KNode.getNodeValue(seq2, ipos2));
217: case Sequence.COMMENT_VALUE:
218: case Sequence.CDATA_VALUE:
219: default:
220: if (kind1 != kind2)
221: return false;
222: return KNode.getNodeValue(seq1, ipos1).equals(
223: KNode.getNodeValue(seq2, ipos2));
224: }
225: }
226:
227: public static boolean deepEqualItems(Object arg1, Object arg2,
228: NamedCollator collator) {
229: if (NumberValue.isNaN(arg1) && NumberValue.isNaN(arg2))
230: return true;
231: return Compare.atomicCompare(Compare.TRUE_IF_EQU, arg1, arg2,
232: collator);
233: }
234:
235: public static boolean deepEqual(Object arg1, Object arg2,
236: NamedCollator collator) {
237: if (arg1 == arg2)
238: return true;
239: if (arg1 == null || arg1 == Values.empty)
240: return arg2 == null || arg2 == Values.empty;
241: if (arg2 == null || arg2 == Values.empty)
242: return false;
243: int ipos1 = 1, ipos2 = 1;
244: boolean is1seq = arg1 instanceof Values;
245: boolean is2seq = arg2 instanceof Values;
246: Values vals1 = is1seq ? (Values) arg1 : null;
247: Values vals2 = is2seq ? (Values) arg2 : null;
248: boolean first = true;
249: for (;;) {
250: if (is1seq) {
251: if (first)
252: ipos1 = vals1.startPos();
253: ipos1 = vals1.nextPos(ipos1);
254: }
255: if (is2seq) {
256: if (first)
257: ipos2 = vals2.startPos();
258: ipos2 = vals2.nextPos(ipos2);
259: }
260: if (ipos1 == 0 || ipos2 == 0)
261: return ipos1 == ipos2;
262: Object item1 = is1seq ? vals1.getPosPrevious(ipos1) : arg1;
263: Object item2 = is2seq ? vals2.getPosPrevious(ipos2) : arg2;
264:
265: if (!(item1 instanceof KNode) && !(item2 instanceof KNode)) {
266: try {
267: if (!deepEqualItems(arg1, arg2, collator))
268: return false;
269: } catch (Throwable ex) {
270: return false;
271: }
272: } else if (item1 instanceof KNode && item2 instanceof KNode) {
273: KNode node1 = (KNode) item1;
274: KNode node2 = (KNode) item2;
275: if (!deepEqual((NodeTree) node1.sequence, node1.ipos,
276: (NodeTree) node2.sequence, node2.ipos, collator))
277: return false;
278: } else
279: return false;
280:
281: if (first) {
282: first = false;
283: if (!is1seq)
284: ipos1 = 0;
285: if (!is2seq)
286: ipos2 = 0;
287: }
288: }
289: }
290: }
|