1 /*
   2  * Copyright (c) 2015, 2017, 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: XslAttribute.java,v 1.2.4.1 2005/09/12 11:39:32 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.GETFIELD;
  30 import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
  31 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  32 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  33 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  34 import com.sun.org.apache.bcel.internal.generic.PUSH;
  35 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  36 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
  37 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  38 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  39 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  40 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  41 import com.sun.org.apache.xml.internal.serializer.ElemDesc;
  42 import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
  43 import com.sun.org.apache.xml.internal.utils.XML11Char;
  44 import java.util.List;
  45 
  46 /**
  47  * @author Jacek Ambroziak
  48  * @author Santiago Pericas-Geertsen
  49  * @author Morten Jorgensen
  50  * @author Erwin Bolwidt <ejb@klomp.org>
  51  * @author Gunnlaugur Briem <gthb@dimon.is>
  52  * @LastModified: Oct 2017
  53  */
  54 final class XslAttribute extends Instruction {
  55 
  56     private String _prefix;
  57     private AttributeValue _name;       // name treated as AVT (7.1.3)
  58     private AttributeValueTemplate _namespace = null;
  59     private boolean _ignore = false;
  60     private boolean _isLiteral = false;  // specified name is not AVT
  61 
  62     /**
  63      * Returns the name of the attribute
  64      */
  65     public AttributeValue getName() {
  66         return _name;
  67     }
  68 
  69     /**
  70      * Displays the contents of the attribute
  71      */
  72     public void display(int indent) {
  73         indent(indent);
  74         Util.println("Attribute " + _name);
  75         displayContents(indent + IndentIncrement);
  76     }
  77 
  78     /**
  79      * Parses the attribute's contents. Special care taken for namespaces.
  80      */
  81     public void parseContents(Parser parser) {
  82         boolean generated = false;
  83         final SymbolTable stable = parser.getSymbolTable();
  84 
  85         String name = getAttribute("name");
  86         String namespace = getAttribute("namespace");
  87         QName qname = parser.getQName(name, false);
  88         final String prefix = qname.getPrefix();
  89 
  90         if (((prefix != null) && (prefix.equals(XMLNS_PREFIX)))||(name.equals(XMLNS_PREFIX))) {
  91             reportError(this, parser, ErrorMsg.ILLEGAL_ATTR_NAME_ERR, name);
  92             return;
  93         }
  94 
  95         _isLiteral = Util.isLiteral(name);
  96         if (_isLiteral) {
  97             if (!XML11Char.isXML11ValidQName(name)) {
  98                 reportError(this, parser, ErrorMsg.ILLEGAL_ATTR_NAME_ERR, name);
  99                 return;
 100             }
 101         }
 102 
 103         // Ignore attribute if preceeded by some other type of element
 104         final SyntaxTreeNode parent = getParent();
 105         final List<SyntaxTreeNode> siblings = parent.getContents();
 106         for (int i = 0; i < parent.elementCount(); i++) {
 107             SyntaxTreeNode item = siblings.get(i);
 108             if (item == this) break;
 109 
 110             // These three objects result in one or more attribute output
 111             if (item instanceof XslAttribute) continue;
 112             if (item instanceof UseAttributeSets) continue;
 113             if (item instanceof LiteralAttribute) continue;
 114             if (item instanceof Text) continue;
 115 
 116             // These objects _can_ result in one or more attribute
 117             // The output handler will generate an error if not (at runtime)
 118             if (item instanceof If) continue;
 119             if (item instanceof Choose) continue;
 120             if (item instanceof CopyOf) continue;
 121             if (item instanceof VariableBase) continue;
 122 
 123             // Report warning but do not ignore attribute
 124             reportWarning(this, parser, ErrorMsg.STRAY_ATTRIBUTE_ERR, name);
 125         }
 126 
 127         // Get namespace from namespace attribute?
 128         if (namespace != null && namespace != Constants.EMPTYSTRING) {
 129             _prefix = lookupPrefix(namespace);
 130             _namespace = new AttributeValueTemplate(namespace, parser, this);
 131         }
 132         // Get namespace from prefix in name attribute?
 133         else if (prefix != null && prefix != Constants.EMPTYSTRING) {
 134             _prefix = prefix;
 135             namespace = lookupNamespace(prefix);
 136             if (namespace != null) {
 137                 _namespace = new AttributeValueTemplate(namespace, parser, this);
 138             }
 139         }
 140 
 141         // Common handling for namespaces:
 142         if (_namespace != null) {
 143             // Generate prefix if we have none
 144             if (_prefix == null || _prefix == Constants.EMPTYSTRING) {
 145                 if (prefix != null) {
 146                     _prefix = prefix;
 147                 }
 148                 else {
 149                     _prefix = stable.generateNamespacePrefix();
 150                     generated = true;
 151                 }
 152             }
 153             else if (prefix != null && !prefix.equals(_prefix)) {
 154                 _prefix = prefix;
 155             }
 156 
 157             name = _prefix + ":" + qname.getLocalPart();
 158 
 159             /*
 160              * TODO: The namespace URI must be passed to the parent
 161              * element but we don't yet know what the actual URI is
 162              * (as we only know it as an attribute value template).
 163              */
 164             if ((parent instanceof LiteralElement) && (!generated)) {
 165                 ((LiteralElement)parent).registerNamespace(_prefix,
 166                                                            namespace,
 167                                                            stable, false);
 168             }
 169         }
 170 
 171         if (parent instanceof LiteralElement) {
 172             ((LiteralElement)parent).addAttribute(this);
 173         }
 174 
 175         _name = AttributeValue.create(this, name, parser);
 176         parseChildren(parser);
 177     }
 178 
 179     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
 180         if (!_ignore) {
 181             _name.typeCheck(stable);
 182             if (_namespace != null) {
 183                 _namespace.typeCheck(stable);
 184             }
 185             typeCheckContents(stable);
 186         }
 187         return Type.Void;
 188     }
 189 
 190     /**
 191      *
 192      */
 193     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
 194         final ConstantPoolGen cpg = classGen.getConstantPool();
 195         final InstructionList il = methodGen.getInstructionList();
 196 
 197         if (_ignore) return;
 198         _ignore = true;
 199 
 200         // Compile code that emits any needed namespace declaration
 201         if (_namespace != null) {
 202             // public void attribute(final String name, final String value)
 203             il.append(methodGen.loadHandler());
 204             il.append(new PUSH(cpg,_prefix));
 205             _namespace.translate(classGen,methodGen);
 206             il.append(methodGen.namespace());
 207         }
 208 
 209         if (!_isLiteral) {
 210             // if the qname is an AVT, then the qname has to be checked at runtime if it is a valid qname
 211             LocalVariableGen nameValue =
 212                     methodGen.addLocalVariable2("nameValue",
 213                     Util.getJCRefType(STRING_SIG),
 214                                                 null);
 215 
 216             // store the name into a variable first so _name.translate only needs to be called once
 217             _name.translate(classGen, methodGen);
 218             nameValue.setStart(il.append(new ASTORE(nameValue.getIndex())));
 219             il.append(new ALOAD(nameValue.getIndex()));
 220 
 221             // call checkQName if the name is an AVT
 222             final int check = cpg.addMethodref(BASIS_LIBRARY_CLASS, "checkAttribQName",
 223                             "("
 224                             +STRING_SIG
 225                             +")V");
 226             il.append(new INVOKESTATIC(check));
 227 
 228             // Save the current handler base on the stack
 229             il.append(methodGen.loadHandler());
 230             il.append(DUP);     // first arg to "attributes" call
 231 
 232             // load name value again
 233             nameValue.setEnd(il.append(new ALOAD(nameValue.getIndex())));
 234         } else {
 235             // Save the current handler base on the stack
 236             il.append(methodGen.loadHandler());
 237             il.append(DUP);     // first arg to "attributes" call
 238 
 239             // Push attribute name
 240             _name.translate(classGen, methodGen);// 2nd arg
 241 
 242         }
 243 
 244         // Push attribute value - shortcut for literal strings
 245         if ((elementCount() == 1) && (elementAt(0) instanceof Text)) {
 246             il.append(new PUSH(cpg, ((Text)elementAt(0)).getText()));
 247         }
 248         else {
 249             il.append(classGen.loadTranslet());
 250             il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
 251                                                    "stringValueHandler",
 252                                                    STRING_VALUE_HANDLER_SIG)));
 253             il.append(DUP);
 254             il.append(methodGen.storeHandler());
 255             // translate contents with substituted handler
 256             translateContents(classGen, methodGen);
 257             // get String out of the handler
 258             il.append(new INVOKEVIRTUAL(cpg.addMethodref(STRING_VALUE_HANDLER,
 259                                                          "getValue",
 260                                                          "()" + STRING_SIG)));
 261         }
 262 
 263         SyntaxTreeNode parent = getParent();
 264         if (parent instanceof LiteralElement
 265             && ((LiteralElement)parent).allAttributesUnique()) {
 266             int flags = 0;
 267             ElemDesc elemDesc = ((LiteralElement)parent).getElemDesc();
 268 
 269             // Set the HTML flags
 270             if (elemDesc != null && _name instanceof SimpleAttributeValue) {
 271                 String attrName = ((SimpleAttributeValue)_name).toString();
 272                 if (elemDesc.isAttrFlagSet(attrName, ElemDesc.ATTREMPTY)) {
 273                     flags = flags | SerializationHandler.HTML_ATTREMPTY;
 274                 }
 275                 else if (elemDesc.isAttrFlagSet(attrName, ElemDesc.ATTRURL)) {
 276                     flags = flags | SerializationHandler.HTML_ATTRURL;
 277                 }
 278             }
 279             il.append(new PUSH(cpg, flags));
 280             il.append(methodGen.uniqueAttribute());
 281         }
 282         else {
 283             // call "attribute"
 284             il.append(methodGen.attribute());
 285         }
 286 
 287         // Restore old handler base from stack
 288         il.append(methodGen.storeHandler());
 289 
 290 
 291 
 292     }
 293 
 294 }