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  */
21 package org.apache.xalan.templates;
22 
23 import java.util.Vector;
24 
25 import org.apache.xalan.res.XSLMessages;
26 import org.apache.xalan.res.XSLTErrorResources;
27 import org.apache.xml.utils.QName;
28 import org.apache.xml.utils.WrappedRuntimeException;
29 import org.apache.xpath.Expression;
30 import org.apache.xpath.ExpressionNode;
31 import org.apache.xpath.ExpressionOwner;
32 import org.apache.xpath.XPath;
33 import org.apache.xpath.axes.AxesWalker;
34 import org.apache.xpath.axes.FilterExprIteratorSimple;
35 import org.apache.xpath.axes.FilterExprWalker;
36 import org.apache.xpath.axes.LocPathIterator;
37 import org.apache.xpath.axes.SelfIteratorNoPredicate;
38 import org.apache.xpath.axes.WalkerFactory;
39 import org.apache.xpath.axes.WalkingIterator;
40 import org.apache.xpath.operations.Variable;
41 import 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  */
49 public 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    */
RedundentExprEliminator()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    */
eleminateRedundentLocals(ElemTemplateElement psuedoVarRecipient)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    */
eleminateRedundentGlobals(StylesheetRoot stylesheet)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    */
eleminateRedundent(ElemTemplateElement psuedoVarRecipient, Vector paths)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    */
eleminateSharedPartialPaths(ElemTemplateElement psuedoVarRecipient, Vector paths)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    */
matchAndEliminatePartialPaths(MultistepExprHolder testee, MultistepExprHolder head, boolean isGlobal, int lengthToTest, ElemTemplateElement varScope)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    */
partialIsVariable(MultistepExprHolder testee, int lengthToTest)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    */
diagnoseLineNumber(Expression expr)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    */
findCommonAncestor(MultistepExprHolder head)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    */
isNotSameAsOwner(MultistepExprHolder head, ElemTemplateElement ete)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    */
countAncestors(ElemTemplateElement elem)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    */
diagnoseMultistepList( int matchCount, int lengthToTest, boolean isGlobal)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    */
changePartToRef(final QName uniquePseudoVarName, WalkingIterator wi, final int numSteps, final boolean isGlobal)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    */
createIteratorFromSteps(final WalkingIterator wi, int numSteps)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    */
stepsEqual(WalkingIterator iter1, WalkingIterator iter2, int numSteps)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    */
createMultistepExprList(Vector paths)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    */
findAndEliminateRedundant(int start, int firstOccuranceIndex, ExpressionOwner firstOccuranceOwner, ElemTemplateElement psuedoVarRecipient, Vector paths)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    */
oldFindAndEliminateRedundant(int start, int firstOccuranceIndex, ExpressionOwner firstOccuranceOwner, ElemTemplateElement psuedoVarRecipient, Vector paths)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    */
countSteps(LocPathIterator lpi)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    */
changeToVarRef(QName varName, ExpressionOwner owner, Vector paths, ElemTemplateElement psuedoVarRecipient)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 
getPseudoVarID()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    */
createPseudoVarDecl( ElemTemplateElement psuedoVarRecipient, LocPathIterator lpi, boolean isGlobal)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    */
createGlobalPseudoVarDecl(QName uniquePseudoVarName, StylesheetRoot stylesheetRoot, LocPathIterator lpi)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    */
createLocalPseudoVarDecl(QName uniquePseudoVarName, ElemTemplateElement psuedoVarRecipient, LocPathIterator lpi)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    */
addVarDeclToElem( ElemTemplateElement psuedoVarRecipient, LocPathIterator lpi, ElemVariable psuedoVar)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    */
isParam(ExpressionNode expr)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    */
getPrevVariableElem(ElemTemplateElement elem)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    */
getPrevElementWithinContext(ElemTemplateElement elem)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    */
getElemFromExpression(Expression expr)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    */
isAbsolute(LocPathIterator path)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    */
visitLocationPath(ExpressionOwner owner, LocPathIterator path)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    */
visitPredicate(ExpressionOwner owner, Expression pred)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    */
visitTopLevelInstruction(ElemTemplateElement elem)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    */
visitInstruction(ElemTemplateElement elem)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    */
diagnoseNumPaths(Vector paths, int numPathsEliminated, int numUniquePathsEliminated)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    */
assertIsLocPathIterator(Expression expr1, ExpressionOwner eo)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    */
validateNewAddition(Vector paths, ExpressionOwner owner, LocPathIterator path)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    */
assertion(boolean b, String msg)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 	 */
clone()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 	 */
MultistepExprHolder(ExpressionOwner exprOwner, int stepCount, MultistepExprHolder next)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 	 */
addInSortedOrder(ExpressionOwner exprOwner, int stepCount)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 	 */
unlink(MultistepExprHolder itemToRemove)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 	 */
getLength()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      */
diagnose()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