001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: TestSeq.java 468650 2006-10-28 07:03:30Z minchau $
020 */
021
022 package org.apache.xalan.xsltc.compiler;
023
024 import java.util.Dictionary;
025 import java.util.Vector;
026
027 import org.apache.bcel.generic.GOTO_W;
028 import org.apache.bcel.generic.InstructionHandle;
029 import org.apache.bcel.generic.InstructionList;
030 import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
031 import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
032
033 /**
034 * A test sequence is a sequence of patterns that
035 *
036 * (1) occured in templates in the same mode
037 * (2) share the same kernel node type (e.g. A/B and C/C/B)
038 * (3) may also contain patterns matching "*" and "node()"
039 * (element sequence only) or matching "@*" (attribute
040 * sequence only).
041 *
042 * A test sequence may have a default template, which will be
043 * instantiated if none of the other patterns match.
044 * @author Jacek Ambroziak
045 * @author Santiago Pericas-Geertsen
046 * @author Erwin Bolwidt <ejb@klomp.org>
047 * @author Morten Jorgensen <morten.jorgensen@sun.com>
048 */
049 final class TestSeq {
050
051 /**
052 * Integer code for the kernel type of this test sequence
053 */
054 private int _kernelType;
055
056 /**
057 * Vector of all patterns in the test sequence. May include
058 * patterns with "*", "@*" or "node()" kernel.
059 */
060 private Vector _patterns = null;
061
062 /**
063 * A reference to the Mode object.
064 */
065 private Mode _mode = null;
066
067 /**
068 * Default template for this test sequence
069 */
070 private Template _default = null;
071
072 /**
073 * Instruction list representing this test sequence.
074 */
075 private InstructionList _instructionList;
076
077 /**
078 * Cached handle to avoid compiling more than once.
079 */
080 private InstructionHandle _start = null;
081
082 /**
083 * Creates a new test sequence given a set of patterns and a mode.
084 */
085 public TestSeq(Vector patterns, Mode mode) {
086 this(patterns, -2, mode);
087 }
088
089 public TestSeq(Vector patterns, int kernelType, Mode mode) {
090 _patterns = patterns;
091 _kernelType = kernelType;
092 _mode = mode;
093 }
094
095 /**
096 * Returns a string representation of this test sequence. Notice
097 * that test sequences are mutable, so the value returned by this
098 * method is different before and after calling reduce().
099 */
100 public String toString() {
101 final int count = _patterns.size();
102 final StringBuffer result = new StringBuffer();
103
104 for (int i = 0; i < count; i++) {
105 final LocationPathPattern pattern =
106 (LocationPathPattern) _patterns.elementAt(i);
107
108 if (i == 0) {
109 result.append("Testseq for kernel " + _kernelType)
110 .append('\n');
111 }
112 result.append(" pattern " + i + ": ")
113 .append(pattern.toString())
114 .append('\n');
115 }
116 return result.toString();
117 }
118
119 /**
120 * Returns the instruction list for this test sequence
121 */
122 public InstructionList getInstructionList() {
123 return _instructionList;
124 }
125
126 /**
127 * Return the highest priority for a pattern in this test
128 * sequence. This is either the priority of the first or
129 * of the default pattern.
130 */
131 public double getPriority() {
132 final Template template = (_patterns.size() == 0) ? _default
133 : ((Pattern) _patterns.elementAt(0)).getTemplate();
134 return template.getPriority();
135 }
136
137 /**
138 * Returns the position of the highest priority pattern in
139 * this test sequence.
140 */
141 public int getPosition() {
142 final Template template = (_patterns.size() == 0) ? _default
143 : ((Pattern) _patterns.elementAt(0)).getTemplate();
144 return template.getPosition();
145 }
146
147 /**
148 * Reduce the patterns in this test sequence. Creates a new
149 * vector of patterns and sets the default pattern if it
150 * finds a patterns that is fully reduced.
151 */
152 public void reduce() {
153 final Vector newPatterns = new Vector();
154
155 final int count = _patterns.size();
156 for (int i = 0; i < count; i++) {
157 final LocationPathPattern pattern =
158 (LocationPathPattern)_patterns.elementAt(i);
159
160 // Reduce this pattern
161 pattern.reduceKernelPattern();
162
163 // Is this pattern fully reduced?
164 if (pattern.isWildcard()) {
165 _default = pattern.getTemplate();
166 break; // Ignore following patterns
167 }
168 else {
169 newPatterns.addElement(pattern);
170 }
171 }
172 _patterns = newPatterns;
173 }
174
175 /**
176 * Returns, by reference, the templates that are included in
177 * this test sequence. Note that a single template can occur
178 * in several test sequences if its pattern is a union.
179 */
180 public void findTemplates(Dictionary templates) {
181 if (_default != null) {
182 templates.put(_default, this);
183 }
184 for (int i = 0; i < _patterns.size(); i++) {
185 final LocationPathPattern pattern =
186 (LocationPathPattern)_patterns.elementAt(i);
187 templates.put(pattern.getTemplate(), this);
188 }
189 }
190
191 /**
192 * Get the instruction handle to a template's code. This is
193 * used when a single template occurs in several test
194 * sequences; that is, if its pattern is a union of patterns
195 * (e.g. match="A/B | A/C").
196 */
197 private InstructionHandle getTemplateHandle(Template template) {
198 return (InstructionHandle)_mode.getTemplateInstructionHandle(template);
199 }
200
201 /**
202 * Returns pattern n in this test sequence
203 */
204 private LocationPathPattern getPattern(int n) {
205 return (LocationPathPattern)_patterns.elementAt(n);
206 }
207
208 /**
209 * Compile the code for this test sequence. Compile patterns
210 * from highest to lowest priority. Note that since patterns
211 * can be share by multiple test sequences, instruction lists
212 * must be copied before backpatching.
213 */
214 public InstructionHandle compile(ClassGenerator classGen,
215 MethodGenerator methodGen,
216 InstructionHandle continuation)
217 {
218 // Returned cached value if already compiled
219 if (_start != null) {
220 return _start;
221 }
222
223 // If not patterns, then return handle for default template
224 final int count = _patterns.size();
225 if (count == 0) {
226 return (_start = getTemplateHandle(_default));
227 }
228
229 // Init handle to jump when all patterns failed
230 InstructionHandle fail = (_default == null) ? continuation
231 : getTemplateHandle(_default);
232
233 // Compile all patterns in reverse order
234 for (int n = count - 1; n >= 0; n--) {
235 final LocationPathPattern pattern = getPattern(n);
236 final Template template = pattern.getTemplate();
237 final InstructionList il = new InstructionList();
238
239 // Patterns expect current node on top of stack
240 il.append(methodGen.loadCurrentNode());
241
242 // Apply the test-code compiled for the pattern
243 InstructionList ilist = methodGen.getInstructionList(pattern);
244 if (ilist == null) {
245 ilist = pattern.compile(classGen, methodGen);
246 methodGen.addInstructionList(pattern, ilist);
247 }
248
249 // Make a copy of the instruction list for backpatching
250 InstructionList copyOfilist = ilist.copy();
251
252 FlowList trueList = pattern.getTrueList();
253 if (trueList != null) {
254 trueList = trueList.copyAndRedirect(ilist, copyOfilist);
255 }
256 FlowList falseList = pattern.getFalseList();
257 if (falseList != null) {
258 falseList = falseList.copyAndRedirect(ilist, copyOfilist);
259 }
260
261 il.append(copyOfilist);
262
263 // On success branch to the template code
264 final InstructionHandle gtmpl = getTemplateHandle(template);
265 final InstructionHandle success = il.append(new GOTO_W(gtmpl));
266
267 if (trueList != null) {
268 trueList.backPatch(success);
269 }
270 if (falseList != null) {
271 falseList.backPatch(fail);
272 }
273
274 // Next pattern's 'fail' target is this pattern's first instruction
275 fail = il.getStart();
276
277 // Append existing instruction list to the end of this one
278 if (_instructionList != null) {
279 il.append(_instructionList);
280 }
281
282 // Set current instruction list to be this one
283 _instructionList = il;
284 }
285 return (_start = fail);
286 }
287 }