1 /* 2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. 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 com.sun.org.apache.bcel.internal.generic.GOTO_W; 27 import com.sun.org.apache.bcel.internal.generic.InstructionHandle; 28 import com.sun.org.apache.bcel.internal.generic.InstructionList; 29 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator; 30 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator; 31 import java.util.ArrayList; 32 import java.util.List; 33 import java.util.Map; 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 * @LastModified: Nov 2017 51 */ 52 final class TestSeq { 53 54 /** 55 * Integer code for the kernel type of this test sequence 56 */ 57 private int _kernelType; 58 59 /** 60 * ArrayList of all patterns in the test sequence. May include 61 * patterns with "*", "@*" or "node()" kernel. 62 */ 63 private List<LocationPathPattern> _patterns = null; 64 65 /** 66 * A reference to the Mode object. 67 */ 68 private Mode _mode = null; 69 70 /** 71 * Default template for this test sequence 72 */ 73 private Template _default = null; 74 75 /** 76 * Instruction list representing this test sequence. 77 */ 78 private InstructionList _instructionList; 79 80 /** 81 * Cached handle to avoid compiling more than once. 82 */ 83 private InstructionHandle _start = null; 84 85 /** 86 * Creates a new test sequence given a set of patterns and a mode. 87 */ 88 public TestSeq(List<LocationPathPattern> patterns, Mode mode) { 89 this(patterns, -2, mode); 90 } 91 92 public TestSeq(List<LocationPathPattern> patterns, int kernelType, Mode mode) { 93 _patterns = patterns; 94 _kernelType = kernelType; 95 _mode = mode; 96 } 97 98 /** 99 * Returns a string representation of this test sequence. Notice 100 * that test sequences are mutable, so the value returned by this 101 * method is different before and after calling reduce(). 102 */ 103 public String toString() { 104 final int count = _patterns.size(); 105 final StringBuffer result = new StringBuffer(); 106 107 for (int i = 0; i < count; i++) { 108 final LocationPathPattern pattern = _patterns.get(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.isEmpty()) ? _default 135 : ((Pattern) _patterns.get(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.isEmpty()) ? _default 145 : ((Pattern) _patterns.get(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 List<LocationPathPattern> newPatterns = new ArrayList<>(); 156 157 for (LocationPathPattern pattern : _patterns) { 158 // Reduce this pattern 159 pattern.reduceKernelPattern(); 160 161 // Is this pattern fully reduced? 162 if (pattern.isWildcard()) { 163 _default = pattern.getTemplate(); 164 break; // Ignore following patterns 165 } 166 else { 167 newPatterns.add(pattern); 168 } 169 } 170 _patterns = newPatterns; 171 } 172 173 /** 174 * Returns, by reference, the templates that are included in 175 * this test sequence. Note that a single template can occur 176 * in several test sequences if its pattern is a union. 177 */ 178 public void findTemplates(Map<Template, Object> templates) { 179 if (_default != null) { 180 templates.put(_default, this); 181 } 182 for (LocationPathPattern pattern : _patterns) { 183 templates.put(pattern.getTemplate(), this); 184 } 185 } 186 187 /** 188 * Get the instruction handle to a template's code. This is 189 * used when a single template occurs in several test 190 * sequences; that is, if its pattern is a union of patterns 191 * (e.g. match="A/B | A/C"). 192 */ 193 private InstructionHandle getTemplateHandle(Template template) { 194 return _mode.getTemplateInstructionHandle(template); 195 } 196 197 /** 198 * Returns pattern n in this test sequence 199 */ 200 private LocationPathPattern getPattern(int n) { 201 return _patterns.get(n); 202 } 203 204 /** 205 * Compile the code for this test sequence. Compile patterns 206 * from highest to lowest priority. Note that since patterns 207 * can be share by multiple test sequences, instruction lists 208 * must be copied before backpatching. 209 */ 210 public InstructionHandle compile(ClassGenerator classGen, 211 MethodGenerator methodGen, 212 InstructionHandle continuation) 213 { 214 // Returned cached value if already compiled 215 if (_start != null) { 216 return _start; 217 } 218 219 // If not patterns, then return handle for default template 220 final int count = _patterns.size(); 221 if (count == 0) { 222 return (_start = getTemplateHandle(_default)); 223 } 224 225 // Init handle to jump when all patterns failed 226 InstructionHandle fail = (_default == null) ? continuation 227 : getTemplateHandle(_default); 228 229 // Compile all patterns in reverse order 230 for (int n = count - 1; n >= 0; n--) { 231 final LocationPathPattern pattern = getPattern(n); 232 final Template template = pattern.getTemplate(); 233 final InstructionList il = new InstructionList(); 234 235 // Patterns expect current node on top of stack 236 il.append(methodGen.loadCurrentNode()); 237 238 // Apply the test-code compiled for the pattern 239 InstructionList ilist = methodGen.getInstructionList(pattern); 240 if (ilist == null) { 241 ilist = pattern.compile(classGen, methodGen); 242 methodGen.addInstructionList(pattern, ilist); 243 } 244 245 // Make a copy of the instruction list for backpatching 246 InstructionList copyOfilist = ilist.copy(); 247 248 FlowList trueList = pattern.getTrueList(); 249 if (trueList != null) { 250 trueList = trueList.copyAndRedirect(ilist, copyOfilist); 251 } 252 FlowList falseList = pattern.getFalseList(); 253 if (falseList != null) { 254 falseList = falseList.copyAndRedirect(ilist, copyOfilist); 255 } 256 257 il.append(copyOfilist); 258 259 // On success branch to the template code 260 final InstructionHandle gtmpl = getTemplateHandle(template); 261 final InstructionHandle success = il.append(new GOTO_W(gtmpl)); 262 263 if (trueList != null) { 264 trueList.backPatch(success); 265 } 266 if (falseList != null) { 267 falseList.backPatch(fail); 268 } 269 270 // Next pattern's 'fail' target is this pattern's first instruction 271 fail = il.getStart(); 272 273 // Append existing instruction list to the end of this one 274 if (_instructionList != null) { 275 il.append(_instructionList); 276 } 277 278 // Set current instruction list to be this one 279 _instructionList = il; 280 } 281 return (_start = fail); 282 } 283 }