SequenceUtils.java 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. // Copyright (c) 2001, 2004, 2006 Per M.A. Bothner and Brainfood Inc.
  2. // This is free software; for terms and warranty disclaimer see ./COPYING.
  3. package gnu.xquery.util;
  4. import gnu.mapping.Values;
  5. import gnu.mapping.*;
  6. import gnu.lists.*;
  7. import gnu.xml.NodeTree;
  8. import gnu.kawa.xml.*;
  9. public class SequenceUtils
  10. {
  11. public static boolean isZeroOrOne (Object arg)
  12. {
  13. // Assumes arg is normalized.
  14. return ! (arg instanceof Values) || ((Values) arg).isEmpty();
  15. }
  16. static Object coerceToZeroOrOne (Object arg, String functionName, int iarg)
  17. {
  18. if (isZeroOrOne(arg))
  19. return arg;
  20. throw new WrongType(functionName, iarg, arg, "xs:item()?");
  21. }
  22. public static Object zeroOrOne (Object arg)
  23. {
  24. return coerceToZeroOrOne(arg, "zero-or-one", 1);
  25. }
  26. public static Object oneOrMore (Object arg)
  27. {
  28. if (arg instanceof Values && ((Values) arg).isEmpty())
  29. throw new IllegalArgumentException();
  30. return arg;
  31. }
  32. public static Object exactlyOne (Object arg)
  33. {
  34. // Assumes arg is normalized.
  35. if (arg instanceof Values)
  36. throw new IllegalArgumentException();
  37. return arg;
  38. }
  39. public static boolean isEmptySequence(Object arg)
  40. {
  41. return arg instanceof Values && ((Values) arg).isEmpty();
  42. }
  43. public static boolean exists (Object arg)
  44. {
  45. return ! (arg instanceof Values && ((Values) arg).isEmpty());
  46. }
  47. public static void insertBefore$X (Object target, long position,
  48. Object inserts, CallContext ctx)
  49. {
  50. Consumer out = ctx.consumer;
  51. boolean written = false;
  52. if (position <= 0)
  53. position = 1;
  54. if (target instanceof Values)
  55. {
  56. Values values = (Values) target;
  57. int ipos = 0;
  58. long i = 0;
  59. for (;;)
  60. {
  61. int next = values.nextPos(ipos);
  62. if ((next == 0 && ! written) || ++i == position)
  63. {
  64. Values.writeValues(inserts, out);
  65. written = true;
  66. }
  67. if (next == 0)
  68. break;
  69. values.consumePosRange(ipos, next, out);
  70. ipos = next;
  71. }
  72. }
  73. else
  74. {
  75. if (position <= 1)
  76. Values.writeValues(inserts, out);
  77. out.writeObject(target);
  78. if (position > 1)
  79. Values.writeValues(inserts, out);
  80. }
  81. }
  82. public static void remove$X (Object arg, long position, CallContext ctx)
  83. {
  84. Consumer out = ctx.consumer;
  85. if (arg instanceof Values)
  86. {
  87. Values values = (Values) arg;
  88. int ipos = 0;
  89. long i = 0;
  90. for (;;)
  91. {
  92. int next = values.nextPos(ipos);
  93. if (next == 0)
  94. break;
  95. if (++i != position )
  96. values.consumePosRange(ipos, next, out);
  97. ipos = next;
  98. }
  99. }
  100. else if (position != 1)
  101. out.writeObject(arg);
  102. }
  103. /** Implements the standard XQuery function {@code reverse}. */
  104. public static void reverse$X (Object arg, CallContext ctx)
  105. {
  106. Consumer out = ctx.consumer;
  107. if (! (arg instanceof Values))
  108. {
  109. out.writeObject(arg);
  110. return;
  111. }
  112. Values vals = (Values) arg;
  113. int ipos = 0;
  114. int[] poses = new int[100];
  115. int n = 0;
  116. for (;;)
  117. {
  118. if (n >= poses.length)
  119. {
  120. int[] t = new int[2 * n];
  121. System.arraycopy(poses, 0, t, 0, n);
  122. poses = t;
  123. }
  124. poses[n++] = ipos;
  125. ipos = vals.nextPos(ipos);
  126. if (ipos == 0)
  127. break;
  128. }
  129. for (int i = n-1; --i >= 0; )
  130. vals.consumePosRange(poses[i], poses[i+1], out);
  131. }
  132. public static void indexOf$X (Object seqParam, Object srchParam,
  133. NamedCollator collator, CallContext ctx)
  134. {
  135. Consumer out = ctx.consumer;
  136. if (seqParam instanceof Values)
  137. {
  138. Values vals = (Values) seqParam;
  139. int ipos = vals.startPos();
  140. int i = 1;
  141. for (; (ipos = vals.nextPos(ipos)) != 0; i++)
  142. if (Compare.apply(Compare.LENIENT_EQ,
  143. vals.getPosPrevious(ipos),
  144. srchParam, collator))
  145. out.writeInt(i);
  146. }
  147. else if (Compare.apply(Compare.LENIENT_EQ, seqParam, srchParam, collator))
  148. out.writeInt(1);
  149. }
  150. public static final NodeType textOrElement
  151. = new NodeType("element-or-text", NodeType.ELEMENT_OK|NodeType.TEXT_OK);
  152. public static boolean deepEqualChildren (NodeTree seq1, int ipos1,
  153. NodeTree seq2, int ipos2,
  154. NamedCollator collator)
  155. {
  156. NodeType filter = textOrElement;
  157. int child1 = seq1.firstChildPos(ipos1, filter);
  158. int child2 = seq2.firstChildPos(ipos2, filter);
  159. for (;;)
  160. {
  161. if (child1 == 0 || child2 == 0)
  162. return child1 == child2;
  163. if (! deepEqual(seq1, child1, seq2, child2, collator))
  164. return false;
  165. child1 = seq1.nextMatching(child1, filter, -1, false);
  166. child2 = seq2.nextMatching(child2, filter, -1, false);
  167. }
  168. }
  169. public static boolean deepEqual (NodeTree seq1, int ipos1,
  170. NodeTree seq2, int ipos2,
  171. NamedCollator collator)
  172. {
  173. int kind1 = seq1.getNextKind(ipos1);
  174. int kind2 = seq2.getNextKind(ipos2);
  175. switch (kind1)
  176. {
  177. case Sequence.ELEMENT_VALUE:
  178. if (kind1 != kind2)
  179. return false;
  180. // Assumes local-name and namespace-URI are interned.
  181. String loc1 = seq1.posLocalName(ipos1);
  182. String loc2 = seq2.posLocalName(ipos2);
  183. if (loc1 != loc2)
  184. return false;
  185. String ns1 = seq1.posNamespaceURI(ipos1);
  186. String ns2 = seq2.posNamespaceURI(ipos2);
  187. if (ns1 != ns2)
  188. return false;
  189. int attr1 = seq1.firstAttributePos(ipos1);
  190. int nattr1 = 0;
  191. for (;;)
  192. {
  193. if (attr1 == 0
  194. || seq1.getNextKind(attr1) != Sequence.ATTRIBUTE_VALUE)
  195. break;
  196. nattr1++;
  197. String local = seq1.posLocalName(attr1);
  198. String ns = seq1.posNamespaceURI(attr1);
  199. int attr2 = seq2.getAttributeI(ipos2, ns, local);
  200. if (attr2 == 0)
  201. return false;
  202. String aval1 = KNode.getNodeValue(seq1, attr1);
  203. String aval2 = KNode.getNodeValue(seq2, attr2);
  204. if (! deepEqualItems(aval1, aval2, collator))
  205. return false;
  206. attr1 = seq1.nextPos(attr1);
  207. }
  208. int nattr2 = seq2.getAttributeCount(ipos2);
  209. if (nattr1 != nattr2)
  210. return false;
  211. /* ... fall through ... */
  212. case Sequence.DOCUMENT_VALUE:
  213. return deepEqualChildren(seq1, ipos1, seq2, ipos2, collator);
  214. case Sequence.ATTRIBUTE_VALUE:
  215. if (seq1.posLocalName(ipos1) != seq2.posLocalName(ipos2)
  216. || seq1.posNamespaceURI(ipos1) != seq2.posNamespaceURI(ipos2))
  217. return false;
  218. return deepEqualItems(KAttr.getObjectValue(seq1, ipos1),
  219. KAttr.getObjectValue(seq2, ipos2),
  220. collator);
  221. case Sequence.PROCESSING_INSTRUCTION_VALUE:
  222. if (! seq1.posTarget(ipos1).equals(seq2.posTarget(ipos2)))
  223. return false;
  224. return KNode.getNodeValue(seq1, ipos1)
  225. .equals(KNode.getNodeValue(seq2, ipos2));
  226. case Sequence.COMMENT_VALUE:
  227. case Sequence.CDATA_VALUE:
  228. default:
  229. if (kind1 != kind2)
  230. return false;
  231. return KNode.getNodeValue(seq1, ipos1)
  232. .equals(KNode.getNodeValue(seq2, ipos2));
  233. }
  234. }
  235. public static boolean deepEqualItems (Object arg1, Object arg2,
  236. NamedCollator collator)
  237. {
  238. if (NumberValue.isNaN(arg1) && NumberValue.isNaN(arg2))
  239. return true;
  240. return Compare.atomicCompare(Compare.TRUE_IF_EQU, arg1, arg2, collator);
  241. }
  242. public static boolean deepEqual (Object arg1, Object arg2,
  243. NamedCollator collator)
  244. {
  245. if (arg1 == arg2)
  246. return true;
  247. if (arg1 == null || arg1 == Values.empty)
  248. return arg2 == null || arg2 == Values.empty;
  249. if (arg2 == null || arg2 == Values.empty)
  250. return false;
  251. int ipos1 = 1, ipos2 = 1;
  252. boolean is1seq = arg1 instanceof Values;
  253. boolean is2seq = arg2 instanceof Values;
  254. Values vals1 = is1seq ? (Values) arg1 : null;
  255. Values vals2 = is2seq ? (Values) arg2 : null;
  256. boolean first = true;
  257. for (;;)
  258. {
  259. if (is1seq)
  260. {
  261. if (first)
  262. ipos1 = vals1.startPos();
  263. ipos1 = vals1.nextPos(ipos1);
  264. }
  265. if (is2seq)
  266. {
  267. if (first)
  268. ipos2 = vals2.startPos();
  269. ipos2 = vals2.nextPos(ipos2);
  270. }
  271. if (ipos1 == 0 || ipos2 == 0)
  272. return ipos1 == ipos2;
  273. Object item1 = is1seq ? vals1.getPosPrevious(ipos1) : arg1;
  274. Object item2 = is2seq ? vals2.getPosPrevious(ipos2) : arg2;
  275. if (! (item1 instanceof KNode) && !( item2 instanceof KNode))
  276. {
  277. try
  278. {
  279. if (! deepEqualItems(arg1, arg2, collator))
  280. return false;
  281. }
  282. catch (Exception ex)
  283. {
  284. return false;
  285. }
  286. }
  287. else if (item1 instanceof KNode && item2 instanceof KNode)
  288. {
  289. KNode node1 = (KNode) item1;
  290. KNode node2 = (KNode) item2;
  291. if (! deepEqual((NodeTree) node1.sequence, node1.ipos,
  292. (NodeTree) node2.sequence, node2.ipos,
  293. collator))
  294. return false;
  295. }
  296. else
  297. return false;
  298. if (first)
  299. {
  300. first = false;
  301. if (! is1seq)
  302. ipos1 = 0;
  303. if (! is2seq)
  304. ipos2 = 0;
  305. }
  306. }
  307. }
  308. public static void subList$X(Object seq, double start, double end,
  309. CallContext ctx)
  310. {
  311. subList$C(seq, start, end, ctx.consumer);
  312. }
  313. public static void subList$C(Object seq, double start, double end,
  314. Consumer out)
  315. {
  316. if (seq instanceof Values)
  317. {
  318. int istart = (int) (start - 1.0);
  319. int iend = (int) (end - 1.0);
  320. Values vals = (Values) seq;
  321. int sz = vals.size();
  322. if (istart < 0)
  323. istart = 0;
  324. if (iend > sz)
  325. iend = sz;
  326. if (iend > istart)
  327. vals.consume(istart, iend, out);
  328. }
  329. else
  330. {
  331. if (start <= 1 && end >= 2)
  332. out.writeObject(seq);
  333. }
  334. }
  335. }