1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 2002 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.xalan.templates;
  58. import java.util.Vector;
  59. import org.apache.xalan.res.XSLMessages;
  60. import org.apache.xalan.res.XSLTErrorResources;
  61. import org.apache.xml.utils.QName;
  62. import org.apache.xml.utils.WrappedRuntimeException;
  63. import org.apache.xpath.Expression;
  64. import org.apache.xpath.ExpressionNode;
  65. import org.apache.xpath.ExpressionOwner;
  66. import org.apache.xpath.XPath;
  67. import org.apache.xpath.axes.AxesWalker;
  68. import org.apache.xpath.axes.FilterExprIteratorSimple;
  69. import org.apache.xpath.axes.FilterExprWalker;
  70. import org.apache.xpath.axes.LocPathIterator;
  71. import org.apache.xpath.axes.SelfIteratorNoPredicate;
  72. import org.apache.xpath.axes.WalkerFactory;
  73. import org.apache.xpath.axes.WalkingIterator;
  74. import org.apache.xpath.operations.Variable;
  75. import org.apache.xpath.operations.VariableSafeAbsRef;
  76. import org.w3c.dom.DOMException;
  77. /**
  78. * This class eleminates redundent XPaths from a given subtree,
  79. * and also collects all absolute paths within the subtree. First
  80. * it must be called as a visitor to the subtree, and then
  81. * eleminateRedundent must be called.
  82. */
  83. public class RedundentExprEliminator extends XSLTVisitor
  84. {
  85. Vector m_paths;
  86. Vector m_absPaths;
  87. boolean m_isSameContext;
  88. AbsPathChecker m_absPathChecker = new AbsPathChecker();
  89. static int m_uniquePsuedoVarID = 1;
  90. static final String PSUEDOVARNAMESPACE = Constants.S_VENDORURL+"/xalan/psuedovar";
  91. public static boolean DEBUG = false;
  92. public static boolean DIAGNOSE_NUM_PATHS_REDUCED = false;
  93. public static boolean DIAGNOSE_MULTISTEPLIST = false;
  94. /**
  95. * So we can reuse it over and over again.
  96. */
  97. VarNameCollector m_varNameCollector = new VarNameCollector();
  98. /**
  99. * Construct a RedundentExprEliminator.
  100. *
  101. * @param absPathsList Vector to which absolute paths will
  102. * be inserted. The vector may be null, if the caller does
  103. * not wish to collect absolute paths.
  104. */
  105. public RedundentExprEliminator()
  106. {
  107. m_isSameContext = true;
  108. m_absPaths = new Vector();
  109. m_paths = null;
  110. }
  111. /**
  112. * Method to be called after the all expressions within an
  113. * node context have been visited. It eliminates redundent
  114. * expressions by creating a variable in the psuedoVarRecipient
  115. * for each redundent expression, and then rewriting the redundent
  116. * expression to be a variable reference.
  117. *
  118. * @param psuedoVarRecipient The recipient of the psuedo vars. The
  119. * variables will be inserted as first children of the element, before
  120. * any existing variables.
  121. */
  122. public void eleminateRedundentLocals(ElemTemplateElement psuedoVarRecipient)
  123. {
  124. eleminateRedundent(psuedoVarRecipient, m_paths);
  125. }
  126. /**
  127. * Method to be called after the all global expressions within a stylesheet
  128. * have been collected. It eliminates redundent
  129. * expressions by creating a variable in the psuedoVarRecipient
  130. * for each redundent expression, and then rewriting the redundent
  131. * expression to be a variable reference.
  132. *
  133. * @param psuedoVarRecipient The recipient of the psuedo vars. The
  134. * variables will be inserted as first children of the element, before
  135. * any existing variables.
  136. */
  137. public void eleminateRedundentGlobals(StylesheetRoot stylesheet)
  138. {
  139. eleminateRedundent(stylesheet, m_absPaths);
  140. }
  141. /**
  142. * Method to be called after the all expressions within an
  143. * node context have been visited. It eliminates redundent
  144. * expressions by creating a variable in the psuedoVarRecipient
  145. * for each redundent expression, and then rewriting the redundent
  146. * expression to be a variable reference.
  147. *
  148. * @param psuedoVarRecipient The owner of the subtree from where the
  149. * paths were collected.
  150. * @param paths A vector of paths that hold ExpressionOwner objects,
  151. * which must yield LocationPathIterators.
  152. */
  153. protected void eleminateRedundent(ElemTemplateElement psuedoVarRecipient, Vector paths)
  154. {
  155. int n = paths.size();
  156. int numPathsEliminated = 0;
  157. int numUniquePathsEliminated = 0;
  158. for (int i = 0; i < n; i++)
  159. {
  160. ExpressionOwner owner = (ExpressionOwner) paths.elementAt(i);
  161. if (null != owner)
  162. {
  163. int found = findAndEliminateRedundant(i + 1, i, owner, psuedoVarRecipient, paths);
  164. if (found > 0)
  165. numUniquePathsEliminated++;
  166. numPathsEliminated += found;
  167. }
  168. }
  169. eleminateSharedPartialPaths(psuedoVarRecipient, paths);
  170. if(DIAGNOSE_NUM_PATHS_REDUCED)
  171. diagnoseNumPaths(paths, numPathsEliminated, numUniquePathsEliminated);
  172. }
  173. /**
  174. * Eliminate the shared partial paths in the expression list.
  175. *
  176. * @param psuedoVarRecipient The recipient of the psuedo vars.
  177. *
  178. * @param paths A vector of paths that hold ExpressionOwner objects,
  179. * which must yield LocationPathIterators.
  180. */
  181. protected void eleminateSharedPartialPaths(ElemTemplateElement psuedoVarRecipient, Vector paths)
  182. {
  183. MultistepExprHolder list = createMultistepExprList(paths);
  184. if(null != list)
  185. {
  186. if(DIAGNOSE_MULTISTEPLIST)
  187. list.diagnose();
  188. boolean isGlobal = (paths == m_absPaths);
  189. // Iterate over the list, starting with the most number of paths,
  190. // trying to find the longest matches first.
  191. int longestStepsCount = list.m_stepCount;
  192. for (int i = longestStepsCount-1; i >= 1; i--)
  193. {
  194. MultistepExprHolder next = list;
  195. while(null != next)
  196. {
  197. if(next.m_stepCount < i)
  198. break;
  199. list = matchAndEliminatePartialPaths(next, list, isGlobal, i, psuedoVarRecipient);
  200. next = next.m_next;
  201. }
  202. }
  203. }
  204. }
  205. /**
  206. * For a given path, see if there are any partitial matches in the list,
  207. * and, if there are, replace those partial paths with psuedo variable refs,
  208. * and create the psuedo variable decl.
  209. *
  210. * @return The head of the list, which may have changed.
  211. */
  212. protected MultistepExprHolder matchAndEliminatePartialPaths(MultistepExprHolder testee,
  213. MultistepExprHolder head,
  214. boolean isGlobal,
  215. int lengthToTest,
  216. ElemTemplateElement varScope)
  217. {
  218. if(null == testee.m_exprOwner)
  219. return head;
  220. // Start with the longest possible match, and move down.
  221. WalkingIterator iter1 = (WalkingIterator) testee.m_exprOwner.getExpression();
  222. if(partialIsVariable(testee, lengthToTest))
  223. return head;
  224. MultistepExprHolder matchedPaths = null;
  225. MultistepExprHolder matchedPathsTail = null;
  226. MultistepExprHolder meh = head;
  227. while( null != meh)
  228. {
  229. if ((meh != testee) && (null != meh.m_exprOwner))
  230. {
  231. WalkingIterator iter2 = (WalkingIterator) meh.m_exprOwner.getExpression();
  232. if (stepsEqual(iter1, iter2, lengthToTest))
  233. {
  234. if (null == matchedPaths)
  235. {
  236. try
  237. {
  238. matchedPaths = (MultistepExprHolder)testee.clone();
  239. testee.m_exprOwner = null; // So it won't be processed again.
  240. }
  241. catch(CloneNotSupportedException cnse){}
  242. matchedPathsTail = matchedPaths;
  243. matchedPathsTail.m_next = null;
  244. }
  245. try
  246. {
  247. matchedPathsTail.m_next = (MultistepExprHolder)meh.clone();
  248. meh.m_exprOwner = null; // So it won't be processed again.
  249. }
  250. catch(CloneNotSupportedException cnse){}
  251. matchedPathsTail = matchedPathsTail.m_next;
  252. matchedPathsTail.m_next = null;
  253. }
  254. }
  255. meh = meh.m_next;
  256. }
  257. int matchCount = 0;
  258. if(null != matchedPaths)
  259. {
  260. ElemTemplateElement root = isGlobal ? varScope : findCommonAncestor(matchedPaths);
  261. WalkingIterator sharedIter = (WalkingIterator)matchedPaths.m_exprOwner.getExpression();
  262. WalkingIterator newIter = createIteratorFromSteps(sharedIter, lengthToTest);
  263. ElemVariable var = createPsuedoVarDecl(root, newIter, isGlobal);
  264. if(DIAGNOSE_MULTISTEPLIST)
  265. System.err.println("Created var: "+var.getName()+(isGlobal ? "(Global)" : ""));
  266. while(null != matchedPaths)
  267. {
  268. ExpressionOwner owner = matchedPaths.m_exprOwner;
  269. WalkingIterator iter = (WalkingIterator)owner.getExpression();
  270. if(DIAGNOSE_MULTISTEPLIST)
  271. diagnoseLineNumber(iter);
  272. LocPathIterator newIter2 =
  273. changePartToRef(var.getName(), iter, lengthToTest, isGlobal);
  274. owner.setExpression(newIter2);
  275. matchedPaths = matchedPaths.m_next;
  276. }
  277. }
  278. if(DIAGNOSE_MULTISTEPLIST)
  279. diagnoseMultistepList(matchCount, lengthToTest, isGlobal);
  280. return head;
  281. }
  282. /**
  283. * Check if results of partial reduction will just be a variable, in
  284. * which case, skip it.
  285. */
  286. boolean partialIsVariable(MultistepExprHolder testee, int lengthToTest)
  287. {
  288. if(1 == lengthToTest)
  289. {
  290. WalkingIterator wi = (WalkingIterator)testee.m_exprOwner.getExpression();
  291. if(wi.getFirstWalker() instanceof FilterExprWalker)
  292. return true;
  293. }
  294. return false;
  295. }
  296. /**
  297. * Tell what line number belongs to a given expression.
  298. */
  299. protected void diagnoseLineNumber(Expression expr)
  300. {
  301. ElemTemplateElement e = getElemFromExpression(expr);
  302. System.err.println(" " + e.getSystemId() + " Line " + e.getLineNumber());
  303. }
  304. /**
  305. * Given a linked list of expressions, find the common ancestor that is
  306. * suitable for holding a psuedo variable for shared access.
  307. */
  308. protected ElemTemplateElement findCommonAncestor(MultistepExprHolder head)
  309. {
  310. // Not sure this algorithm is the best, but will do for the moment.
  311. int numExprs = head.getLength();
  312. // The following could be made cheaper by pre-allocating large arrays,
  313. // but then we would have to assume a max number of reductions,
  314. // which I am not inclined to do right now.
  315. ElemTemplateElement[] elems = new ElemTemplateElement[numExprs];
  316. int[] ancestorCounts = new int[numExprs];
  317. // Loop through, getting the parent elements, and counting the
  318. // ancestors.
  319. MultistepExprHolder next = head;
  320. int shortestAncestorCount = 10000;
  321. for(int i = 0; i < numExprs; i++)
  322. {
  323. ElemTemplateElement elem =
  324. getElemFromExpression(next.m_exprOwner.getExpression());
  325. elems[i] = elem;
  326. int numAncestors = countAncestors(elem);
  327. ancestorCounts[i] = numAncestors;
  328. if(numAncestors < shortestAncestorCount)
  329. {
  330. shortestAncestorCount = numAncestors;
  331. }
  332. next = next.m_next;
  333. }
  334. // Now loop through and "correct" the elements that have more ancestors.
  335. for(int i = 0; i < numExprs; i++)
  336. {
  337. if(ancestorCounts[i] > shortestAncestorCount)
  338. {
  339. int numStepCorrection = ancestorCounts[i] - shortestAncestorCount;
  340. for(int j = 0; j < numStepCorrection; j++)
  341. {
  342. elems[i] = elems[i].getParentElem();
  343. }
  344. }
  345. }
  346. // Now everyone has an equal number of ancestors. Walk up from here
  347. // equally until all are equal.
  348. ElemTemplateElement first = null;
  349. while(shortestAncestorCount-- >= 0)
  350. {
  351. boolean areEqual = true;
  352. first = elems[0];
  353. for(int i = 1; i < numExprs; i++)
  354. {
  355. if(first != elems[i])
  356. {
  357. areEqual = false;
  358. break;
  359. }
  360. }
  361. // This second check is to make sure we have a common ancestor that is not the same
  362. // as the expression owner... i.e. the var decl has to go above the expression owner.
  363. if(areEqual && isNotSameAsOwner(head, first) && first.canAcceptVariables())
  364. {
  365. if(DIAGNOSE_MULTISTEPLIST)
  366. {
  367. System.err.print(first.getClass().getName());
  368. System.err.println(" at " + first.getSystemId() + " Line " + first.getLineNumber());
  369. }
  370. return first;
  371. }
  372. for(int i = 0; i < numExprs; i++)
  373. {
  374. elems[i] = elems[i].getParentElem();
  375. }
  376. }
  377. assertion(false, "Could not find common ancestor!!!");
  378. return null;
  379. }
  380. /**
  381. * Find out if the given ElemTemplateElement is not the same as one of
  382. * the ElemTemplateElement owners of the expressions.
  383. *
  384. * @param head Head of linked list of expression owners.
  385. * @param ete The ElemTemplateElement that is a candidate for a psuedo
  386. * variable parent.
  387. * @return true if the given ElemTemplateElement is not the same as one of
  388. * the ElemTemplateElement owners of the expressions. This is to make sure
  389. * we find an ElemTemplateElement that is in a viable position to hold
  390. * psuedo variables that are visible to the references.
  391. */
  392. protected boolean isNotSameAsOwner(MultistepExprHolder head, ElemTemplateElement ete)
  393. {
  394. MultistepExprHolder next = head;
  395. while(null != next)
  396. {
  397. ElemTemplateElement elemOwner = getElemFromExpression(next.m_exprOwner.getExpression());
  398. if(elemOwner == ete)
  399. return false;
  400. next = next.m_next;
  401. }
  402. return true;
  403. }
  404. /**
  405. * Count the number of ancestors that a ElemTemplateElement has.
  406. *
  407. * @param elem An representation of an element in an XSLT stylesheet.
  408. * @return The number of ancestors of elem (including the element itself).
  409. */
  410. protected int countAncestors(ElemTemplateElement elem)
  411. {
  412. int count = 0;
  413. while(null != elem)
  414. {
  415. count++;
  416. elem = elem.getParentElem();
  417. }
  418. return count;
  419. }
  420. /**
  421. * Print out diagnostics about partial multistep evaluation.
  422. */
  423. protected void diagnoseMultistepList(
  424. int matchCount,
  425. int lengthToTest,
  426. boolean isGlobal)
  427. {
  428. if (matchCount > 0)
  429. {
  430. System.err.print(
  431. "Found multistep matches: " + matchCount + ", " + lengthToTest + " length");
  432. if (isGlobal)
  433. System.err.println(" (global)");
  434. else
  435. System.err.println();
  436. }
  437. }
  438. /**
  439. * Change a given number of steps to a single variable reference.
  440. *
  441. * @param uniquePsuedoVarName The name of the variable reference.
  442. * @param wi The walking iterator that is to be changed.
  443. * @param numSteps The number of steps to be changed.
  444. * @param isGlobal true if this will be a global reference.
  445. */
  446. protected LocPathIterator changePartToRef(final QName uniquePsuedoVarName, WalkingIterator wi,
  447. final int numSteps, final boolean isGlobal)
  448. {
  449. Variable var = new Variable();
  450. var.setQName(uniquePsuedoVarName);
  451. var.setIsGlobal(isGlobal);
  452. if(isGlobal)
  453. { ElemTemplateElement elem = getElemFromExpression(wi);
  454. StylesheetRoot root = elem.getStylesheetRoot();
  455. Vector vars = root.getVariablesAndParamsComposed();
  456. var.setIndex(vars.size()-1);
  457. }
  458. // Walk to the first walker after the one's we are replacing.
  459. AxesWalker walker = wi.getFirstWalker();
  460. for(int i = 0; i < numSteps; i++)
  461. {
  462. assertion(null != walker, "Walker should not be null!");
  463. walker = walker.getNextWalker();
  464. }
  465. if(null != walker)
  466. {
  467. FilterExprWalker few = new FilterExprWalker(wi);
  468. few.setInnerExpression(var);
  469. few.exprSetParent(wi);
  470. few.setNextWalker(walker);
  471. walker.setPrevWalker(few);
  472. wi.setFirstWalker(few);
  473. return wi;
  474. }
  475. else
  476. {
  477. FilterExprIteratorSimple feis = new FilterExprIteratorSimple(var);
  478. feis.exprSetParent(wi.exprGetParent());
  479. return feis;
  480. }
  481. }
  482. /**
  483. * Create a new WalkingIterator from the steps in another WalkingIterator.
  484. *
  485. * @param wi The iterator from where the steps will be taken.
  486. * @param numSteps The number of steps from the first to copy into the new
  487. * iterator.
  488. * @return The new iterator.
  489. */
  490. protected WalkingIterator createIteratorFromSteps(final WalkingIterator wi, int numSteps)
  491. {
  492. WalkingIterator newIter = new WalkingIterator(wi.getPrefixResolver());
  493. try
  494. {
  495. AxesWalker walker = (AxesWalker)wi.getFirstWalker().clone();
  496. newIter.setFirstWalker(walker);
  497. walker.setLocPathIterator(newIter);
  498. for(int i = 1; i < numSteps; i++)
  499. {
  500. AxesWalker next = (AxesWalker)walker.getNextWalker().clone();
  501. walker.setNextWalker(next);
  502. next.setLocPathIterator(newIter);
  503. walker = next;
  504. }
  505. walker.setNextWalker(null);
  506. }
  507. catch(CloneNotSupportedException cnse)
  508. {
  509. throw new WrappedRuntimeException(cnse);
  510. }
  511. return newIter;
  512. }
  513. /**
  514. * Compare a given number of steps between two iterators, to see if they are equal.
  515. *
  516. * @param iter1 The first iterator to compare.
  517. * @param iter2 The second iterator to compare.
  518. * @param numSteps The number of steps to compare.
  519. * @return true If the given number of steps are equal.
  520. *
  521. */
  522. protected boolean stepsEqual(WalkingIterator iter1, WalkingIterator iter2,
  523. int numSteps)
  524. {
  525. AxesWalker aw1 = iter1.getFirstWalker();
  526. AxesWalker aw2 = iter2.getFirstWalker();
  527. for(int i = 0; (i < numSteps); i++)
  528. {
  529. if((null == aw1) || (null == aw2))
  530. return false;
  531. if(!aw1.deepEquals(aw2))
  532. return false;
  533. aw1 = aw1.getNextWalker();
  534. aw2 = aw2.getNextWalker();
  535. }
  536. assertion((null != aw1) || (null != aw2), "Total match is incorrect!");
  537. return true;
  538. }
  539. /**
  540. * For the reduction of location path parts, create a list of all
  541. * the multistep paths with more than one step, sorted by the
  542. * number of steps, with the most steps occuring earlier in the list.
  543. * If the list is only one member, don't bother returning it.
  544. *
  545. * @param paths Vector of ExpressionOwner objects, which may contain null entries.
  546. * The ExpressionOwner objects must own LocPathIterator objects.
  547. * @return null if no multipart paths are found or the list is only of length 1,
  548. * otherwise the first MultistepExprHolder in a linked list of these objects.
  549. */
  550. protected MultistepExprHolder createMultistepExprList(Vector paths)
  551. {
  552. MultistepExprHolder first = null;
  553. int n = paths.size();
  554. for(int i = 0; i < n; i++)
  555. {
  556. ExpressionOwner eo = (ExpressionOwner)paths.elementAt(i);
  557. if(null == eo)
  558. continue;
  559. // Assuming location path iterators should be OK.
  560. LocPathIterator lpi = (LocPathIterator)eo.getExpression();
  561. int numPaths = countSteps(lpi);
  562. if(numPaths > 1)
  563. {
  564. if(null == first)
  565. first = new MultistepExprHolder(eo, numPaths, null);
  566. else
  567. first = first.addInSortedOrder(eo, numPaths);
  568. }
  569. }
  570. if((null == first) || (first.getLength() <= 1))
  571. return null;
  572. else
  573. return first;
  574. }
  575. /**
  576. * Look through the vector from start point, looking for redundant occurances.
  577. * When one or more are found, create a psuedo variable declaration, insert
  578. * it into the stylesheet, and replace the occurance with a reference to
  579. * the psuedo variable. When a redundent variable is found, it's slot in
  580. * the vector will be replaced by null.
  581. *
  582. * @param start The position to start looking in the vector.
  583. * @param targetIndex The position of firstOccuranceOwner.
  584. * @param firstOccuranceOwner The owner of the expression we are looking for.
  585. * @param psuedoVarRecipient Where to put the psuedo variables.
  586. *
  587. * @return The number of expression occurances that were modified.
  588. */
  589. protected int findAndEliminateRedundant(int start, int firstOccuranceIndex,
  590. ExpressionOwner firstOccuranceOwner,
  591. ElemTemplateElement psuedoVarRecipient,
  592. Vector paths)
  593. throws org.w3c.dom.DOMException
  594. {
  595. MultistepExprHolder head = null;
  596. MultistepExprHolder tail = null;
  597. int numPathsFound = 0;
  598. int n = paths.size();
  599. Expression expr1 = firstOccuranceOwner.getExpression();
  600. if(DEBUG)
  601. assertIsLocPathIterator(expr1, firstOccuranceOwner);
  602. boolean isGlobal = (paths == m_absPaths);
  603. LocPathIterator lpi = (LocPathIterator)expr1;
  604. int stepCount = countSteps(lpi);
  605. for(int j = start; j < n; j++)
  606. {
  607. ExpressionOwner owner2 = (ExpressionOwner)paths.elementAt(j);
  608. if(null != owner2)
  609. {
  610. Expression expr2 = owner2.getExpression();
  611. boolean isEqual = expr2.deepEquals(lpi);
  612. if(isEqual)
  613. {
  614. LocPathIterator lpi2 = (LocPathIterator)expr2;
  615. if(null == head)
  616. {
  617. head = new MultistepExprHolder(firstOccuranceOwner, stepCount, null);
  618. tail = head;
  619. numPathsFound++;
  620. }
  621. tail.m_next = new MultistepExprHolder(owner2, stepCount, null);
  622. tail = tail.m_next;
  623. // Null out the occurance, so we don't have to test it again.
  624. paths.setElementAt(null, j);
  625. // foundFirst = true;
  626. numPathsFound++;
  627. }
  628. }
  629. }
  630. // Change all globals in xsl:templates, etc, to global vars no matter what.
  631. if((0 == numPathsFound) && isGlobal)
  632. {
  633. head = new MultistepExprHolder(firstOccuranceOwner, stepCount, null);
  634. numPathsFound++;
  635. }
  636. if(null != head)
  637. {
  638. ElemTemplateElement root = isGlobal ? psuedoVarRecipient : findCommonAncestor(head);
  639. LocPathIterator sharedIter = (LocPathIterator)head.m_exprOwner.getExpression();
  640. ElemVariable var = createPsuedoVarDecl(root, sharedIter, isGlobal);
  641. if(DIAGNOSE_MULTISTEPLIST)
  642. System.err.println("Created var: "+var.getName()+(isGlobal ? "(Global)" : ""));
  643. QName uniquePsuedoVarName = var.getName();
  644. while(null != head)
  645. {
  646. ExpressionOwner owner = head.m_exprOwner;
  647. if(DIAGNOSE_MULTISTEPLIST)
  648. diagnoseLineNumber(owner.getExpression());
  649. changeToVarRef(uniquePsuedoVarName, owner, paths, root);
  650. head = head.m_next;
  651. }
  652. // Replace the first occurance with the variable's XPath, so
  653. // that further reduction may take place if needed.
  654. paths.setElementAt(var.getSelect(), firstOccuranceIndex);
  655. }
  656. return numPathsFound;
  657. }
  658. /**
  659. * To be removed.
  660. */
  661. protected int oldFindAndEliminateRedundant(int start, int firstOccuranceIndex,
  662. ExpressionOwner firstOccuranceOwner,
  663. ElemTemplateElement psuedoVarRecipient,
  664. Vector paths)
  665. throws org.w3c.dom.DOMException
  666. {
  667. QName uniquePsuedoVarName = null;
  668. boolean foundFirst = false;
  669. int numPathsFound = 0;
  670. int n = paths.size();
  671. Expression expr1 = firstOccuranceOwner.getExpression();
  672. if(DEBUG)
  673. assertIsLocPathIterator(expr1, firstOccuranceOwner);
  674. boolean isGlobal = (paths == m_absPaths);
  675. LocPathIterator lpi = (LocPathIterator)expr1;
  676. for(int j = start; j < n; j++)
  677. {
  678. ExpressionOwner owner2 = (ExpressionOwner)paths.elementAt(j);
  679. if(null != owner2)
  680. {
  681. Expression expr2 = owner2.getExpression();
  682. boolean isEqual = expr2.deepEquals(lpi);
  683. if(isEqual)
  684. {
  685. LocPathIterator lpi2 = (LocPathIterator)expr2;
  686. if(!foundFirst)
  687. {
  688. foundFirst = true;
  689. // Insert variable decl into psuedoVarRecipient
  690. // We want to insert this into the first legitimate
  691. // position for a variable.
  692. ElemVariable var = createPsuedoVarDecl(psuedoVarRecipient, lpi, isGlobal);
  693. if(null == var)
  694. return 0;
  695. uniquePsuedoVarName = var.getName();
  696. changeToVarRef(uniquePsuedoVarName, firstOccuranceOwner,
  697. paths, psuedoVarRecipient);
  698. // Replace the first occurance with the variable's XPath, so
  699. // that further reduction may take place if needed.
  700. paths.setElementAt(var.getSelect(), firstOccuranceIndex);
  701. numPathsFound++;
  702. }
  703. changeToVarRef(uniquePsuedoVarName, owner2, paths, psuedoVarRecipient);
  704. // Null out the occurance, so we don't have to test it again.
  705. paths.setElementAt(null, j);
  706. // foundFirst = true;
  707. numPathsFound++;
  708. }
  709. }
  710. }
  711. // Change all globals in xsl:templates, etc, to global vars no matter what.
  712. if((0 == numPathsFound) && (paths == m_absPaths))
  713. {
  714. ElemVariable var = createPsuedoVarDecl(psuedoVarRecipient, lpi, true);
  715. if(null == var)
  716. return 0;
  717. uniquePsuedoVarName = var.getName();
  718. changeToVarRef(uniquePsuedoVarName, firstOccuranceOwner, paths, psuedoVarRecipient);
  719. paths.setElementAt(var.getSelect(), firstOccuranceIndex);
  720. numPathsFound++;
  721. }
  722. return numPathsFound;
  723. }
  724. /**
  725. * Count the steps in a given location path.
  726. *
  727. * @param lpi The location path iterator that owns the steps.
  728. * @return The number of steps in the given location path.
  729. */
  730. protected int countSteps(LocPathIterator lpi)
  731. {
  732. if(lpi instanceof WalkingIterator)
  733. {
  734. WalkingIterator wi = (WalkingIterator)lpi;
  735. AxesWalker aw = wi.getFirstWalker();
  736. int count = 0;
  737. while(null != aw)
  738. {
  739. count++;
  740. aw = aw.getNextWalker();
  741. }
  742. return count;
  743. }
  744. else
  745. return 1;
  746. }
  747. /**
  748. * Change the expression owned by the owner argument to a variable reference
  749. * of the given name.
  750. *
  751. * Warning: For global vars, this function relies on the variable declaration
  752. * to which it refers having been added just prior to this function being called,
  753. * so that the reference index can be determined from the size of the global variables
  754. * list minus one.
  755. *
  756. * @param varName The name of the variable which will be referenced.
  757. * @param owner The owner of the expression which will be replaced by a variable ref.
  758. * @param paths The paths list that the iterator came from, mainly to determine
  759. * if this is a local or global reduction.
  760. * @param psuedoVarRecipient The element within whose scope the variable is
  761. * being inserted, possibly a StylesheetRoot.
  762. */
  763. protected void changeToVarRef(QName varName, ExpressionOwner owner,
  764. Vector paths, ElemTemplateElement psuedoVarRecipient)
  765. {
  766. Variable varRef = (paths == m_absPaths) ? new VariableSafeAbsRef() : new Variable();
  767. varRef.setQName(varName);
  768. if(paths == m_absPaths)
  769. {
  770. StylesheetRoot root = (StylesheetRoot)psuedoVarRecipient;
  771. Vector globalVars = root.getVariablesAndParamsComposed();
  772. // Assume this operation is occuring just after the decl has
  773. // been added.
  774. varRef.setIndex(globalVars.size()-1);
  775. varRef.setIsGlobal(true);
  776. }
  777. owner.setExpression(varRef);
  778. }
  779. /**
  780. * Create a psuedo variable reference that will represent the
  781. * shared redundent XPath, and add it to the stylesheet.
  782. *
  783. * @param psuedoVarRecipient The broadest scope of where the variable
  784. * should be inserted, usually an xsl:template or xsl:for-each.
  785. * @param lpi The LocationPathIterator that the variable should represent.
  786. * @param isGlobal true if the paths are global.
  787. * @return The new psuedo var element.
  788. */
  789. protected ElemVariable createPsuedoVarDecl(
  790. ElemTemplateElement psuedoVarRecipient,
  791. LocPathIterator lpi, boolean isGlobal)
  792. throws org.w3c.dom.DOMException
  793. {
  794. QName uniquePsuedoVarName = new QName (PSUEDOVARNAMESPACE, "#"+m_uniquePsuedoVarID);
  795. m_uniquePsuedoVarID++;
  796. if(isGlobal)
  797. {
  798. return createGlobalPsuedoVarDecl(uniquePsuedoVarName,
  799. (StylesheetRoot)psuedoVarRecipient, lpi);
  800. }
  801. else
  802. return createLocalPsuedoVarDecl(uniquePsuedoVarName, psuedoVarRecipient, lpi);
  803. }
  804. /**
  805. * Create a psuedo variable reference that will represent the
  806. * shared redundent XPath, for a local reduction.
  807. *
  808. * @param uniquePsuedoVarName The name of the new variable.
  809. * @param stylesheetRoot The broadest scope of where the variable
  810. * should be inserted, which must be a StylesheetRoot element in this case.
  811. * @param lpi The LocationPathIterator that the variable should represent.
  812. * @return null if the decl was not created, otherwise the new Psuedo var
  813. * element.
  814. */
  815. protected ElemVariable createGlobalPsuedoVarDecl(QName uniquePsuedoVarName,
  816. StylesheetRoot stylesheetRoot,
  817. LocPathIterator lpi)
  818. throws org.w3c.dom.DOMException
  819. {
  820. ElemVariable psuedoVar = new ElemVariable();
  821. psuedoVar.setIsTopLevel(true);
  822. XPath xpath = new XPath(lpi);
  823. psuedoVar.setSelect(xpath);
  824. psuedoVar.setName(uniquePsuedoVarName);
  825. Vector globalVars = stylesheetRoot.getVariablesAndParamsComposed();
  826. psuedoVar.setIndex(globalVars.size());
  827. globalVars.addElement(psuedoVar);
  828. return psuedoVar;
  829. }
  830. /**
  831. * Create a psuedo variable reference that will represent the
  832. * shared redundent XPath, for a local reduction.
  833. *
  834. * @param uniquePsuedoVarName The name of the new variable.
  835. * @param psuedoVarRecipient The broadest scope of where the variable
  836. * should be inserted, usually an xsl:template or xsl:for-each.
  837. * @param lpi The LocationPathIterator that the variable should represent.
  838. * @param addToContext true if the decl should be added to psuedoVarRecipient.
  839. * @return null if the decl was not created, otherwise the new Psuedo var
  840. * element.
  841. */
  842. protected ElemVariable createLocalPsuedoVarDecl(QName uniquePsuedoVarName,
  843. ElemTemplateElement psuedoVarRecipient,
  844. LocPathIterator lpi)
  845. throws org.w3c.dom.DOMException
  846. {
  847. ElemVariable psuedoVar = new ElemVariablePsuedo();
  848. XPath xpath = new XPath(lpi);
  849. psuedoVar.setSelect(xpath);
  850. psuedoVar.setName(uniquePsuedoVarName);
  851. ElemVariable var = addVarDeclToElem(psuedoVarRecipient, lpi, psuedoVar);
  852. lpi.exprSetParent(var);
  853. return var;
  854. }
  855. /**
  856. * Add the given variable to the psuedoVarRecipient.
  857. */
  858. protected ElemVariable addVarDeclToElem(
  859. ElemTemplateElement psuedoVarRecipient,
  860. LocPathIterator lpi,
  861. ElemVariable psuedoVar)
  862. throws org.w3c.dom.DOMException
  863. {
  864. // Create psuedo variable element
  865. ElemTemplateElement ete = psuedoVarRecipient.getFirstChildElem();
  866. lpi.callVisitors(null, m_varNameCollector);
  867. // If the location path contains variables, we have to insert the
  868. // psuedo variable after the reference. (Otherwise, we want to
  869. // insert it as close as possible to the top, so we'll be sure
  870. // it is in scope for any other vars.
  871. if (m_varNameCollector.getVarCount() > 0)
  872. {
  873. ElemTemplateElement baseElem = getElemFromExpression(lpi);
  874. ElemVariable varElem = getPrevVariableElem(baseElem);
  875. while (null != varElem)
  876. {
  877. if (m_varNameCollector.doesOccur(varElem.getName()))
  878. {
  879. psuedoVarRecipient = varElem.getParentElem();
  880. ete = varElem.getNextSiblingElem();
  881. break;
  882. }
  883. varElem = getPrevVariableElem(varElem);
  884. }
  885. }
  886. if ((null != ete) && (Constants.ELEMNAME_PARAMVARIABLE == ete.getXSLToken()))
  887. {
  888. // Can't stick something in front of a param, so abandon! (see variable13.xsl)
  889. if(isParam(lpi))
  890. return null;
  891. while (null != ete)
  892. {
  893. ete = ete.getNextSiblingElem();
  894. if ((null != ete) && Constants.ELEMNAME_PARAMVARIABLE != ete.getXSLToken())
  895. break;
  896. }
  897. }
  898. psuedoVarRecipient.insertBefore(psuedoVar, ete);
  899. m_varNameCollector.reset();
  900. return psuedoVar;
  901. }
  902. /**
  903. * Tell if the expr param is contained within an xsl:param.
  904. */
  905. protected boolean isParam(ExpressionNode expr)
  906. {
  907. while(null != expr)
  908. {
  909. if(expr instanceof ElemTemplateElement)
  910. break;
  911. expr = expr.exprGetParent();
  912. }
  913. if(null != expr)
  914. {
  915. ElemTemplateElement ete = (ElemTemplateElement)expr;
  916. while(null != ete)
  917. {
  918. int type = ete.getXSLToken();
  919. switch(type)
  920. {
  921. case Constants.ELEMNAME_PARAMVARIABLE:
  922. return true;
  923. case Constants.ELEMNAME_TEMPLATE:
  924. case Constants.ELEMNAME_STYLESHEET:
  925. return false;
  926. }
  927. ete = ete.getParentElem();
  928. }
  929. }
  930. return false;
  931. }
  932. /**
  933. * Find the previous occurance of a xsl:variable. Stop
  934. * the search when a xsl:for-each, xsl:template, or xsl:stylesheet is
  935. * encountered.
  936. *
  937. * @param elem Should be non-null template element.
  938. * @return The first previous occurance of an xsl:variable or xsl:param,
  939. * or null if none is found.
  940. */
  941. protected ElemVariable getPrevVariableElem(ElemTemplateElement elem)
  942. {
  943. // This could be somewhat optimized. since getPreviousSiblingElem is a
  944. // fairly expensive operation.
  945. while(null != (elem = getPrevElementWithinContext(elem)))
  946. {
  947. int type = elem.getXSLToken();
  948. if((Constants.ELEMNAME_VARIABLE == type) ||
  949. (Constants.ELEMNAME_PARAMVARIABLE == type))
  950. {
  951. return (ElemVariable)elem;
  952. }
  953. }
  954. return null;
  955. }
  956. /**
  957. * Get the previous sibling or parent of the given template, stopping at
  958. * xsl:for-each, xsl:template, or xsl:stylesheet.
  959. *
  960. * @param elem Should be non-null template element.
  961. * @return previous sibling or parent, or null if previous is xsl:for-each,
  962. * xsl:template, or xsl:stylesheet.
  963. */
  964. protected ElemTemplateElement getPrevElementWithinContext(ElemTemplateElement elem)
  965. {
  966. ElemTemplateElement prev = elem.getPreviousSiblingElem();
  967. if(null == prev)
  968. prev = elem.getParentElem();
  969. if(null != prev)
  970. {
  971. int type = prev.getXSLToken();
  972. if((Constants.ELEMNAME_FOREACH == type) ||
  973. (Constants.ELEMNAME_TEMPLATE == type) ||
  974. (Constants.ELEMNAME_STYLESHEET == type))
  975. {
  976. prev = null;
  977. }
  978. }
  979. return prev;
  980. }
  981. /**
  982. * From an XPath expression component, get the ElemTemplateElement
  983. * owner.
  984. *
  985. * @param expr Should be static expression with proper parentage.
  986. * @return Valid ElemTemplateElement, or throw a runtime exception
  987. * if it is not found.
  988. */
  989. protected ElemTemplateElement getElemFromExpression(Expression expr)
  990. {
  991. ExpressionNode parent = expr.exprGetParent();
  992. while(null != parent)
  993. {
  994. if(parent instanceof ElemTemplateElement)
  995. return (ElemTemplateElement)parent;
  996. parent = parent.exprGetParent();
  997. }
  998. throw new RuntimeException(XSLMessages.createMessage(XSLTErrorResources.ER_ASSERT_NO_TEMPLATE_PARENT, null));
  999. // "Programmer's error! expr has no ElemTemplateElement parent!");
  1000. }
  1001. /**
  1002. * Tell if the given LocPathIterator is relative to an absolute path, i.e.
  1003. * in not dependent on the context.
  1004. *
  1005. * @return true if the LocPathIterator is not dependent on the context node.
  1006. */
  1007. public boolean isAbsolute(LocPathIterator path)
  1008. {
  1009. int analysis = path.getAnalysisBits();
  1010. boolean isAbs = (WalkerFactory.isSet(analysis, WalkerFactory.BIT_ROOT) ||
  1011. WalkerFactory.isSet(analysis, WalkerFactory.BIT_ANY_DESCENDANT_FROM_ROOT));
  1012. if(isAbs)
  1013. {
  1014. isAbs = m_absPathChecker.checkAbsolute(path);
  1015. }
  1016. return isAbs;
  1017. }
  1018. /**
  1019. * Visit a LocationPath.
  1020. * @param owner The owner of the expression, to which the expression can
  1021. * be reset if rewriting takes place.
  1022. * @param path The LocationPath object.
  1023. * @return true if the sub expressions should be traversed.
  1024. */
  1025. public boolean visitLocationPath(ExpressionOwner owner, LocPathIterator path)
  1026. {
  1027. // Don't optimize "." or single step variable paths.
  1028. // Both of these cases could use some further optimization by themselves.
  1029. if(path instanceof SelfIteratorNoPredicate)
  1030. {
  1031. return true;
  1032. }
  1033. else if(path instanceof WalkingIterator)
  1034. {
  1035. WalkingIterator wi = (WalkingIterator)path;
  1036. AxesWalker aw = wi.getFirstWalker();
  1037. if((aw instanceof FilterExprWalker) && (null == aw.getNextWalker()))
  1038. {
  1039. FilterExprWalker few = (FilterExprWalker)aw;
  1040. Expression exp = few.getInnerExpression();
  1041. if(exp instanceof Variable)
  1042. return true;
  1043. }
  1044. }
  1045. if (isAbsolute(path) && (null != m_absPaths))
  1046. {
  1047. if(DEBUG)
  1048. validateNewAddition(m_absPaths, owner, path);
  1049. m_absPaths.addElement(owner);
  1050. }
  1051. else if (m_isSameContext && (null != m_paths))
  1052. {
  1053. if(DEBUG)
  1054. validateNewAddition(m_paths, owner, path);
  1055. m_paths.addElement(owner);
  1056. }
  1057. return true;
  1058. }
  1059. /**
  1060. * Visit a predicate within a location path. Note that there isn't a
  1061. * proper unique component for predicates, and that the expression will
  1062. * be called also for whatever type Expression is.
  1063. *
  1064. * @param owner The owner of the expression, to which the expression can
  1065. * be reset if rewriting takes place.
  1066. * @param pred The predicate object.
  1067. * @return true if the sub expressions should be traversed.
  1068. */
  1069. public boolean visitPredicate(ExpressionOwner owner, Expression pred)
  1070. {
  1071. boolean savedIsSame = m_isSameContext;
  1072. m_isSameContext = false;
  1073. // Any further down, just collect the absolute paths.
  1074. pred.callVisitors(owner, this);
  1075. m_isSameContext = savedIsSame;
  1076. // We've already gone down the subtree, so don't go have the caller
  1077. // go any further.
  1078. return false;
  1079. }
  1080. /**
  1081. * Visit an XSLT top-level instruction.
  1082. *
  1083. * @param elem The xsl instruction element object.
  1084. * @return true if the sub expressions should be traversed.
  1085. */
  1086. boolean visitTopLevelInstruction(ElemTemplateElement elem)
  1087. {
  1088. int type = elem.getXSLToken();
  1089. switch(type)
  1090. {
  1091. case Constants.ELEMNAME_TEMPLATE :
  1092. return visitInstruction(elem);
  1093. default:
  1094. return true;
  1095. }
  1096. }
  1097. /**
  1098. * Visit an XSLT instruction. Any element that isn't called by one
  1099. * of the other visit methods, will be called by this method.
  1100. *
  1101. * @param elem The xsl instruction element object.
  1102. * @return true if the sub expressions should be traversed.
  1103. */
  1104. boolean visitInstruction(ElemTemplateElement elem)
  1105. {
  1106. int type = elem.getXSLToken();
  1107. switch (type)
  1108. {
  1109. case Constants.ELEMNAME_CALLTEMPLATE :
  1110. case Constants.ELEMNAME_TEMPLATE :
  1111. case Constants.ELEMNAME_FOREACH :
  1112. {
  1113. // Just get the select value.
  1114. if(type == Constants.ELEMNAME_FOREACH)
  1115. {
  1116. ElemForEach efe = (ElemForEach) elem;
  1117. Expression select = efe.getSelect();
  1118. select.callVisitors(efe, this);
  1119. }
  1120. Vector savedPaths = m_paths;
  1121. m_paths = new Vector();
  1122. // Visit children. Call the superclass callChildVisitors, because
  1123. // we don't want to visit the xsl:for-each select attribute, or, for
  1124. // that matter, the xsl:template's match attribute.
  1125. elem.callChildVisitors(this, false);
  1126. eleminateRedundentLocals(elem);
  1127. m_paths = savedPaths;
  1128. // select.callVisitors(efe, this);
  1129. return false;
  1130. }
  1131. case Constants.ELEMNAME_NUMBER :
  1132. case Constants.ELEMNAME_SORT :
  1133. // Just collect absolute paths until and unless we can fully
  1134. // analyze these cases.
  1135. boolean savedIsSame = m_isSameContext;
  1136. m_isSameContext = false;
  1137. elem.callChildVisitors(this);
  1138. m_isSameContext = savedIsSame;
  1139. return false;
  1140. default :
  1141. return true;
  1142. }
  1143. }
  1144. // ==== DIAGNOSTIC AND DEBUG FUNCTIONS ====
  1145. /**
  1146. * Print out to std err the number of paths reduced.
  1147. */
  1148. protected void diagnoseNumPaths(Vector paths, int numPathsEliminated,
  1149. int numUniquePathsEliminated)
  1150. {
  1151. if (numPathsEliminated > 0)
  1152. {
  1153. if(paths == m_paths)
  1154. {
  1155. System.err.println("Eliminated " + numPathsEliminated + " total paths!");
  1156. System.err.println(
  1157. "Consolodated " + numUniquePathsEliminated + " redundent paths!");
  1158. }
  1159. else
  1160. {
  1161. System.err.println("Eliminated " + numPathsEliminated + " total global paths!");
  1162. System.err.println(
  1163. "Consolodated " + numUniquePathsEliminated + " redundent global paths!");
  1164. }
  1165. }
  1166. }
  1167. /**
  1168. * Assert that the expression is a LocPathIterator, and, if
  1169. * not, try to give some diagnostic info.
  1170. */
  1171. private final void assertIsLocPathIterator(Expression expr1, ExpressionOwner eo)
  1172. throws RuntimeException
  1173. {
  1174. if(!(expr1 instanceof LocPathIterator))
  1175. {
  1176. String errMsg;
  1177. if(expr1 instanceof Variable)
  1178. {
  1179. errMsg = "Programmer's assertion: expr1 not an iterator: "+
  1180. ((Variable)expr1).getQName();
  1181. }
  1182. else
  1183. {
  1184. errMsg = "Programmer's assertion: expr1 not an iterator: "+
  1185. expr1.getClass().getName();
  1186. }
  1187. throw new RuntimeException(errMsg + ", "+
  1188. eo.getClass().getName()+" "+
  1189. expr1.exprGetParent());
  1190. }
  1191. }
  1192. /**
  1193. * Validate some assumptions about the new LocPathIterator and it's
  1194. * owner and the state of the list.
  1195. */
  1196. private static void validateNewAddition(Vector paths, ExpressionOwner owner,
  1197. LocPathIterator path)
  1198. throws RuntimeException
  1199. {
  1200. assertion(owner.getExpression() == path, "owner.getExpression() != path!!!");
  1201. int n = paths.size();
  1202. // There should never be any duplicates in the list!
  1203. for(int i = 0; i < n; i++)
  1204. {
  1205. ExpressionOwner ew = (ExpressionOwner)paths.elementAt(i);
  1206. assertion(ew != owner, "duplicate owner on the list!!!");
  1207. assertion(ew.getExpression() != path, "duplicate expression on the list!!!");
  1208. }
  1209. }
  1210. /**
  1211. * Simple assertion.
  1212. */
  1213. protected static void assertion(boolean b, String msg)
  1214. {
  1215. if(!b)
  1216. {
  1217. throw new RuntimeException(XSLMessages.createMessage(XSLTErrorResources.ER_ASSERT_REDUNDENT_EXPR_ELIMINATOR, new Object[]{msg}));
  1218. // "Programmer's assertion in RundundentExprEliminator: "+msg);
  1219. }
  1220. }
  1221. /**
  1222. * Since we want to sort multistep expressions by length, use
  1223. * a linked list with elements of type MultistepExprHolder.
  1224. */
  1225. class MultistepExprHolder implements Cloneable
  1226. {
  1227. ExpressionOwner m_exprOwner; // Will change to null once we have processed this item.
  1228. final int m_stepCount;
  1229. MultistepExprHolder m_next;
  1230. /**
  1231. * Clone this object.
  1232. */
  1233. public Object clone()
  1234. throws CloneNotSupportedException
  1235. {
  1236. return super.clone();
  1237. }
  1238. /**
  1239. * Create a MultistepExprHolder.
  1240. *
  1241. * @param exprOwner the owner of the expression we are holding.
  1242. * It must hold a LocationPathIterator.
  1243. * @param stepCount The number of steps in the location path.
  1244. */
  1245. MultistepExprHolder(ExpressionOwner exprOwner, int stepCount, MultistepExprHolder next)
  1246. {
  1247. m_exprOwner = exprOwner;
  1248. assertion(null != m_exprOwner, "exprOwner can not be null!");
  1249. m_stepCount = stepCount;
  1250. m_next = next;
  1251. }
  1252. /**
  1253. * Add a new MultistepExprHolder in sorted order in the list.
  1254. *
  1255. * @param exprOwner the owner of the expression we are holding.
  1256. * It must hold a LocationPathIterator.
  1257. * @param stepCount The number of steps in the location path.
  1258. * @return The new head of the linked list.
  1259. */
  1260. MultistepExprHolder addInSortedOrder(ExpressionOwner exprOwner, int stepCount)
  1261. {
  1262. MultistepExprHolder first = this;
  1263. MultistepExprHolder next = this;
  1264. MultistepExprHolder prev = null;
  1265. while(null != next)
  1266. {
  1267. if(stepCount >= next.m_stepCount)
  1268. {
  1269. MultistepExprHolder newholder = new MultistepExprHolder(exprOwner, stepCount, next);
  1270. if(null == prev)
  1271. first = newholder;
  1272. else
  1273. prev.m_next = newholder;
  1274. return first;
  1275. }
  1276. prev = next;
  1277. next = next.m_next;
  1278. }
  1279. prev.m_next = new MultistepExprHolder(exprOwner, stepCount, null);
  1280. return first;
  1281. }
  1282. /**
  1283. * Remove the given element from the list. 'this' should
  1284. * be the head of the list. If the item to be removed is not
  1285. * found, an assertion will be made.
  1286. *
  1287. * @param itemToRemove The item to remove from the list.
  1288. * @return The head of the list, which may have changed if itemToRemove
  1289. * is the same as this element. Null if the item to remove is the
  1290. * only item in the list.
  1291. */
  1292. MultistepExprHolder unlink(MultistepExprHolder itemToRemove)
  1293. {
  1294. MultistepExprHolder first = this;
  1295. MultistepExprHolder next = this;
  1296. MultistepExprHolder prev = null;
  1297. while(null != next)
  1298. {
  1299. if(next == itemToRemove)
  1300. {
  1301. if(null == prev)
  1302. first = next.m_next;
  1303. else
  1304. prev.m_next = next.m_next;
  1305. next.m_next = null;
  1306. return first;
  1307. }
  1308. prev = next;
  1309. next = next.m_next;
  1310. }
  1311. assertion(false, "unlink failed!!!");
  1312. return null;
  1313. }
  1314. /**
  1315. * Get the number of linked list items.
  1316. */
  1317. int getLength()
  1318. {
  1319. int count = 0;
  1320. MultistepExprHolder next = this;
  1321. while(null != next)
  1322. {
  1323. count++;
  1324. next = next.m_next;
  1325. }
  1326. return count;
  1327. }
  1328. /**
  1329. * Print diagnostics out for the multistep list.
  1330. */
  1331. protected void diagnose()
  1332. {
  1333. System.err.print("Found multistep iterators: " + this.getLength() + " ");
  1334. MultistepExprHolder next = this;
  1335. while (null != next)
  1336. {
  1337. System.err.print("" + next.m_stepCount);
  1338. next = next.m_next;
  1339. if (null != next)
  1340. System.err.print(", ");
  1341. }
  1342. System.err.println();
  1343. }
  1344. }
  1345. }