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: XslElement.java 1225842 2011-12-30 15:14:35Z mrglavas $
020     */
021    
022    package org.apache.xalan.xsltc.compiler;
023    
024    import org.apache.bcel.generic.ALOAD;
025    import org.apache.bcel.generic.ASTORE;
026    import org.apache.bcel.generic.ConstantPoolGen;
027    import org.apache.bcel.generic.GETSTATIC;
028    import org.apache.bcel.generic.INVOKESTATIC;
029    import org.apache.bcel.generic.InstructionList;
030    import org.apache.bcel.generic.LocalVariableGen;
031    import org.apache.bcel.generic.PUSH;
032    import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
033    import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
034    import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
035    import org.apache.xalan.xsltc.compiler.util.Type;
036    import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
037    import org.apache.xalan.xsltc.compiler.util.Util;
038    import org.apache.xml.utils.XML11Char;
039    
040    /**
041     * @author Jacek Ambroziak
042     * @author Santiago Pericas-Geertsen
043     * @author Morten Jorgensen
044     */
045    final class XslElement extends Instruction {
046    
047        private String  _prefix;
048        private boolean _ignore = false;
049        private boolean _isLiteralName = true;
050        private AttributeValueTemplate _name; 
051        private AttributeValueTemplate _namespace;
052    
053        /**
054         * Displays the contents of the element
055         */
056        public void display(int indent) {
057            indent(indent);
058            Util.println("Element " + _name);
059            displayContents(indent + IndentIncrement);
060        }
061    
062        /**
063         * This method is now deprecated. The new implemation of this class
064         * never declares the default NS.
065         */
066        public boolean declaresDefaultNS() {
067            return false;
068        }
069    
070        public void parseContents(Parser parser) {
071            final SymbolTable stable = parser.getSymbolTable();
072    
073            // Handle the 'name' attribute
074            String name = getAttribute("name");
075            if (name == EMPTYSTRING) {
076                ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR,
077                                            name, this);
078                parser.reportError(WARNING, msg);
079                parseChildren(parser);
080                _ignore = true;     // Ignore the element if the QName is invalid
081                return;
082            }
083    
084            // Get namespace attribute
085            String namespace = getAttribute("namespace");
086    
087            // Optimize compilation when name is known at compile time
088            _isLiteralName = Util.isLiteral(name);
089            if (_isLiteralName) {
090                if (!XML11Char.isXML11ValidQName(name)) {
091                    ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR,
092                                                name, this);
093                    parser.reportError(WARNING, msg);
094                    parseChildren(parser);
095                    _ignore = true;         // Ignore the element if the QName is invalid
096                    return;
097                }
098    
099                final QName qname = parser.getQNameSafe(name);
100                String prefix = qname.getPrefix();
101                String local = qname.getLocalPart();
102                
103                if (prefix == null) {
104                    prefix = EMPTYSTRING;
105                }
106    
107                if (!hasAttribute("namespace")) {
108                    namespace = lookupNamespace(prefix); 
109                    if (namespace == null) {
110                        ErrorMsg err = new ErrorMsg(ErrorMsg.NAMESPACE_UNDEF_ERR,
111                                                    prefix, this);
112                        parser.reportError(WARNING, err);
113                        parseChildren(parser);
114                        _ignore = true;     // Ignore the element if prefix is undeclared
115                        return;
116                    }
117                    _prefix = prefix;
118                    _namespace = new AttributeValueTemplate(namespace, parser, this);
119                }
120                else {
121                    if (prefix == EMPTYSTRING) {
122                        if (Util.isLiteral(namespace)) {
123                            prefix = lookupPrefix(namespace);
124                            if (prefix == null) {
125                                prefix = stable.generateNamespacePrefix();
126                            }
127                        }
128    
129                        // Prepend prefix to local name
130                        final StringBuffer newName = new StringBuffer(prefix);
131                        if (prefix != EMPTYSTRING) {
132                            newName.append(':');
133                        }
134                        name = newName.append(local).toString();
135                    }
136                    _prefix = prefix;
137                    _namespace = new AttributeValueTemplate(namespace, parser, this);
138                }
139            }
140            else {
141                // name attribute contains variable parts.  If there is no namespace
142                // attribute, the generated code needs to be prepared to look up
143                // any prefix in the stylesheet at run-time.
144                _namespace = (namespace == EMPTYSTRING) ? null :
145                             new AttributeValueTemplate(namespace, parser, this);
146            }
147    
148            _name = new AttributeValueTemplate(name, parser, this);
149    
150            final String useSets = getAttribute("use-attribute-sets");
151            if (useSets.length() > 0) {
152                if (!Util.isValidQNames(useSets)) {
153                    ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, useSets, this);
154                    parser.reportError(Constants.ERROR, err);       
155                }
156                setFirstElement(new UseAttributeSets(useSets, parser));
157            }
158    
159            parseChildren(parser);
160        }
161    
162        /**
163         * Run type check on element name & contents
164         */
165        public Type typeCheck(SymbolTable stable) throws TypeCheckError {
166            if (!_ignore) {
167                _name.typeCheck(stable);
168                if (_namespace != null) {
169                    _namespace.typeCheck(stable);
170                }
171            }
172            typeCheckContents(stable);
173            return Type.Void;
174        }
175    
176        /**
177         * This method is called when the name of the element is known at compile time.
178         * In this case, there is no need to inspect the element name at runtime to
179         * determine if a prefix exists, needs to be generated, etc.
180         */
181        public void translateLiteral(ClassGenerator classGen, MethodGenerator methodGen) {
182            final ConstantPoolGen cpg = classGen.getConstantPool();
183            final InstructionList il = methodGen.getInstructionList();
184    
185            if (!_ignore) {
186                il.append(methodGen.loadHandler());
187                _name.translate(classGen, methodGen);
188                il.append(DUP2);
189                il.append(methodGen.startElement());
190    
191                if (_namespace != null) {
192                    il.append(methodGen.loadHandler());
193                    il.append(new PUSH(cpg, _prefix));
194                    _namespace.translate(classGen,methodGen);
195                    il.append(methodGen.namespace());
196                }
197            }
198    
199            translateContents(classGen, methodGen);
200    
201            if (!_ignore) {
202                il.append(methodGen.endElement());
203            }
204        }
205    
206        /**
207         * At runtime the compilation of xsl:element results in code that: (i)
208         * evaluates the avt for the name, (ii) checks for a prefix in the name
209         * (iii) generates a new prefix and create a new qname when necessary
210         * (iv) calls startElement() on the handler (v) looks up a uri in the XML
211         * when the prefix is not known at compile time (vi) calls namespace() 
212         * on the handler (vii) evaluates the contents (viii) calls endElement().
213         */
214        public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
215            LocalVariableGen local = null;
216            final ConstantPoolGen cpg = classGen.getConstantPool();
217            final InstructionList il = methodGen.getInstructionList();
218    
219            // Optimize translation if element name is a literal
220            if (_isLiteralName) {
221                translateLiteral(classGen, methodGen);
222                return;
223            }
224    
225            if (!_ignore) {
226           
227                // if the qname is an AVT, then the qname has to be checked at runtime if it is a valid qname
228                LocalVariableGen nameValue =
229                        methodGen.addLocalVariable2("nameValue",
230                                                    Util.getJCRefType(STRING_SIG),
231                                                    null);
232                        
233                // store the name into a variable first so _name.translate only needs to be called once  
234                _name.translate(classGen, methodGen);
235                nameValue.setStart(il.append(new ASTORE(nameValue.getIndex())));
236                il.append(new ALOAD(nameValue.getIndex()));
237                
238                // call checkQName if the name is an AVT
239                final int check = cpg.addMethodref(BASIS_LIBRARY_CLASS, "checkQName",
240                                "("
241                                +STRING_SIG
242                                +")V");                 
243                il.append(new INVOKESTATIC(check));
244                
245                // Push handler for call to endElement()
246                il.append(methodGen.loadHandler());         
247                
248                // load name value again
249                nameValue.setEnd(il.append(new ALOAD(nameValue.getIndex())));
250                        
251                if (_namespace != null) {
252                    _namespace.translate(classGen, methodGen);
253                }
254                else {
255                    // If name is an AVT and namespace is not specified, need to
256                    // look up any prefix in the stylesheet by calling
257                    //   BasisLibrary.lookupStylesheetQNameNamespace(
258                    //                name, stylesheetNode, ancestorsArray,
259                    //                prefixURIsIndexArray, prefixURIPairsArray,
260                    //                !ignoreDefaultNamespace)
261                    String transletClassName = getXSLTC().getClassName();
262                    il.append(DUP);
263                    il.append(new PUSH(cpg, getNodeIDForStylesheetNSLookup()));
264                    il.append(new GETSTATIC(cpg.addFieldref(
265                                                 transletClassName,
266                                                 STATIC_NS_ANCESTORS_ARRAY_FIELD,
267                                                 NS_ANCESTORS_INDEX_SIG)));
268                    il.append(new GETSTATIC(cpg.addFieldref(
269                                                 transletClassName,
270                                                 STATIC_PREFIX_URIS_IDX_ARRAY_FIELD,
271                                                 PREFIX_URIS_IDX_SIG)));
272                    il.append(new GETSTATIC(cpg.addFieldref(
273                                                 transletClassName,
274                                                 STATIC_PREFIX_URIS_ARRAY_FIELD,
275                                                 PREFIX_URIS_ARRAY_SIG)));
276                    // Default namespace is significant
277                    il.append(ICONST_0);
278                    il.append(
279                        new INVOKESTATIC(
280                            cpg.addMethodref(BASIS_LIBRARY_CLASS,
281                                               LOOKUP_STYLESHEET_QNAME_NS_REF,
282                                               LOOKUP_STYLESHEET_QNAME_NS_SIG)));
283                }
284    
285                // Push additional arguments
286                il.append(methodGen.loadHandler());
287                il.append(methodGen.loadDOM());
288                il.append(methodGen.loadCurrentNode());
289            
290                // Invoke BasisLibrary.startXslElemCheckQName()
291                il.append(new INVOKESTATIC(
292                cpg.addMethodref(BASIS_LIBRARY_CLASS, "startXslElement",
293                        "(" + STRING_SIG 
294                        + STRING_SIG 
295                        + TRANSLET_OUTPUT_SIG 
296                        + DOM_INTF_SIG + "I)" + STRING_SIG)));                
297    
298    
299            }
300    
301            translateContents(classGen, methodGen);
302    
303            if (!_ignore) {
304                il.append(methodGen.endElement());
305            }
306        }
307    
308        /**
309         * Override this method to make sure that xsl:attributes are not
310         * copied to output if this xsl:element is to be ignored
311         */
312        public void translateContents(ClassGenerator classGen,
313                                      MethodGenerator methodGen) {
314            final int n = elementCount();
315            for (int i = 0; i < n; i++) {
316                final SyntaxTreeNode item =
317                    (SyntaxTreeNode)getContents().elementAt(i);
318                if (_ignore && item instanceof XslAttribute) continue;
319                item.translate(classGen, methodGen);
320            }
321        }
322    
323    }