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 }