1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xpath.axes;
  58. import java.io.IOException;
  59. import java.io.ObjectInputStream;
  60. import java.io.Serializable;
  61. import java.util.Vector;
  62. import javax.xml.transform.TransformerException;
  63. import org.apache.xml.dtm.Axis;
  64. import org.apache.xml.dtm.DTM;
  65. import org.apache.xml.dtm.DTMIterator;
  66. import org.apache.xpath.Expression;
  67. import org.apache.xpath.ExpressionOwner;
  68. import org.apache.xpath.XPathVisitor;
  69. import org.apache.xpath.compiler.Compiler;
  70. import org.apache.xpath.compiler.OpCodes;
  71. /**
  72. * <meta name="usage" content="advanced"/>
  73. * This class extends NodeSetDTM, which implements DTMIterator,
  74. * and fetches nodes one at a time in document order based on a XPath
  75. * <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
  76. * As each node is iterated via nextNode(), the node is also stored
  77. * in the NodeVector, so that previousNode() can easily be done.
  78. */
  79. public class UnionPathIterator extends LocPathIterator
  80. implements Cloneable, DTMIterator, java.io.Serializable, PathComponent
  81. {
  82. /**
  83. * Constructor to create an instance which you can add location paths to.
  84. */
  85. public UnionPathIterator()
  86. {
  87. super();
  88. // m_mutable = false;
  89. // m_cacheNodes = false;
  90. m_iterators = null;
  91. m_exprs = null;
  92. }
  93. /**
  94. * Initialize the context values for this expression
  95. * after it is cloned.
  96. *
  97. * @param execContext The XPath runtime context for this
  98. * transformation.
  99. */
  100. public void setRoot(int context, Object environment)
  101. {
  102. super.setRoot(context, environment);
  103. try
  104. {
  105. if (null != m_exprs)
  106. {
  107. int n = m_exprs.length;
  108. DTMIterator newIters[] = new DTMIterator[n];
  109. for (int i = 0; i < n; i++)
  110. {
  111. DTMIterator iter = m_exprs[i].asIterator(m_execContext, context);
  112. newIters[i] = iter;
  113. iter.nextNode();
  114. }
  115. m_iterators = newIters;
  116. }
  117. }
  118. catch(Exception e)
  119. {
  120. throw new org.apache.xml.utils.WrappedRuntimeException(e);
  121. }
  122. }
  123. /**
  124. * Add an iterator to the union list.
  125. *
  126. * @param iter non-null reference to a location path iterator.
  127. */
  128. public void addIterator(DTMIterator expr)
  129. {
  130. // Increase array size by only 1 at a time. Fix this
  131. // if it looks to be a problem.
  132. if (null == m_iterators)
  133. {
  134. m_iterators = new DTMIterator[1];
  135. m_iterators[0] = expr;
  136. }
  137. else
  138. {
  139. DTMIterator[] exprs = m_iterators;
  140. int len = m_iterators.length;
  141. m_iterators = new DTMIterator[len + 1];
  142. System.arraycopy(exprs, 0, m_iterators, 0, len);
  143. m_iterators[len] = expr;
  144. }
  145. expr.nextNode();
  146. if(expr instanceof Expression)
  147. ((Expression)expr).exprSetParent(this);
  148. }
  149. /**
  150. * Detaches the iterator from the set which it iterated over, releasing
  151. * any computational resources and placing the iterator in the INVALID
  152. * state. After<code>detach</code> has been invoked, calls to
  153. * <code>nextNode</code> or<code>previousNode</code> will raise the
  154. * exception INVALID_STATE_ERR.
  155. */
  156. public void detach()
  157. {
  158. if(null != m_iterators)
  159. {
  160. int n = m_iterators.length;
  161. for(int i = 0; i < n; i++)
  162. {
  163. m_iterators[i].detach();
  164. }
  165. m_iterators = null;
  166. }
  167. }
  168. /**
  169. * Create a UnionPathIterator object, including creation
  170. * of location path iterators from the opcode list, and call back
  171. * into the Compiler to create predicate expressions.
  172. *
  173. * @param compiler The Compiler which is creating
  174. * this expression.
  175. * @param opPos The position of this iterator in the
  176. * opcode list from the compiler.
  177. *
  178. * @throws javax.xml.transform.TransformerException
  179. */
  180. public UnionPathIterator(Compiler compiler, int opPos)
  181. throws javax.xml.transform.TransformerException
  182. {
  183. super();
  184. opPos = compiler.getFirstChildPos(opPos);
  185. loadLocationPaths(compiler, opPos, 0);
  186. }
  187. /**
  188. * This will return an iterator capable of handling the union of paths given.
  189. *
  190. * @param compiler The Compiler which is creating
  191. * this expression.
  192. * @param opPos The position of this iterator in the
  193. * opcode list from the compiler.
  194. *
  195. * @return Object that is derived from LocPathIterator.
  196. *
  197. * @throws javax.xml.transform.TransformerException
  198. */
  199. public static LocPathIterator createUnionIterator(Compiler compiler, int opPos)
  200. throws javax.xml.transform.TransformerException
  201. {
  202. // For the moment, I'm going to first create a full UnionPathIterator, and
  203. // then see if I can reduce it to a UnionChildIterator. It would obviously
  204. // be more effecient to just test for the conditions for a UnionChildIterator,
  205. // and then create that directly.
  206. UnionPathIterator upi = new UnionPathIterator(compiler, opPos);
  207. int nPaths = upi.m_exprs.length;
  208. boolean isAllChildIterators = true;
  209. for(int i = 0; i < nPaths; i++)
  210. {
  211. LocPathIterator lpi = upi.m_exprs[i];
  212. if(lpi.getAxis() != Axis.CHILD)
  213. {
  214. isAllChildIterators = false;
  215. break;
  216. }
  217. else
  218. {
  219. // check for positional predicates or position function, which won't work.
  220. if(HasPositionalPredChecker.check(lpi))
  221. {
  222. isAllChildIterators = false;
  223. break;
  224. }
  225. }
  226. }
  227. if(isAllChildIterators)
  228. {
  229. UnionChildIterator uci = new UnionChildIterator();
  230. for(int i = 0; i < nPaths; i++)
  231. {
  232. PredicatedNodeTest lpi = upi.m_exprs[i];
  233. // I could strip the lpi down to a pure PredicatedNodeTest, but
  234. // I don't think it's worth it. Note that the test can be used
  235. // as a static object... so it doesn't have to be cloned.
  236. uci.addNodeTest(lpi);
  237. }
  238. return uci;
  239. }
  240. else
  241. return upi;
  242. }
  243. /**
  244. * Get the analysis bits for this walker, as defined in the WalkerFactory.
  245. * @return One of WalkerFactory#BIT_DESCENDANT, etc.
  246. */
  247. public int getAnalysisBits()
  248. {
  249. int bits = 0;
  250. if (m_exprs != null)
  251. {
  252. int n = m_exprs.length;
  253. for (int i = 0; i < n; i++)
  254. {
  255. int bit = m_exprs[i].getAnalysisBits();
  256. bits |= bit;
  257. }
  258. }
  259. return bits;
  260. }
  261. /**
  262. * Read the object from a serialization stream.
  263. *
  264. * @param stream Input stream to read from
  265. *
  266. * @throws java.io.IOException
  267. * @throws javax.xml.transform.TransformerException
  268. */
  269. private void readObject(java.io.ObjectInputStream stream)
  270. throws java.io.IOException, javax.xml.transform.TransformerException
  271. {
  272. try
  273. {
  274. stream.defaultReadObject();
  275. m_clones = new IteratorPool(this);
  276. }
  277. catch (ClassNotFoundException cnfe)
  278. {
  279. throw new javax.xml.transform.TransformerException(cnfe);
  280. }
  281. }
  282. /**
  283. * Get a cloned LocPathIterator that holds the same
  284. * position as this iterator.
  285. *
  286. * @return A clone of this iterator that holds the same node position.
  287. *
  288. * @throws CloneNotSupportedException
  289. */
  290. public Object clone() throws CloneNotSupportedException
  291. {
  292. UnionPathIterator clone = (UnionPathIterator) super.clone();
  293. // if (m_iterators != null)
  294. // {
  295. // int n = m_iterators.length;
  296. //
  297. // clone.m_iterators = new LocPathIterator[n];
  298. //
  299. // for (int i = 0; i < n; i++)
  300. // {
  301. // clone.m_iterators[i] = (LocPathIterator)m_iterators[i].clone();
  302. // }
  303. // }
  304. return clone;
  305. }
  306. /**
  307. * Create a new location path iterator.
  308. *
  309. * @param compiler The Compiler which is creating
  310. * this expression.
  311. * @param opPos The position of this iterator in the
  312. *
  313. * @return New location path iterator.
  314. *
  315. * @throws javax.xml.transform.TransformerException
  316. */
  317. protected LocPathIterator createDTMIterator(
  318. Compiler compiler, int opPos) throws javax.xml.transform.TransformerException
  319. {
  320. LocPathIterator lpi = (LocPathIterator)WalkerFactory.newDTMIterator(compiler, opPos,
  321. (compiler.getLocationPathDepth() <= 0));
  322. return lpi;
  323. }
  324. /**
  325. * Initialize the location path iterators. Recursive.
  326. *
  327. * @param compiler The Compiler which is creating
  328. * this expression.
  329. * @param opPos The position of this iterator in the
  330. * opcode list from the compiler.
  331. * @param count The insert position of the iterator.
  332. *
  333. * @throws javax.xml.transform.TransformerException
  334. */
  335. protected void loadLocationPaths(Compiler compiler, int opPos, int count)
  336. throws javax.xml.transform.TransformerException
  337. {
  338. // TODO: Handle unwrapped FilterExpr
  339. int steptype = compiler.getOp(opPos);
  340. if (steptype == OpCodes.OP_LOCATIONPATH)
  341. {
  342. loadLocationPaths(compiler, compiler.getNextOpPos(opPos), count + 1);
  343. m_exprs[count] = createDTMIterator(compiler, opPos);
  344. m_exprs[count].exprSetParent(this);
  345. }
  346. else
  347. {
  348. // Have to check for unwrapped functions, which the LocPathIterator
  349. // doesn't handle.
  350. switch (steptype)
  351. {
  352. case OpCodes.OP_VARIABLE :
  353. case OpCodes.OP_EXTFUNCTION :
  354. case OpCodes.OP_FUNCTION :
  355. case OpCodes.OP_GROUP :
  356. loadLocationPaths(compiler, compiler.getNextOpPos(opPos), count + 1);
  357. WalkingIterator iter =
  358. new WalkingIterator(compiler.getNamespaceContext());
  359. iter.exprSetParent(this);
  360. if(compiler.getLocationPathDepth() <= 0)
  361. iter.setIsTopLevel(true);
  362. iter.m_firstWalker = new org.apache.xpath.axes.FilterExprWalker(iter);
  363. iter.m_firstWalker.init(compiler, opPos, steptype);
  364. m_exprs[count] = iter;
  365. break;
  366. default :
  367. m_exprs = new LocPathIterator[count];
  368. }
  369. }
  370. }
  371. /**
  372. * Returns the next node in the set and advances the position of the
  373. * iterator in the set. After a DTMIterator is created, the first call
  374. * to nextNode() returns the first node in the set.
  375. * @return The next <code>Node</code> in the set being iterated over, or
  376. * <code>null</code> if there are no more members in that set.
  377. */
  378. public int nextNode()
  379. {
  380. if(m_foundLast)
  381. return DTM.NULL;
  382. // Loop through the iterators getting the current fetched
  383. // node, and get the earliest occuring in document order
  384. int earliestNode = DTM.NULL;
  385. if (null != m_iterators)
  386. {
  387. int n = m_iterators.length;
  388. int iteratorUsed = -1;
  389. for (int i = 0; i < n; i++)
  390. {
  391. int node = m_iterators[i].getCurrentNode();
  392. if (DTM.NULL == node)
  393. continue;
  394. else if (DTM.NULL == earliestNode)
  395. {
  396. iteratorUsed = i;
  397. earliestNode = node;
  398. }
  399. else
  400. {
  401. if (node == earliestNode)
  402. {
  403. // Found a duplicate, so skip past it.
  404. m_iterators[i].nextNode();
  405. }
  406. else
  407. {
  408. DTM dtm = getDTM(node);
  409. if (dtm.isNodeAfter(node, earliestNode))
  410. {
  411. iteratorUsed = i;
  412. earliestNode = node;
  413. }
  414. }
  415. }
  416. }
  417. if (DTM.NULL != earliestNode)
  418. {
  419. m_iterators[iteratorUsed].nextNode();
  420. incrementCurrentPos();
  421. }
  422. else
  423. m_foundLast = true;
  424. }
  425. m_lastFetched = earliestNode;
  426. return earliestNode;
  427. }
  428. /**
  429. * This function is used to fixup variables from QNames to stack frame
  430. * indexes at stylesheet build time.
  431. * @param vars List of QNames that correspond to variables. This list
  432. * should be searched backwards for the first qualified name that
  433. * corresponds to the variable reference qname. The position of the
  434. * QName in the vector from the start of the vector will be its position
  435. * in the stack frame (but variables above the globalsTop value will need
  436. * to be offset to the current stack frame).
  437. */
  438. public void fixupVariables(java.util.Vector vars, int globalsSize)
  439. {
  440. for (int i = 0; i < m_exprs.length; i++)
  441. {
  442. m_exprs[i].fixupVariables(vars, globalsSize);
  443. }
  444. }
  445. /**
  446. * The location path iterators, one for each
  447. * <a href="http://www.w3.org/TR/xpath#NT-LocationPath">location
  448. * path</a> contained in the union expression.
  449. * @serial
  450. */
  451. protected LocPathIterator[] m_exprs;
  452. /**
  453. * The location path iterators, one for each
  454. * <a href="http://www.w3.org/TR/xpath#NT-LocationPath">location
  455. * path</a> contained in the union expression.
  456. * @serial
  457. */
  458. protected DTMIterator[] m_iterators;
  459. /**
  460. * Returns the axis being iterated, if it is known.
  461. *
  462. * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple
  463. * types.
  464. */
  465. public int getAxis()
  466. {
  467. // Could be smarter.
  468. return -1;
  469. }
  470. class iterOwner implements ExpressionOwner
  471. {
  472. int m_index;
  473. iterOwner(int index)
  474. {
  475. m_index = index;
  476. }
  477. /**
  478. * @see ExpressionOwner#getExpression()
  479. */
  480. public Expression getExpression()
  481. {
  482. return m_exprs[m_index];
  483. }
  484. /**
  485. * @see ExpressionOwner#setExpression(Expression)
  486. */
  487. public void setExpression(Expression exp)
  488. {
  489. if(!(exp instanceof LocPathIterator))
  490. {
  491. // Yuck. Need FilterExprIter. Or make it so m_exprs can be just
  492. // plain expressions?
  493. WalkingIterator wi = new WalkingIterator(getPrefixResolver());
  494. FilterExprWalker few = new FilterExprWalker(wi);
  495. wi.setFirstWalker(few);
  496. few.setInnerExpression(exp);
  497. wi.exprSetParent(UnionPathIterator.this);
  498. few.exprSetParent(wi);
  499. exp.exprSetParent(few);
  500. exp = wi;
  501. }
  502. else
  503. exp.exprSetParent(UnionPathIterator.this);
  504. m_exprs[m_index] = (LocPathIterator)exp;
  505. }
  506. }
  507. /**
  508. * @see XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
  509. */
  510. public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
  511. {
  512. if(visitor.visitUnionPath(owner, this))
  513. {
  514. if(null != m_exprs)
  515. {
  516. int n = m_exprs.length;
  517. for(int i = 0; i < n; i++)
  518. {
  519. m_exprs[i].callVisitors(new iterOwner(i), visitor);
  520. }
  521. }
  522. }
  523. }
  524. /**
  525. * @see Expression#deepEquals(Expression)
  526. */
  527. public boolean deepEquals(Expression expr)
  528. {
  529. if (!super.deepEquals(expr))
  530. return false;
  531. UnionPathIterator upi = (UnionPathIterator) expr;
  532. if (null != m_exprs)
  533. {
  534. int n = m_exprs.length;
  535. if((null == upi.m_exprs) || (upi.m_exprs.length != n))
  536. return false;
  537. for (int i = 0; i < n; i++)
  538. {
  539. if(!m_exprs[i].deepEquals(upi.m_exprs[i]))
  540. return false;
  541. }
  542. }
  543. else if (null != upi.m_exprs)
  544. {
  545. return false;
  546. }
  547. return true;
  548. }
  549. }