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 }