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: XslElement.java 1225842 2011-12-30 15:14:35Z mrglavas $ 020 */ 021 022 package org.apache.xalan.xsltc.compiler; 023 024 import org.apache.bcel.generic.ALOAD; 025 import org.apache.bcel.generic.ASTORE; 026 import org.apache.bcel.generic.ConstantPoolGen; 027 import org.apache.bcel.generic.GETSTATIC; 028 import org.apache.bcel.generic.INVOKESTATIC; 029 import org.apache.bcel.generic.InstructionList; 030 import org.apache.bcel.generic.LocalVariableGen; 031 import org.apache.bcel.generic.PUSH; 032 import org.apache.xalan.xsltc.compiler.util.ClassGenerator; 033 import org.apache.xalan.xsltc.compiler.util.ErrorMsg; 034 import org.apache.xalan.xsltc.compiler.util.MethodGenerator; 035 import org.apache.xalan.xsltc.compiler.util.Type; 036 import org.apache.xalan.xsltc.compiler.util.TypeCheckError; 037 import org.apache.xalan.xsltc.compiler.util.Util; 038 import org.apache.xml.utils.XML11Char; 039 040 /** 041 * @author Jacek Ambroziak 042 * @author Santiago Pericas-Geertsen 043 * @author Morten Jorgensen 044 */ 045 final class XslElement extends Instruction { 046 047 private String _prefix; 048 private boolean _ignore = false; 049 private boolean _isLiteralName = true; 050 private AttributeValueTemplate _name; 051 private AttributeValueTemplate _namespace; 052 053 /** 054 * Displays the contents of the element 055 */ 056 public void display(int indent) { 057 indent(indent); 058 Util.println("Element " + _name); 059 displayContents(indent + IndentIncrement); 060 } 061 062 /** 063 * This method is now deprecated. The new implemation of this class 064 * never declares the default NS. 065 */ 066 public boolean declaresDefaultNS() { 067 return false; 068 } 069 070 public void parseContents(Parser parser) { 071 final SymbolTable stable = parser.getSymbolTable(); 072 073 // Handle the 'name' attribute 074 String name = getAttribute("name"); 075 if (name == EMPTYSTRING) { 076 ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR, 077 name, this); 078 parser.reportError(WARNING, msg); 079 parseChildren(parser); 080 _ignore = true; // Ignore the element if the QName is invalid 081 return; 082 } 083 084 // Get namespace attribute 085 String namespace = getAttribute("namespace"); 086 087 // Optimize compilation when name is known at compile time 088 _isLiteralName = Util.isLiteral(name); 089 if (_isLiteralName) { 090 if (!XML11Char.isXML11ValidQName(name)) { 091 ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR, 092 name, this); 093 parser.reportError(WARNING, msg); 094 parseChildren(parser); 095 _ignore = true; // Ignore the element if the QName is invalid 096 return; 097 } 098 099 final QName qname = parser.getQNameSafe(name); 100 String prefix = qname.getPrefix(); 101 String local = qname.getLocalPart(); 102 103 if (prefix == null) { 104 prefix = EMPTYSTRING; 105 } 106 107 if (!hasAttribute("namespace")) { 108 namespace = lookupNamespace(prefix); 109 if (namespace == null) { 110 ErrorMsg err = new ErrorMsg(ErrorMsg.NAMESPACE_UNDEF_ERR, 111 prefix, this); 112 parser.reportError(WARNING, err); 113 parseChildren(parser); 114 _ignore = true; // Ignore the element if prefix is undeclared 115 return; 116 } 117 _prefix = prefix; 118 _namespace = new AttributeValueTemplate(namespace, parser, this); 119 } 120 else { 121 if (prefix == EMPTYSTRING) { 122 if (Util.isLiteral(namespace)) { 123 prefix = lookupPrefix(namespace); 124 if (prefix == null) { 125 prefix = stable.generateNamespacePrefix(); 126 } 127 } 128 129 // Prepend prefix to local name 130 final StringBuffer newName = new StringBuffer(prefix); 131 if (prefix != EMPTYSTRING) { 132 newName.append(':'); 133 } 134 name = newName.append(local).toString(); 135 } 136 _prefix = prefix; 137 _namespace = new AttributeValueTemplate(namespace, parser, this); 138 } 139 } 140 else { 141 // name attribute contains variable parts. If there is no namespace 142 // attribute, the generated code needs to be prepared to look up 143 // any prefix in the stylesheet at run-time. 144 _namespace = (namespace == EMPTYSTRING) ? null : 145 new AttributeValueTemplate(namespace, parser, this); 146 } 147 148 _name = new AttributeValueTemplate(name, parser, this); 149 150 final String useSets = getAttribute("use-attribute-sets"); 151 if (useSets.length() > 0) { 152 if (!Util.isValidQNames(useSets)) { 153 ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, useSets, this); 154 parser.reportError(Constants.ERROR, err); 155 } 156 setFirstElement(new UseAttributeSets(useSets, parser)); 157 } 158 159 parseChildren(parser); 160 } 161 162 /** 163 * Run type check on element name & contents 164 */ 165 public Type typeCheck(SymbolTable stable) throws TypeCheckError { 166 if (!_ignore) { 167 _name.typeCheck(stable); 168 if (_namespace != null) { 169 _namespace.typeCheck(stable); 170 } 171 } 172 typeCheckContents(stable); 173 return Type.Void; 174 } 175 176 /** 177 * This method is called when the name of the element is known at compile time. 178 * In this case, there is no need to inspect the element name at runtime to 179 * determine if a prefix exists, needs to be generated, etc. 180 */ 181 public void translateLiteral(ClassGenerator classGen, MethodGenerator methodGen) { 182 final ConstantPoolGen cpg = classGen.getConstantPool(); 183 final InstructionList il = methodGen.getInstructionList(); 184 185 if (!_ignore) { 186 il.append(methodGen.loadHandler()); 187 _name.translate(classGen, methodGen); 188 il.append(DUP2); 189 il.append(methodGen.startElement()); 190 191 if (_namespace != null) { 192 il.append(methodGen.loadHandler()); 193 il.append(new PUSH(cpg, _prefix)); 194 _namespace.translate(classGen,methodGen); 195 il.append(methodGen.namespace()); 196 } 197 } 198 199 translateContents(classGen, methodGen); 200 201 if (!_ignore) { 202 il.append(methodGen.endElement()); 203 } 204 } 205 206 /** 207 * At runtime the compilation of xsl:element results in code that: (i) 208 * evaluates the avt for the name, (ii) checks for a prefix in the name 209 * (iii) generates a new prefix and create a new qname when necessary 210 * (iv) calls startElement() on the handler (v) looks up a uri in the XML 211 * when the prefix is not known at compile time (vi) calls namespace() 212 * on the handler (vii) evaluates the contents (viii) calls endElement(). 213 */ 214 public void translate(ClassGenerator classGen, MethodGenerator methodGen) { 215 LocalVariableGen local = null; 216 final ConstantPoolGen cpg = classGen.getConstantPool(); 217 final InstructionList il = methodGen.getInstructionList(); 218 219 // Optimize translation if element name is a literal 220 if (_isLiteralName) { 221 translateLiteral(classGen, methodGen); 222 return; 223 } 224 225 if (!_ignore) { 226 227 // if the qname is an AVT, then the qname has to be checked at runtime if it is a valid qname 228 LocalVariableGen nameValue = 229 methodGen.addLocalVariable2("nameValue", 230 Util.getJCRefType(STRING_SIG), 231 null); 232 233 // store the name into a variable first so _name.translate only needs to be called once 234 _name.translate(classGen, methodGen); 235 nameValue.setStart(il.append(new ASTORE(nameValue.getIndex()))); 236 il.append(new ALOAD(nameValue.getIndex())); 237 238 // call checkQName if the name is an AVT 239 final int check = cpg.addMethodref(BASIS_LIBRARY_CLASS, "checkQName", 240 "(" 241 +STRING_SIG 242 +")V"); 243 il.append(new INVOKESTATIC(check)); 244 245 // Push handler for call to endElement() 246 il.append(methodGen.loadHandler()); 247 248 // load name value again 249 nameValue.setEnd(il.append(new ALOAD(nameValue.getIndex()))); 250 251 if (_namespace != null) { 252 _namespace.translate(classGen, methodGen); 253 } 254 else { 255 // If name is an AVT and namespace is not specified, need to 256 // look up any prefix in the stylesheet by calling 257 // BasisLibrary.lookupStylesheetQNameNamespace( 258 // name, stylesheetNode, ancestorsArray, 259 // prefixURIsIndexArray, prefixURIPairsArray, 260 // !ignoreDefaultNamespace) 261 String transletClassName = getXSLTC().getClassName(); 262 il.append(DUP); 263 il.append(new PUSH(cpg, getNodeIDForStylesheetNSLookup())); 264 il.append(new GETSTATIC(cpg.addFieldref( 265 transletClassName, 266 STATIC_NS_ANCESTORS_ARRAY_FIELD, 267 NS_ANCESTORS_INDEX_SIG))); 268 il.append(new GETSTATIC(cpg.addFieldref( 269 transletClassName, 270 STATIC_PREFIX_URIS_IDX_ARRAY_FIELD, 271 PREFIX_URIS_IDX_SIG))); 272 il.append(new GETSTATIC(cpg.addFieldref( 273 transletClassName, 274 STATIC_PREFIX_URIS_ARRAY_FIELD, 275 PREFIX_URIS_ARRAY_SIG))); 276 // Default namespace is significant 277 il.append(ICONST_0); 278 il.append( 279 new INVOKESTATIC( 280 cpg.addMethodref(BASIS_LIBRARY_CLASS, 281 LOOKUP_STYLESHEET_QNAME_NS_REF, 282 LOOKUP_STYLESHEET_QNAME_NS_SIG))); 283 } 284 285 // Push additional arguments 286 il.append(methodGen.loadHandler()); 287 il.append(methodGen.loadDOM()); 288 il.append(methodGen.loadCurrentNode()); 289 290 // Invoke BasisLibrary.startXslElemCheckQName() 291 il.append(new INVOKESTATIC( 292 cpg.addMethodref(BASIS_LIBRARY_CLASS, "startXslElement", 293 "(" + STRING_SIG 294 + STRING_SIG 295 + TRANSLET_OUTPUT_SIG 296 + DOM_INTF_SIG + "I)" + STRING_SIG))); 297 298 299 } 300 301 translateContents(classGen, methodGen); 302 303 if (!_ignore) { 304 il.append(methodGen.endElement()); 305 } 306 } 307 308 /** 309 * Override this method to make sure that xsl:attributes are not 310 * copied to output if this xsl:element is to be ignored 311 */ 312 public void translateContents(ClassGenerator classGen, 313 MethodGenerator methodGen) { 314 final int n = elementCount(); 315 for (int i = 0; i < n; i++) { 316 final SyntaxTreeNode item = 317 (SyntaxTreeNode)getContents().elementAt(i); 318 if (_ignore && item instanceof XslAttribute) continue; 319 item.translate(classGen, methodGen); 320 } 321 } 322 323 }