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 */ 54 public void display(int indent) { 55 indent(indent); 56 Util.println("Element " + _name); 57 displayContents(indent + IndentIncrement); 58 } 59 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 */ 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 */ 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) calls startElement() on the handler (iv) looks up a uri in the XML 197 * when the prefix is not known at compile time (v) calls namespace() 198 * on the handler (vi) evaluates the contents (vii) calls endElement(). 199 */ 200 public void translate(ClassGenerator classGen, MethodGenerator methodGen) { 201 final ConstantPoolGen cpg = classGen.getConstantPool(); 202 final InstructionList il = methodGen.getInstructionList(); 203 204 // Optimize translation if element name is a literal 205 if (_isLiteralName) { 206 translateLiteral(classGen, methodGen); 207 return; 208 } 209 210 if (!_ignore) { 211 // store the name into a variable so _name.translate only needs 212 // to be called once 213 LocalVariableGen nameValue = methodGen.addLocalVariable2 214 ("nameValue", Util.getJCRefType(STRING_SIG), null); 215 216 _name.translate(classGen, methodGen); 217 218 nameValue.setStart(il.append(new ASTORE(nameValue.getIndex()))); 219 il.append(new ALOAD(nameValue.getIndex())); 220 221 // if the qname is an AVT, then the qname has to be checked at 222 // runtime if it is a valid qname 223 il.append(new INVOKESTATIC(cpg.addMethodref 224 (BASIS_LIBRARY_CLASS, "checkQName", "(" + STRING_SIG + ")V"))); 225 226 // push handler for call to endElement() 227 il.append(methodGen.loadHandler()); 228 229 // load name value again 230 nameValue.setEnd(il.append(new ALOAD(nameValue.getIndex()))); 231 232 if (_namespace != null) { 233 _namespace.translate(classGen, methodGen); 234 } else { 235 il.append(ACONST_NULL); 236 } 237 238 // push additional arguments 239 il.append(methodGen.loadHandler()); 240 il.append(methodGen.loadDOM()); 241 il.append(methodGen.loadCurrentNode()); 242 243 // invoke BasisLibrary.startXslElement() 244 il.append(new INVOKESTATIC(cpg.addMethodref 245 (BASIS_LIBRARY_CLASS, "startXslElement", "(" + STRING_SIG + 246 STRING_SIG + TRANSLET_OUTPUT_SIG + DOM_INTF_SIG + "I)" + 247 STRING_SIG))); 248 } 249 250 translateContents(classGen, methodGen); 251 252 if (!_ignore) { 253 il.append(methodGen.endElement()); 254 } 255 } 256 257 /** 258 * Override this method to make sure that xsl:attributes are not 259 * copied to output if this xsl:element is to be ignored 260 */ 261 public void translateContents(ClassGenerator classGen, 262 MethodGenerator methodGen) { 263 final int n = elementCount(); 264 for (int i = 0; i < n; i++) { 265 final SyntaxTreeNode item = getContents().get(i); 266 if (_ignore && item instanceof XslAttribute) continue; 267 item.translate(classGen, methodGen); 268 } 269 } 270 271 }