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