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
10
* http://www.apache.org/licenses/LICENSE-2.0
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.
19
* $Id: RedundentExprEliminator.java,v 1.2 2009/12/10 03:18:29 matthewoliver Exp $
21
package org.apache.xalan.templates;
23
import java.util.Vector;
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;
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.
49
public class RedundentExprEliminator extends XSLTVisitor
53
boolean m_isSameContext;
54
AbsPathChecker m_absPathChecker = new AbsPathChecker();
56
private static int m_uniquePseudoVarID = 1;
57
static final String PSUEDOVARNAMESPACE = Constants.S_VENDORURL+"/xalan/psuedovar";
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;
64
* So we can reuse it over and over again.
66
VarNameCollector m_varNameCollector = new VarNameCollector();
69
* Construct a RedundentExprEliminator.
71
public RedundentExprEliminator()
73
m_isSameContext = true;
74
m_absPaths = new Vector();
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.
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.
90
public void eleminateRedundentLocals(ElemTemplateElement psuedoVarRecipient)
92
eleminateRedundent(psuedoVarRecipient, m_paths);
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.
103
public void eleminateRedundentGlobals(StylesheetRoot stylesheet)
105
eleminateRedundent(stylesheet, m_absPaths);
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.
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.
121
protected void eleminateRedundent(ElemTemplateElement psuedoVarRecipient, Vector paths)
123
int n = paths.size();
124
int numPathsEliminated = 0;
125
int numUniquePathsEliminated = 0;
126
for (int i = 0; i < n; i++)
128
ExpressionOwner owner = (ExpressionOwner) paths.elementAt(i);
131
int found = findAndEliminateRedundant(i + 1, i, owner, psuedoVarRecipient, paths);
133
numUniquePathsEliminated++;
134
numPathsEliminated += found;
138
eleminateSharedPartialPaths(psuedoVarRecipient, paths);
140
if(DIAGNOSE_NUM_PATHS_REDUCED)
141
diagnoseNumPaths(paths, numPathsEliminated, numUniquePathsEliminated);
145
* Eliminate the shared partial paths in the expression list.
147
* @param psuedoVarRecipient The recipient of the psuedo vars.
149
* @param paths A vector of paths that hold ExpressionOwner objects,
150
* which must yield LocationPathIterators.
152
protected void eleminateSharedPartialPaths(ElemTemplateElement psuedoVarRecipient, Vector paths)
154
MultistepExprHolder list = createMultistepExprList(paths);
157
if(DIAGNOSE_MULTISTEPLIST)
160
boolean isGlobal = (paths == m_absPaths);
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--)
167
MultistepExprHolder next = list;
170
if(next.m_stepCount < i)
172
list = matchAndEliminatePartialPaths(next, list, isGlobal, i, psuedoVarRecipient);
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.
184
* @return The head of the list, which may have changed.
186
protected MultistepExprHolder matchAndEliminatePartialPaths(MultistepExprHolder testee,
187
MultistepExprHolder head,
190
ElemTemplateElement varScope)
192
if(null == testee.m_exprOwner)
195
// Start with the longest possible match, and move down.
196
WalkingIterator iter1 = (WalkingIterator) testee.m_exprOwner.getExpression();
197
if(partialIsVariable(testee, lengthToTest))
199
MultistepExprHolder matchedPaths = null;
200
MultistepExprHolder matchedPathsTail = null;
201
MultistepExprHolder meh = head;
204
if ((meh != testee) && (null != meh.m_exprOwner))
206
WalkingIterator iter2 = (WalkingIterator) meh.m_exprOwner.getExpression();
207
if (stepsEqual(iter1, iter2, lengthToTest))
209
if (null == matchedPaths)
213
matchedPaths = (MultistepExprHolder)testee.clone();
214
testee.m_exprOwner = null; // So it won't be processed again.
216
catch(CloneNotSupportedException cnse){}
217
matchedPathsTail = matchedPaths;
218
matchedPathsTail.m_next = null;
223
matchedPathsTail.m_next = (MultistepExprHolder)meh.clone();
224
meh.m_exprOwner = null; // So it won't be processed again.
226
catch(CloneNotSupportedException cnse){}
227
matchedPathsTail = matchedPathsTail.m_next;
228
matchedPathsTail.m_next = null;
235
if(null != matchedPaths)
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)
245
ExpressionOwner owner = matchedPaths.m_exprOwner;
246
WalkingIterator iter = (WalkingIterator)owner.getExpression();
248
if(DIAGNOSE_MULTISTEPLIST)
249
diagnoseLineNumber(iter);
251
LocPathIterator newIter2 =
252
changePartToRef(var.getName(), iter, lengthToTest, isGlobal);
253
owner.setExpression(newIter2);
255
matchedPaths = matchedPaths.m_next;
259
if(DIAGNOSE_MULTISTEPLIST)
260
diagnoseMultistepList(matchCount, lengthToTest, isGlobal);
265
* Check if results of partial reduction will just be a variable, in
266
* which case, skip it.
268
boolean partialIsVariable(MultistepExprHolder testee, int lengthToTest)
270
if(1 == lengthToTest)
272
WalkingIterator wi = (WalkingIterator)testee.m_exprOwner.getExpression();
273
if(wi.getFirstWalker() instanceof FilterExprWalker)
280
* Tell what line number belongs to a given expression.
282
protected void diagnoseLineNumber(Expression expr)
284
ElemTemplateElement e = getElemFromExpression(expr);
285
System.err.println(" " + e.getSystemId() + " Line " + e.getLineNumber());
289
* Given a linked list of expressions, find the common ancestor that is
290
* suitable for holding a psuedo variable for shared access.
292
protected ElemTemplateElement findCommonAncestor(MultistepExprHolder head)
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];
302
// Loop through, getting the parent elements, and counting the
304
MultistepExprHolder next = head;
305
int shortestAncestorCount = 10000;
306
for(int i = 0; i < numExprs; i++)
308
ElemTemplateElement elem =
309
getElemFromExpression(next.m_exprOwner.getExpression());
311
int numAncestors = countAncestors(elem);
312
ancestorCounts[i] = numAncestors;
313
if(numAncestors < shortestAncestorCount)
315
shortestAncestorCount = numAncestors;
320
// Now loop through and "correct" the elements that have more ancestors.
321
for(int i = 0; i < numExprs; i++)
323
if(ancestorCounts[i] > shortestAncestorCount)
325
int numStepCorrection = ancestorCounts[i] - shortestAncestorCount;
326
for(int j = 0; j < numStepCorrection; j++)
328
elems[i] = elems[i].getParentElem();
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)
338
boolean areEqual = true;
340
for(int i = 1; i < numExprs; i++)
342
if(first != elems[i])
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())
352
if(DIAGNOSE_MULTISTEPLIST)
354
System.err.print(first.getClass().getName());
355
System.err.println(" at " + first.getSystemId() + " Line " + first.getLineNumber());
360
for(int i = 0; i < numExprs; i++)
362
elems[i] = elems[i].getParentElem();
366
assertion(false, "Could not find common ancestor!!!");
371
* Find out if the given ElemTemplateElement is not the same as one of
372
* the ElemTemplateElement owners of the expressions.
374
* @param head Head of linked list of expression owners.
375
* @param ete The ElemTemplateElement that is a candidate for a psuedo
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.
382
protected boolean isNotSameAsOwner(MultistepExprHolder head, ElemTemplateElement ete)
384
MultistepExprHolder next = head;
387
ElemTemplateElement elemOwner = getElemFromExpression(next.m_exprOwner.getExpression());
396
* Count the number of ancestors that a ElemTemplateElement has.
398
* @param elem An representation of an element in an XSLT stylesheet.
399
* @return The number of ancestors of elem (including the element itself).
401
protected int countAncestors(ElemTemplateElement elem)
407
elem = elem.getParentElem();
413
* Print out diagnostics about partial multistep evaluation.
415
protected void diagnoseMultistepList(
423
"Found multistep matches: " + matchCount + ", " + lengthToTest + " length");
425
System.err.println(" (global)");
427
System.err.println();
432
* Change a given number of steps to a single variable reference.
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.
439
protected LocPathIterator changePartToRef(final QName uniquePseudoVarName, WalkingIterator wi,
440
final int numSteps, final boolean isGlobal)
442
Variable var = new Variable();
443
var.setQName(uniquePseudoVarName);
444
var.setIsGlobal(isGlobal);
446
{ ElemTemplateElement elem = getElemFromExpression(wi);
447
StylesheetRoot root = elem.getStylesheetRoot();
448
Vector vars = root.getVariablesAndParamsComposed();
449
var.setIndex(vars.size()-1);
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++)
456
assertion(null != walker, "Walker should not be null!");
457
walker = walker.getNextWalker();
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);
473
FilterExprIteratorSimple feis = new FilterExprIteratorSimple(var);
474
feis.exprSetParent(wi.exprGetParent());
480
* Create a new WalkingIterator from the steps in another WalkingIterator.
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
485
* @return The new iterator.
487
protected WalkingIterator createIteratorFromSteps(final WalkingIterator wi, int numSteps)
489
WalkingIterator newIter = new WalkingIterator(wi.getPrefixResolver());
492
AxesWalker walker = (AxesWalker)wi.getFirstWalker().clone();
493
newIter.setFirstWalker(walker);
494
walker.setLocPathIterator(newIter);
495
for(int i = 1; i < numSteps; i++)
497
AxesWalker next = (AxesWalker)walker.getNextWalker().clone();
498
walker.setNextWalker(next);
499
next.setLocPathIterator(newIter);
502
walker.setNextWalker(null);
504
catch(CloneNotSupportedException cnse)
506
throw new WrappedRuntimeException(cnse);
512
* Compare a given number of steps between two iterators, to see if they are equal.
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.
520
protected boolean stepsEqual(WalkingIterator iter1, WalkingIterator iter2,
523
AxesWalker aw1 = iter1.getFirstWalker();
524
AxesWalker aw2 = iter2.getFirstWalker();
526
for(int i = 0; (i < numSteps); i++)
528
if((null == aw1) || (null == aw2))
531
if(!aw1.deepEquals(aw2))
534
aw1 = aw1.getNextWalker();
535
aw2 = aw2.getNextWalker();
538
assertion((null != aw1) || (null != aw2), "Total match is incorrect!");
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.
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.
554
protected MultistepExprHolder createMultistepExprList(Vector paths)
556
MultistepExprHolder first = null;
557
int n = paths.size();
558
for(int i = 0; i < n; i++)
560
ExpressionOwner eo = (ExpressionOwner)paths.elementAt(i);
564
// Assuming location path iterators should be OK.
565
LocPathIterator lpi = (LocPathIterator)eo.getExpression();
566
int numPaths = countSteps(lpi);
570
first = new MultistepExprHolder(eo, numPaths, null);
572
first = first.addInSortedOrder(eo, numPaths);
576
if((null == first) || (first.getLength() <= 1))
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.
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.
594
* @return The number of expression occurances that were modified.
596
protected int findAndEliminateRedundant(int start, int firstOccuranceIndex,
597
ExpressionOwner firstOccuranceOwner,
598
ElemTemplateElement psuedoVarRecipient,
600
throws org.w3c.dom.DOMException
602
MultistepExprHolder head = null;
603
MultistepExprHolder tail = null;
604
int numPathsFound = 0;
605
int n = paths.size();
607
Expression expr1 = firstOccuranceOwner.getExpression();
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++)
615
ExpressionOwner owner2 = (ExpressionOwner)paths.elementAt(j);
618
Expression expr2 = owner2.getExpression();
619
boolean isEqual = expr2.deepEquals(lpi);
622
LocPathIterator lpi2 = (LocPathIterator)expr2;
625
head = new MultistepExprHolder(firstOccuranceOwner, stepCount, null);
629
tail.m_next = new MultistepExprHolder(owner2, stepCount, null);
632
// Null out the occurance, so we don't have to test it again.
633
paths.setElementAt(null, j);
635
// foundFirst = true;
641
// Change all globals in xsl:templates, etc, to global vars no matter what.
642
if((0 == numPathsFound) && isGlobal)
644
head = new MultistepExprHolder(firstOccuranceOwner, stepCount, null);
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();
658
ExpressionOwner owner = head.m_exprOwner;
659
if(DIAGNOSE_MULTISTEPLIST)
660
diagnoseLineNumber(owner.getExpression());
661
changeToVarRef(uniquePseudoVarName, owner, paths, root);
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);
669
return numPathsFound;
675
protected int oldFindAndEliminateRedundant(int start, int firstOccuranceIndex,
676
ExpressionOwner firstOccuranceOwner,
677
ElemTemplateElement psuedoVarRecipient,
679
throws org.w3c.dom.DOMException
681
QName uniquePseudoVarName = null;
682
boolean foundFirst = false;
683
int numPathsFound = 0;
684
int n = paths.size();
685
Expression expr1 = firstOccuranceOwner.getExpression();
687
assertIsLocPathIterator(expr1, firstOccuranceOwner);
688
boolean isGlobal = (paths == m_absPaths);
689
LocPathIterator lpi = (LocPathIterator)expr1;
690
for(int j = start; j < n; j++)
692
ExpressionOwner owner2 = (ExpressionOwner)paths.elementAt(j);
695
Expression expr2 = owner2.getExpression();
696
boolean isEqual = expr2.deepEquals(lpi);
699
LocPathIterator lpi2 = (LocPathIterator)expr2;
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);
709
uniquePseudoVarName = var.getName();
711
changeToVarRef(uniquePseudoVarName, firstOccuranceOwner,
712
paths, psuedoVarRecipient);
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);
720
changeToVarRef(uniquePseudoVarName, owner2, paths, psuedoVarRecipient);
722
// Null out the occurance, so we don't have to test it again.
723
paths.setElementAt(null, j);
725
// foundFirst = true;
731
// Change all globals in xsl:templates, etc, to global vars no matter what.
732
if((0 == numPathsFound) && (paths == m_absPaths))
734
ElemVariable var = createPseudoVarDecl(psuedoVarRecipient, lpi, true);
737
uniquePseudoVarName = var.getName();
738
changeToVarRef(uniquePseudoVarName, firstOccuranceOwner, paths, psuedoVarRecipient);
739
paths.setElementAt(var.getSelect(), firstOccuranceIndex);
742
return numPathsFound;
746
* Count the steps in a given location path.
748
* @param lpi The location path iterator that owns the steps.
749
* @return The number of steps in the given location path.
751
protected int countSteps(LocPathIterator lpi)
753
if(lpi instanceof WalkingIterator)
755
WalkingIterator wi = (WalkingIterator)lpi;
756
AxesWalker aw = wi.getFirstWalker();
761
aw = aw.getNextWalker();
770
* Change the expression owned by the owner argument to a variable reference
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
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.
785
protected void changeToVarRef(QName varName, ExpressionOwner owner,
786
Vector paths, ElemTemplateElement psuedoVarRecipient)
788
Variable varRef = (paths == m_absPaths) ? new VariableSafeAbsRef() : new Variable();
789
varRef.setQName(varName);
790
if(paths == m_absPaths)
792
StylesheetRoot root = (StylesheetRoot)psuedoVarRecipient;
793
Vector globalVars = root.getVariablesAndParamsComposed();
794
// Assume this operation is occuring just after the decl has
796
varRef.setIndex(globalVars.size()-1);
797
varRef.setIsGlobal(true);
799
owner.setExpression(varRef);
802
private synchronized static int getPseudoVarID(){
803
return m_uniquePseudoVarID++;
807
* Create a psuedo variable reference that will represent the
808
* shared redundent XPath, and add it to the stylesheet.
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.
816
protected ElemVariable createPseudoVarDecl(
817
ElemTemplateElement psuedoVarRecipient,
818
LocPathIterator lpi, boolean isGlobal)
819
throws org.w3c.dom.DOMException
821
QName uniquePseudoVarName = new QName (PSUEDOVARNAMESPACE, "#"+getPseudoVarID());
825
return createGlobalPseudoVarDecl(uniquePseudoVarName,
826
(StylesheetRoot)psuedoVarRecipient, lpi);
829
return createLocalPseudoVarDecl(uniquePseudoVarName, psuedoVarRecipient, lpi);
833
* Create a psuedo variable reference that will represent the
834
* shared redundent XPath, for a local reduction.
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
843
protected ElemVariable createGlobalPseudoVarDecl(QName uniquePseudoVarName,
844
StylesheetRoot stylesheetRoot,
846
throws org.w3c.dom.DOMException
848
ElemVariable psuedoVar = new ElemVariable();
849
psuedoVar.setIsTopLevel(true);
850
XPath xpath = new XPath(lpi);
851
psuedoVar.setSelect(xpath);
852
psuedoVar.setName(uniquePseudoVarName);
854
Vector globalVars = stylesheetRoot.getVariablesAndParamsComposed();
855
psuedoVar.setIndex(globalVars.size());
856
globalVars.addElement(psuedoVar);
864
* Create a psuedo variable reference that will represent the
865
* shared redundent XPath, for a local reduction.
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
874
protected ElemVariable createLocalPseudoVarDecl(QName uniquePseudoVarName,
875
ElemTemplateElement psuedoVarRecipient,
877
throws org.w3c.dom.DOMException
879
ElemVariable psuedoVar = new ElemVariablePsuedo();
881
XPath xpath = new XPath(lpi);
882
psuedoVar.setSelect(xpath);
883
psuedoVar.setName(uniquePseudoVarName);
885
ElemVariable var = addVarDeclToElem(psuedoVarRecipient, lpi, psuedoVar);
887
lpi.exprSetParent(var);
893
* Add the given variable to the psuedoVarRecipient.
895
protected ElemVariable addVarDeclToElem(
896
ElemTemplateElement psuedoVarRecipient,
898
ElemVariable psuedoVar)
899
throws org.w3c.dom.DOMException
901
// Create psuedo variable element
902
ElemTemplateElement ete = psuedoVarRecipient.getFirstChildElem();
904
lpi.callVisitors(null, m_varNameCollector);
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)
912
ElemTemplateElement baseElem = getElemFromExpression(lpi);
913
ElemVariable varElem = getPrevVariableElem(baseElem);
914
while (null != varElem)
916
if (m_varNameCollector.doesOccur(varElem.getName()))
918
psuedoVarRecipient = varElem.getParentElem();
919
ete = varElem.getNextSiblingElem();
922
varElem = getPrevVariableElem(varElem);
926
if ((null != ete) && (Constants.ELEMNAME_PARAMVARIABLE == ete.getXSLToken()))
928
// Can't stick something in front of a param, so abandon! (see variable13.xsl)
934
ete = ete.getNextSiblingElem();
935
if ((null != ete) && Constants.ELEMNAME_PARAMVARIABLE != ete.getXSLToken())
939
psuedoVarRecipient.insertBefore(psuedoVar, ete);
940
m_varNameCollector.reset();
945
* Tell if the expr param is contained within an xsl:param.
947
protected boolean isParam(ExpressionNode expr)
951
if(expr instanceof ElemTemplateElement)
953
expr = expr.exprGetParent();
957
ElemTemplateElement ete = (ElemTemplateElement)expr;
960
int type = ete.getXSLToken();
963
case Constants.ELEMNAME_PARAMVARIABLE:
965
case Constants.ELEMNAME_TEMPLATE:
966
case Constants.ELEMNAME_STYLESHEET:
969
ete = ete.getParentElem();
977
* Find the previous occurance of a xsl:variable. Stop
978
* the search when a xsl:for-each, xsl:template, or xsl:stylesheet is
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.
985
protected ElemVariable getPrevVariableElem(ElemTemplateElement elem)
987
// This could be somewhat optimized. since getPreviousSiblingElem is a
988
// fairly expensive operation.
989
while(null != (elem = getPrevElementWithinContext(elem)))
991
int type = elem.getXSLToken();
993
if((Constants.ELEMNAME_VARIABLE == type) ||
994
(Constants.ELEMNAME_PARAMVARIABLE == type))
996
return (ElemVariable)elem;
1003
* Get the previous sibling or parent of the given template, stopping at
1004
* xsl:for-each, xsl:template, or xsl:stylesheet.
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.
1010
protected ElemTemplateElement getPrevElementWithinContext(ElemTemplateElement elem)
1012
ElemTemplateElement prev = elem.getPreviousSiblingElem();
1014
prev = elem.getParentElem();
1017
int type = prev.getXSLToken();
1018
if((Constants.ELEMNAME_FOREACH == type) ||
1019
(Constants.ELEMNAME_TEMPLATE == type) ||
1020
(Constants.ELEMNAME_STYLESHEET == type))
1029
* From an XPath expression component, get the ElemTemplateElement
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.
1036
protected ElemTemplateElement getElemFromExpression(Expression expr)
1038
ExpressionNode parent = expr.exprGetParent();
1039
while(null != parent)
1041
if(parent instanceof ElemTemplateElement)
1042
return (ElemTemplateElement)parent;
1043
parent = parent.exprGetParent();
1045
throw new RuntimeException(XSLMessages.createMessage(XSLTErrorResources.ER_ASSERT_NO_TEMPLATE_PARENT, null));
1046
// "Programmer's error! expr has no ElemTemplateElement parent!");
1050
* Tell if the given LocPathIterator is relative to an absolute path, i.e.
1051
* in not dependent on the context.
1053
* @return true if the LocPathIterator is not dependent on the context node.
1055
public boolean isAbsolute(LocPathIterator path)
1057
int analysis = path.getAnalysisBits();
1058
boolean isAbs = (WalkerFactory.isSet(analysis, WalkerFactory.BIT_ROOT) ||
1059
WalkerFactory.isSet(analysis, WalkerFactory.BIT_ANY_DESCENDANT_FROM_ROOT));
1062
isAbs = m_absPathChecker.checkAbsolute(path);
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.
1075
public boolean visitLocationPath(ExpressionOwner owner, LocPathIterator path)
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)
1083
else if(path instanceof WalkingIterator)
1085
WalkingIterator wi = (WalkingIterator)path;
1086
AxesWalker aw = wi.getFirstWalker();
1087
if((aw instanceof FilterExprWalker) && (null == aw.getNextWalker()))
1089
FilterExprWalker few = (FilterExprWalker)aw;
1090
Expression exp = few.getInnerExpression();
1091
if(exp instanceof Variable)
1096
if (isAbsolute(path) && (null != m_absPaths))
1099
validateNewAddition(m_absPaths, owner, path);
1100
m_absPaths.addElement(owner);
1102
else if (m_isSameContext && (null != m_paths))
1105
validateNewAddition(m_paths, owner, path);
1106
m_paths.addElement(owner);
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.
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.
1122
public boolean visitPredicate(ExpressionOwner owner, Expression pred)
1124
boolean savedIsSame = m_isSameContext;
1125
m_isSameContext = false;
1127
// Any further down, just collect the absolute paths.
1128
pred.callVisitors(owner, this);
1130
m_isSameContext = savedIsSame;
1132
// We've already gone down the subtree, so don't go have the caller
1138
* Visit an XSLT top-level instruction.
1140
* @param elem The xsl instruction element object.
1141
* @return true if the sub expressions should be traversed.
1143
public boolean visitTopLevelInstruction(ElemTemplateElement elem)
1145
int type = elem.getXSLToken();
1148
case Constants.ELEMNAME_TEMPLATE :
1149
return visitInstruction(elem);
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.
1160
* @param elem The xsl instruction element object.
1161
* @return true if the sub expressions should be traversed.
1163
public boolean visitInstruction(ElemTemplateElement elem)
1165
int type = elem.getXSLToken();
1168
case Constants.ELEMNAME_CALLTEMPLATE :
1169
case Constants.ELEMNAME_TEMPLATE :
1170
case Constants.ELEMNAME_FOREACH :
1173
// Just get the select value.
1174
if(type == Constants.ELEMNAME_FOREACH)
1176
ElemForEach efe = (ElemForEach) elem;
1178
Expression select = efe.getSelect();
1179
select.callVisitors(efe, this);
1182
Vector savedPaths = m_paths;
1183
m_paths = new Vector();
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);
1191
m_paths = savedPaths;
1193
// select.callVisitors(efe, this);
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;
1211
// ==== DIAGNOSTIC AND DEBUG FUNCTIONS ====
1214
* Print out to std err the number of paths reduced.
1216
protected void diagnoseNumPaths(Vector paths, int numPathsEliminated,
1217
int numUniquePathsEliminated)
1219
if (numPathsEliminated > 0)
1221
if(paths == m_paths)
1223
System.err.println("Eliminated " + numPathsEliminated + " total paths!");
1225
"Consolodated " + numUniquePathsEliminated + " redundent paths!");
1229
System.err.println("Eliminated " + numPathsEliminated + " total global paths!");
1231
"Consolodated " + numUniquePathsEliminated + " redundent global paths!");
1238
* Assert that the expression is a LocPathIterator, and, if
1239
* not, try to give some diagnostic info.
1241
private final void assertIsLocPathIterator(Expression expr1, ExpressionOwner eo)
1242
throws RuntimeException
1244
if(!(expr1 instanceof LocPathIterator))
1247
if(expr1 instanceof Variable)
1249
errMsg = "Programmer's assertion: expr1 not an iterator: "+
1250
((Variable)expr1).getQName();
1254
errMsg = "Programmer's assertion: expr1 not an iterator: "+
1255
expr1.getClass().getName();
1257
throw new RuntimeException(errMsg + ", "+
1258
eo.getClass().getName()+" "+
1259
expr1.exprGetParent());
1265
* Validate some assumptions about the new LocPathIterator and it's
1266
* owner and the state of the list.
1268
private static void validateNewAddition(Vector paths, ExpressionOwner owner,
1269
LocPathIterator path)
1270
throws RuntimeException
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++)
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!!!");
1286
protected static void assertion(boolean b, String msg)
1290
throw new RuntimeException(XSLMessages.createMessage(XSLTErrorResources.ER_ASSERT_REDUNDENT_EXPR_ELIMINATOR, new Object[]{msg}));
1291
// "Programmer's assertion in RundundentExprEliminator: "+msg);
1296
* Since we want to sort multistep expressions by length, use
1297
* a linked list with elements of type MultistepExprHolder.
1299
class MultistepExprHolder implements Cloneable
1301
ExpressionOwner m_exprOwner; // Will change to null once we have processed this item.
1302
final int m_stepCount;
1303
MultistepExprHolder m_next;
1306
* Clone this object.
1308
public Object clone()
1309
throws CloneNotSupportedException
1311
return super.clone();
1315
* Create a MultistepExprHolder.
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.
1321
MultistepExprHolder(ExpressionOwner exprOwner, int stepCount, MultistepExprHolder next)
1323
m_exprOwner = exprOwner;
1324
assertion(null != m_exprOwner, "exprOwner can not be null!");
1325
m_stepCount = stepCount;
1330
* Add a new MultistepExprHolder in sorted order in the list.
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.
1337
MultistepExprHolder addInSortedOrder(ExpressionOwner exprOwner, int stepCount)
1339
MultistepExprHolder first = this;
1340
MultistepExprHolder next = this;
1341
MultistepExprHolder prev = null;
1344
if(stepCount >= next.m_stepCount)
1346
MultistepExprHolder newholder = new MultistepExprHolder(exprOwner, stepCount, next);
1350
prev.m_next = newholder;
1358
prev.m_next = new MultistepExprHolder(exprOwner, stepCount, null);
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.
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.
1372
MultistepExprHolder unlink(MultistepExprHolder itemToRemove)
1374
MultistepExprHolder first = this;
1375
MultistepExprHolder next = this;
1376
MultistepExprHolder prev = null;
1379
if(next == itemToRemove)
1382
first = next.m_next;
1384
prev.m_next = next.m_next;
1394
assertion(false, "unlink failed!!!");
1399
* Get the number of linked list items.
1404
MultistepExprHolder next = this;
1414
* Print diagnostics out for the multistep list.
1416
protected void diagnose()
1418
System.err.print("Found multistep iterators: " + this.getLength() + " ");
1419
MultistepExprHolder next = this;
1420
while (null != next)
1422
System.err.print("" + next.m_stepCount);
1425
System.err.print(", ");
1427
System.err.println();