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 }