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 }