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