1/*
2 * Copyright (c) 2015, 2016, 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
21package com.sun.org.apache.xalan.internal.xsltc.compiler;
22
23import com.sun.org.apache.bcel.internal.generic.ALOAD;
24import com.sun.org.apache.bcel.internal.generic.ASTORE;
25import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
26import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
27import com.sun.org.apache.bcel.internal.generic.InstructionList;
28import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
29import com.sun.org.apache.bcel.internal.generic.PUSH;
30import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
31import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
32import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
33import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
34import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
35import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
36import com.sun.org.apache.xml.internal.utils.XML11Char;
37
38/**
39 * @author Jacek Ambroziak
40 * @author Santiago Pericas-Geertsen
41 * @author Morten Jorgensen
42 */
43final class XslElement extends Instruction {
44
45    private String  _prefix;
46    private boolean _ignore = false;
47    private boolean _isLiteralName = true;
48    private AttributeValueTemplate _name;
49    private AttributeValueTemplate _namespace;
50
51    /**
52     * Displays the contents of the element
53     */
54    public void display(int indent) {
55        indent(indent);
56        Util.println("Element " + _name);
57        displayContents(indent + IndentIncrement);
58    }
59
60    public void parseContents(Parser parser) {
61        final SymbolTable stable = parser.getSymbolTable();
62
63        // Handle the 'name' attribute
64        String name = getAttribute("name");
65        if (name == EMPTYSTRING) {
66            ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR,
67                                        name, this);
68            parser.reportError(WARNING, msg);
69            parseChildren(parser);
70            _ignore = true;     // Ignore the element if the QName is invalid
71            return;
72        }
73
74        // Get namespace attribute
75        String namespace = getAttribute("namespace");
76
77        // Optimize compilation when name is known at compile time
78        _isLiteralName = Util.isLiteral(name);
79        if (_isLiteralName) {
80            if (!XML11Char.isXML11ValidQName(name)) {
81                ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR,
82                                            name, this);
83                parser.reportError(WARNING, msg);
84                parseChildren(parser);
85                _ignore = true;         // Ignore the element if the QName is invalid
86                return;
87            }
88
89            final QName qname = parser.getQNameSafe(name);
90            String prefix = qname.getPrefix();
91            String local = qname.getLocalPart();
92
93            if (prefix == null) {
94                prefix = EMPTYSTRING;
95            }
96
97            if (!hasAttribute("namespace")) {
98                namespace = lookupNamespace(prefix);
99                if (namespace == null) {
100                    ErrorMsg err = new ErrorMsg(ErrorMsg.NAMESPACE_UNDEF_ERR,
101                                                prefix, this);
102                    parser.reportError(WARNING, err);
103                    parseChildren(parser);
104                    _ignore = true;     // Ignore the element if prefix is undeclared
105                    return;
106                }
107                _prefix = prefix;
108                _namespace = new AttributeValueTemplate(namespace, parser, this);
109            }
110            else {
111                if (prefix == EMPTYSTRING) {
112                    if (Util.isLiteral(namespace)) {
113                        prefix = lookupPrefix(namespace);
114                        if (prefix == null) {
115                            prefix = stable.generateNamespacePrefix();
116                        }
117                    }
118
119                    // Prepend prefix to local name
120                    final StringBuffer newName = new StringBuffer(prefix);
121                    if (prefix != EMPTYSTRING) {
122                        newName.append(':');
123                    }
124                    name = newName.append(local).toString();
125                }
126                _prefix = prefix;
127                _namespace = new AttributeValueTemplate(namespace, parser, this);
128            }
129        }
130        else {
131            _namespace = (namespace == EMPTYSTRING) ? null :
132                         new AttributeValueTemplate(namespace, parser, this);
133        }
134
135        _name = new AttributeValueTemplate(name, parser, this);
136
137        final String useSets = getAttribute("use-attribute-sets");
138        if (useSets.length() > 0) {
139            if (!Util.isValidQNames(useSets)) {
140                ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, useSets, this);
141                parser.reportError(Constants.ERROR, err);
142            }
143            setFirstElement(new UseAttributeSets(useSets, parser));
144        }
145
146        parseChildren(parser);
147    }
148
149    /**
150     * Run type check on element name & contents
151     */
152    public Type typeCheck(SymbolTable stable) throws TypeCheckError {
153        if (!_ignore) {
154            _name.typeCheck(stable);
155            if (_namespace != null) {
156                _namespace.typeCheck(stable);
157            }
158        }
159        typeCheckContents(stable);
160        return Type.Void;
161    }
162
163    /**
164     * This method is called when the name of the element is known at compile time.
165     * In this case, there is no need to inspect the element name at runtime to
166     * determine if a prefix exists, needs to be generated, etc.
167     */
168    public void translateLiteral(ClassGenerator classGen, MethodGenerator methodGen) {
169        final ConstantPoolGen cpg = classGen.getConstantPool();
170        final InstructionList il = methodGen.getInstructionList();
171
172        if (!_ignore) {
173            il.append(methodGen.loadHandler());
174            _name.translate(classGen, methodGen);
175            il.append(DUP2);
176            il.append(methodGen.startElement());
177
178            if (_namespace != null) {
179                il.append(methodGen.loadHandler());
180                il.append(new PUSH(cpg, _prefix));
181                _namespace.translate(classGen,methodGen);
182                il.append(methodGen.namespace());
183            }
184        }
185
186        translateContents(classGen, methodGen);
187
188        if (!_ignore) {
189            il.append(methodGen.endElement());
190        }
191    }
192
193    /**
194     * At runtime the compilation of xsl:element results in code that: (i)
195     * evaluates the avt for the name, (ii) checks for a prefix in the name
196     * (iii) generates a new prefix and create a new qname when necessary
197     * (iv) calls startElement() on the handler (v) looks up a uri in the XML
198     * when the prefix is not known at compile time (vi) calls namespace()
199     * on the handler (vii) evaluates the contents (viii) calls endElement().
200     */
201    public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
202        final ConstantPoolGen cpg = classGen.getConstantPool();
203        final InstructionList il = methodGen.getInstructionList();
204
205        // Optimize translation if element name is a literal
206        if (_isLiteralName) {
207            translateLiteral(classGen, methodGen);
208            return;
209        }
210
211        if (!_ignore) {
212
213            // if the qname is an AVT, then the qname has to be checked at runtime if it is a valid qname
214            LocalVariableGen nameValue =
215                    methodGen.addLocalVariable2("nameValue",
216                                                Util.getJCRefType(STRING_SIG),
217                                                null);
218
219            // store the name into a variable first so _name.translate only needs to be called once
220            _name.translate(classGen, methodGen);
221            nameValue.setStart(il.append(new ASTORE(nameValue.getIndex())));
222            il.append(new ALOAD(nameValue.getIndex()));
223
224            // call checkQName if the name is an AVT
225            final int check = cpg.addMethodref(BASIS_LIBRARY_CLASS, "checkQName",
226                            "("
227                            +STRING_SIG
228                            +")V");
229            il.append(new INVOKESTATIC(check));
230
231            // Push handler for call to endElement()
232            il.append(methodGen.loadHandler());
233
234            // load name value again
235            nameValue.setEnd(il.append(new ALOAD(nameValue.getIndex())));
236
237            if (_namespace != null) {
238                _namespace.translate(classGen, methodGen);
239            }
240            else {
241                il.append(ACONST_NULL);
242            }
243
244            // Push additional arguments
245            il.append(methodGen.loadHandler());
246            il.append(methodGen.loadDOM());
247            il.append(methodGen.loadCurrentNode());
248
249            // Invoke BasisLibrary.startXslElemCheckQName()
250            il.append(new INVOKESTATIC(
251            cpg.addMethodref(BASIS_LIBRARY_CLASS, "startXslElement",
252                    "(" + STRING_SIG
253                    + STRING_SIG
254                    + TRANSLET_OUTPUT_SIG
255                    + DOM_INTF_SIG + "I)" + STRING_SIG)));
256
257
258        }
259
260        translateContents(classGen, methodGen);
261
262        if (!_ignore) {
263            il.append(methodGen.endElement());
264        }
265    }
266
267    /**
268     * Override this method to make sure that xsl:attributes are not
269     * copied to output if this xsl:element is to be ignored
270     */
271    public void translateContents(ClassGenerator classGen,
272                                  MethodGenerator methodGen) {
273        final int n = elementCount();
274        for (int i = 0; i < n; i++) {
275            final SyntaxTreeNode item = getContents().get(i);
276            if (_ignore && item instanceof XslAttribute) continue;
277            item.translate(classGen, methodGen);
278        }
279    }
280
281}
282