View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   /*
6    * Copyright 2001-2004 The Apache Software Foundation.
7    *
8    * Licensed under the Apache License, Version 2.0 (the "License");
9    * you may not use this file except in compliance with the License.
10   * You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  /*
21   * $Id: TestSeq.java,v 1.2.4.1 2005/09/12 11:31:38 pvedula Exp $
22   */
23  
24  package com.sun.org.apache.xalan.internal.xsltc.compiler;
25  
26  import java.util.Dictionary;
27  import java.util.Vector;
28  
29  import com.sun.org.apache.bcel.internal.generic.GOTO_W;
30  import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
31  import com.sun.org.apache.bcel.internal.generic.InstructionList;
32  import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
33  import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
34  
35  /**
36   * A test sequence is a sequence of patterns that
37   *
38   *  (1) occured in templates in the same mode
39   *  (2) share the same kernel node type (e.g. A/B and C/C/B)
40   *  (3) may also contain patterns matching "*" and "node()"
41   *      (element sequence only) or matching "@*" (attribute
42   *      sequence only).
43   *
44   * A test sequence may have a default template, which will be
45   * instantiated if none of the other patterns match.
46   * @author Jacek Ambroziak
47   * @author Santiago Pericas-Geertsen
48   * @author Erwin Bolwidt <ejb@klomp.org>
49   * @author Morten Jorgensen <morten.jorgensen@sun.com>
50   */
51  final class TestSeq {
52  
53      /**
54       * Integer code for the kernel type of this test sequence
55       */
56      private int _kernelType;
57  
58      /**
59       * Vector of all patterns in the test sequence. May include
60       * patterns with "*", "@*" or "node()" kernel.
61       */
62      private Vector _patterns = null;
63  
64      /**
65       * A reference to the Mode object.
66       */
67      private Mode _mode = null;
68  
69      /**
70       * Default template for this test sequence
71       */
72      private Template _default = null;
73  
74      /**
75       * Instruction list representing this test sequence.
76       */
77      private InstructionList _instructionList;
78  
79      /**
80       * Cached handle to avoid compiling more than once.
81       */
82      private InstructionHandle _start = null;
83  
84      /**
85       * Creates a new test sequence given a set of patterns and a mode.
86       */
87      public TestSeq(Vector patterns, Mode mode) {
88          this(patterns, -2, mode);
89      }
90  
91      public TestSeq(Vector patterns, int kernelType, Mode mode) {
92          _patterns = patterns;
93          _kernelType = kernelType;
94          _mode = mode;
95      }
96  
97      /**
98       * Returns a string representation of this test sequence. Notice
99       * that test sequences are mutable, so the value returned by this
100      * method is different before and after calling reduce().
101      */
102     public String toString() {
103         final int count = _patterns.size();
104         final StringBuffer result = new StringBuffer();
105 
106         for (int i = 0; i < count; i++) {
107             final LocationPathPattern pattern =
108                 (LocationPathPattern) _patterns.elementAt(i);
109 
110             if (i == 0) {
111                 result.append("Testseq for kernel ").append(_kernelType)
112                       .append('\n');
113             }
114             result.append("   pattern ").append(i).append(": ")
115                   .append(pattern.toString())
116                   .append('\n');
117         }
118         return result.toString();
119     }
120 
121     /**
122      * Returns the instruction list for this test sequence
123      */
124     public InstructionList getInstructionList() {
125         return _instructionList;
126     }
127 
128     /**
129      * Return the highest priority for a pattern in this test
130      * sequence. This is either the priority of the first or
131      * of the default pattern.
132      */
133     public double getPriority() {
134         final Template template = (_patterns.size() == 0) ? _default
135             : ((Pattern) _patterns.elementAt(0)).getTemplate();
136         return template.getPriority();
137     }
138 
139     /**
140      * Returns the position of the highest priority pattern in
141      * this test sequence.
142      */
143     public int getPosition() {
144         final Template template = (_patterns.size() == 0) ? _default
145             : ((Pattern) _patterns.elementAt(0)).getTemplate();
146         return template.getPosition();
147     }
148 
149     /**
150      * Reduce the patterns in this test sequence. Creates a new
151      * vector of patterns and sets the default pattern if it
152      * finds a patterns that is fully reduced.
153      */
154     public void reduce() {
155         final Vector newPatterns = new Vector();
156 
157         final int count = _patterns.size();
158         for (int i = 0; i < count; i++) {
159             final LocationPathPattern pattern =
160                 (LocationPathPattern)_patterns.elementAt(i);
161 
162             // Reduce this pattern
163             pattern.reduceKernelPattern();
164 
165             // Is this pattern fully reduced?
166             if (pattern.isWildcard()) {
167                 _default = pattern.getTemplate();
168                 break;          // Ignore following patterns
169             }
170             else {
171                 newPatterns.addElement(pattern);
172             }
173         }
174         _patterns = newPatterns;
175     }
176 
177     /**
178      * Returns, by reference, the templates that are included in
179      * this test sequence. Note that a single template can occur
180      * in several test sequences if its pattern is a union.
181      */
182     public void findTemplates(Dictionary templates) {
183         if (_default != null) {
184             templates.put(_default, this);
185         }
186         for (int i = 0; i < _patterns.size(); i++) {
187             final LocationPathPattern pattern =
188                 (LocationPathPattern)_patterns.elementAt(i);
189             templates.put(pattern.getTemplate(), this);
190         }
191     }
192 
193     /**
194      * Get the instruction handle to a template's code. This is
195      * used when a single template occurs in several test
196      * sequences; that is, if its pattern is a union of patterns
197      * (e.g. match="A/B | A/C").
198      */
199     private InstructionHandle getTemplateHandle(Template template) {
200         return (InstructionHandle)_mode.getTemplateInstructionHandle(template);
201     }
202 
203     /**
204      * Returns pattern n in this test sequence
205      */
206     private LocationPathPattern getPattern(int n) {
207         return (LocationPathPattern)_patterns.elementAt(n);
208     }
209 
210     /**
211      * Compile the code for this test sequence. Compile patterns
212      * from highest to lowest priority. Note that since patterns
213      * can be share by multiple test sequences, instruction lists
214      * must be copied before backpatching.
215      */
216     public InstructionHandle compile(ClassGenerator classGen,
217                                      MethodGenerator methodGen,
218                                      InstructionHandle continuation)
219     {
220         // Returned cached value if already compiled
221         if (_start != null) {
222             return _start;
223         }
224 
225         // If not patterns, then return handle for default template
226         final int count = _patterns.size();
227         if (count == 0) {
228             return (_start = getTemplateHandle(_default));
229         }
230 
231         // Init handle to jump when all patterns failed
232         InstructionHandle fail = (_default == null) ? continuation
233             : getTemplateHandle(_default);
234 
235         // Compile all patterns in reverse order
236         for (int n = count - 1; n >= 0; n--) {
237             final LocationPathPattern pattern = getPattern(n);
238             final Template template = pattern.getTemplate();
239             final InstructionList il = new InstructionList();
240 
241             // Patterns expect current node on top of stack
242             il.append(methodGen.loadCurrentNode());
243 
244             // Apply the test-code compiled for the pattern
245             InstructionList ilist = methodGen.getInstructionList(pattern);
246             if (ilist == null) {
247                 ilist = pattern.compile(classGen, methodGen);
248                 methodGen.addInstructionList(pattern, ilist);
249             }
250 
251             // Make a copy of the instruction list for backpatching
252             InstructionList copyOfilist = ilist.copy();
253 
254             FlowList trueList = pattern.getTrueList();
255             if (trueList != null) {
256                 trueList = trueList.copyAndRedirect(ilist, copyOfilist);
257             }
258             FlowList falseList = pattern.getFalseList();
259             if (falseList != null) {
260                 falseList = falseList.copyAndRedirect(ilist, copyOfilist);
261             }
262 
263             il.append(copyOfilist);
264 
265             // On success branch to the template code
266             final InstructionHandle gtmpl = getTemplateHandle(template);
267             final InstructionHandle success = il.append(new GOTO_W(gtmpl));
268 
269             if (trueList != null) {
270                 trueList.backPatch(success);
271             }
272             if (falseList != null) {
273                 falseList.backPatch(fail);
274             }
275 
276             // Next pattern's 'fail' target is this pattern's first instruction
277             fail = il.getStart();
278 
279             // Append existing instruction list to the end of this one
280             if (_instructionList != null) {
281                 il.append(_instructionList);
282             }
283 
284             // Set current instruction list to be this one
285             _instructionList = il;
286         }
287         return (_start = fail);
288     }
289 }