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 }