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