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