001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: XslAttribute.java 468650 2006-10-28 07:03:30Z minchau $ 020 */ 021 022 package org.apache.xalan.xsltc.compiler; 023 024 import java.util.Vector; 025 026 import org.apache.bcel.generic.ALOAD; 027 import org.apache.bcel.generic.ASTORE; 028 import org.apache.bcel.generic.ConstantPoolGen; 029 import org.apache.bcel.generic.GETFIELD; 030 import org.apache.bcel.generic.INVOKESTATIC; 031 import org.apache.bcel.generic.INVOKEVIRTUAL; 032 import org.apache.bcel.generic.InstructionList; 033 import org.apache.bcel.generic.LocalVariableGen; 034 import org.apache.bcel.generic.PUSH; 035 import org.apache.xalan.xsltc.compiler.util.ClassGenerator; 036 import org.apache.xalan.xsltc.compiler.util.ErrorMsg; 037 import org.apache.xalan.xsltc.compiler.util.MethodGenerator; 038 import org.apache.xalan.xsltc.compiler.util.Type; 039 import org.apache.xalan.xsltc.compiler.util.TypeCheckError; 040 import org.apache.xalan.xsltc.compiler.util.Util; 041 042 import org.apache.xml.serializer.ElemDesc; 043 import org.apache.xml.serializer.SerializationHandler; 044 import org.apache.xml.utils.XML11Char; 045 046 /** 047 * @author Jacek Ambroziak 048 * @author Santiago Pericas-Geertsen 049 * @author Morten Jorgensen 050 * @author Erwin Bolwidt <ejb@klomp.org> 051 * @author Gunnlaugur Briem <gthb@dimon.is> 052 */ 053 final class XslAttribute extends Instruction { 054 055 private String _prefix; 056 private AttributeValue _name; // name treated as AVT (7.1.3) 057 private AttributeValueTemplate _namespace = null; 058 private boolean _ignore = false; 059 private boolean _isLiteral = false; // specified name is not AVT 060 061 /** 062 * Returns the name of the attribute 063 */ 064 public AttributeValue getName() { 065 return _name; 066 } 067 068 /** 069 * Displays the contents of the attribute 070 */ 071 public void display(int indent) { 072 indent(indent); 073 Util.println("Attribute " + _name); 074 displayContents(indent + IndentIncrement); 075 } 076 077 /** 078 * Parses the attribute's contents. Special care taken for namespaces. 079 */ 080 public void parseContents(Parser parser) { 081 boolean generated = false; 082 final SymbolTable stable = parser.getSymbolTable(); 083 084 String name = getAttribute("name"); 085 String namespace = getAttribute("namespace"); 086 QName qname = parser.getQName(name, false); 087 final String prefix = qname.getPrefix(); 088 089 if (((prefix != null) && (prefix.equals(XMLNS_PREFIX)))||(name.equals(XMLNS_PREFIX))) { 090 reportError(this, parser, ErrorMsg.ILLEGAL_ATTR_NAME_ERR, name); 091 return; 092 } 093 094 _isLiteral = Util.isLiteral(name); 095 if (_isLiteral) { 096 if (!XML11Char.isXML11ValidQName(name)) { 097 reportError(this, parser, ErrorMsg.ILLEGAL_ATTR_NAME_ERR, name); 098 return; 099 } 100 } 101 102 // Ignore attribute if preceeded by some other type of element 103 final SyntaxTreeNode parent = getParent(); 104 final Vector siblings = parent.getContents(); 105 for (int i = 0; i < parent.elementCount(); i++) { 106 SyntaxTreeNode item = (SyntaxTreeNode)siblings.elementAt(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 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 */ 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 }