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) 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      */
 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      */
 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 }