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    }