1 /* 2 * Copyright (c) 2015, 2016, 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 package com.sun.org.apache.xalan.internal.xsltc.compiler; 22 23 import com.sun.org.apache.bcel.internal.generic.ALOAD; 24 import com.sun.org.apache.bcel.internal.generic.ASTORE; 25 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen; 26 import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC; 27 import com.sun.org.apache.bcel.internal.generic.InstructionList; 28 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen; 29 import com.sun.org.apache.bcel.internal.generic.PUSH; 30 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator; 31 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg; 32 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator; 33 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type; 34 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError; 35 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util; 36 import com.sun.org.apache.xml.internal.utils.XML11Char; 37 38 /** 39 * @author Jacek Ambroziak 40 * @author Santiago Pericas-Geertsen 41 * @author Morten Jorgensen 42 */ 43 final class XslElement extends Instruction { 44 45 private String _prefix; 46 private boolean _ignore = false; 47 private boolean _isLiteralName = true; 48 private AttributeValueTemplate _name; 49 private AttributeValueTemplate _namespace; 50 51 /** 52 * Displays the contents of the element 53 */ display(int indent)54 public void display(int indent) { 55 indent(indent); 56 Util.println("Element " + _name); 57 displayContents(indent + IndentIncrement); 58 } 59 parseContents(Parser parser)60 public void parseContents(Parser parser) { 61 final SymbolTable stable = parser.getSymbolTable(); 62 63 // Handle the 'name' attribute 64 String name = getAttribute("name"); 65 if (name == EMPTYSTRING) { 66 ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR, 67 name, this); 68 parser.reportError(WARNING, msg); 69 parseChildren(parser); 70 _ignore = true; // Ignore the element if the QName is invalid 71 return; 72 } 73 74 // Get namespace attribute 75 String namespace = getAttribute("namespace"); 76 77 // Optimize compilation when name is known at compile time 78 _isLiteralName = Util.isLiteral(name); 79 if (_isLiteralName) { 80 if (!XML11Char.isXML11ValidQName(name)) { 81 ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR, 82 name, this); 83 parser.reportError(WARNING, msg); 84 parseChildren(parser); 85 _ignore = true; // Ignore the element if the QName is invalid 86 return; 87 } 88 89 final QName qname = parser.getQNameSafe(name); 90 String prefix = qname.getPrefix(); 91 String local = qname.getLocalPart(); 92 93 if (prefix == null) { 94 prefix = EMPTYSTRING; 95 } 96 97 if (!hasAttribute("namespace")) { 98 namespace = lookupNamespace(prefix); 99 if (namespace == null) { 100 ErrorMsg err = new ErrorMsg(ErrorMsg.NAMESPACE_UNDEF_ERR, 101 prefix, this); 102 parser.reportError(WARNING, err); 103 parseChildren(parser); 104 _ignore = true; // Ignore the element if prefix is undeclared 105 return; 106 } 107 _prefix = prefix; 108 _namespace = new AttributeValueTemplate(namespace, parser, this); 109 } 110 else { 111 if (prefix == EMPTYSTRING) { 112 if (Util.isLiteral(namespace)) { 113 prefix = lookupPrefix(namespace); 114 if (prefix == null) { 115 prefix = stable.generateNamespacePrefix(); 116 } 117 } 118 119 // Prepend prefix to local name 120 final StringBuffer newName = new StringBuffer(prefix); 121 if (prefix != EMPTYSTRING) { 122 newName.append(':'); 123 } 124 name = newName.append(local).toString(); 125 } 126 _prefix = prefix; 127 _namespace = new AttributeValueTemplate(namespace, parser, this); 128 } 129 } 130 else { 131 _namespace = (namespace == EMPTYSTRING) ? null : 132 new AttributeValueTemplate(namespace, parser, this); 133 } 134 135 _name = new AttributeValueTemplate(name, parser, this); 136 137 final String useSets = getAttribute("use-attribute-sets"); 138 if (useSets.length() > 0) { 139 if (!Util.isValidQNames(useSets)) { 140 ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, useSets, this); 141 parser.reportError(Constants.ERROR, err); 142 } 143 setFirstElement(new UseAttributeSets(useSets, parser)); 144 } 145 146 parseChildren(parser); 147 } 148 149 /** 150 * Run type check on element name & contents 151 */ typeCheck(SymbolTable stable)152 public Type typeCheck(SymbolTable stable) throws TypeCheckError { 153 if (!_ignore) { 154 _name.typeCheck(stable); 155 if (_namespace != null) { 156 _namespace.typeCheck(stable); 157 } 158 } 159 typeCheckContents(stable); 160 return Type.Void; 161 } 162 163 /** 164 * This method is called when the name of the element is known at compile time. 165 * In this case, there is no need to inspect the element name at runtime to 166 * determine if a prefix exists, needs to be generated, etc. 167 */ translateLiteral(ClassGenerator classGen, MethodGenerator methodGen)168 public void translateLiteral(ClassGenerator classGen, MethodGenerator methodGen) { 169 final ConstantPoolGen cpg = classGen.getConstantPool(); 170 final InstructionList il = methodGen.getInstructionList(); 171 172 if (!_ignore) { 173 il.append(methodGen.loadHandler()); 174 _name.translate(classGen, methodGen); 175 il.append(DUP2); 176 il.append(methodGen.startElement()); 177 178 if (_namespace != null) { 179 il.append(methodGen.loadHandler()); 180 il.append(new PUSH(cpg, _prefix)); 181 _namespace.translate(classGen,methodGen); 182 il.append(methodGen.namespace()); 183 } 184 } 185 186 translateContents(classGen, methodGen); 187 188 if (!_ignore) { 189 il.append(methodGen.endElement()); 190 } 191 } 192 193 /** 194 * At runtime the compilation of xsl:element results in code that: (i) 195 * evaluates the avt for the name, (ii) checks for a prefix in the name 196 * (iii) generates a new prefix and create a new qname when necessary 197 * (iv) calls startElement() on the handler (v) looks up a uri in the XML 198 * when the prefix is not known at compile time (vi) calls namespace() 199 * on the handler (vii) evaluates the contents (viii) calls endElement(). 200 */ translate(ClassGenerator classGen, MethodGenerator methodGen)201 public void translate(ClassGenerator classGen, MethodGenerator methodGen) { 202 final ConstantPoolGen cpg = classGen.getConstantPool(); 203 final InstructionList il = methodGen.getInstructionList(); 204 205 // Optimize translation if element name is a literal 206 if (_isLiteralName) { 207 translateLiteral(classGen, methodGen); 208 return; 209 } 210 211 if (!_ignore) { 212 213 // if the qname is an AVT, then the qname has to be checked at runtime if it is a valid qname 214 LocalVariableGen nameValue = 215 methodGen.addLocalVariable2("nameValue", 216 Util.getJCRefType(STRING_SIG), 217 null); 218 219 // store the name into a variable first so _name.translate only needs to be called once 220 _name.translate(classGen, methodGen); 221 nameValue.setStart(il.append(new ASTORE(nameValue.getIndex()))); 222 il.append(new ALOAD(nameValue.getIndex())); 223 224 // call checkQName if the name is an AVT 225 final int check = cpg.addMethodref(BASIS_LIBRARY_CLASS, "checkQName", 226 "(" 227 +STRING_SIG 228 +")V"); 229 il.append(new INVOKESTATIC(check)); 230 231 // Push handler for call to endElement() 232 il.append(methodGen.loadHandler()); 233 234 // load name value again 235 nameValue.setEnd(il.append(new ALOAD(nameValue.getIndex()))); 236 237 if (_namespace != null) { 238 _namespace.translate(classGen, methodGen); 239 } 240 else { 241 il.append(ACONST_NULL); 242 } 243 244 // Push additional arguments 245 il.append(methodGen.loadHandler()); 246 il.append(methodGen.loadDOM()); 247 il.append(methodGen.loadCurrentNode()); 248 249 // Invoke BasisLibrary.startXslElemCheckQName() 250 il.append(new INVOKESTATIC( 251 cpg.addMethodref(BASIS_LIBRARY_CLASS, "startXslElement", 252 "(" + STRING_SIG 253 + STRING_SIG 254 + TRANSLET_OUTPUT_SIG 255 + DOM_INTF_SIG + "I)" + STRING_SIG))); 256 257 258 } 259 260 translateContents(classGen, methodGen); 261 262 if (!_ignore) { 263 il.append(methodGen.endElement()); 264 } 265 } 266 267 /** 268 * Override this method to make sure that xsl:attributes are not 269 * copied to output if this xsl:element is to be ignored 270 */ translateContents(ClassGenerator classGen, MethodGenerator methodGen)271 public void translateContents(ClassGenerator classGen, 272 MethodGenerator methodGen) { 273 final int n = elementCount(); 274 for (int i = 0; i < n; i++) { 275 final SyntaxTreeNode item = getContents().get(i); 276 if (_ignore && item instanceof XslAttribute) continue; 277 item.translate(classGen, methodGen); 278 } 279 } 280 281 } 282