1/*
2 * Copyright (c) 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.CHECKCAST;
26import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
27import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
28import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
29import com.sun.org.apache.bcel.internal.generic.InstructionList;
30import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
31import com.sun.org.apache.bcel.internal.generic.PUSH;
32import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
33import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
34import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
35import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ReferenceType;
36import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
37import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
38import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
39import com.sun.org.apache.xml.internal.utils.XML11Char;
40
41/**
42 * @author Jacek Ambroziak
43 * @author Santiago Pericas-Geertsen
44 * @author Morten Jorgensen
45 * @author John Howard <JohnH@schemasoft.com>
46 */
47final class WithParam extends Instruction {
48
49    /**
50     * Parameter's name.
51     */
52    private QName _name;
53
54    /**
55     * The escaped qname of the with-param.
56     */
57    protected String _escapedName;
58
59    /**
60     * Parameter's default value.
61     */
62    private Expression _select;
63
64    /**
65     * Reference to JVM variable holding temporary result tree.
66     */
67    private LocalVariableGen _domAdapter;
68
69    /**
70     * %OPT% This is set to true when the WithParam is used in a CallTemplate
71     * for a simple named template. If this is true, the parameters are
72     * passed to the named template through method arguments rather than
73     * using the expensive Translet.addParameter() call.
74     */
75    private boolean _doParameterOptimization = false;
76
77    /**
78     * Displays the contents of this element
79     */
80    public void display(int indent) {
81        indent(indent);
82        Util.println("with-param " + _name);
83        if (_select != null) {
84            indent(indent + IndentIncrement);
85            Util.println("select " + _select.toString());
86        }
87        displayContents(indent + IndentIncrement);
88    }
89
90    /**
91     * Returns the escaped qname of the parameter
92     */
93    public String getEscapedName() {
94        return _escapedName;
95    }
96
97    /**
98     * Return the name of this WithParam.
99     */
100    public QName getName() {
101        return _name;
102    }
103
104    /**
105     * Set the name of the variable or paremeter. Escape all special chars.
106     */
107    public void setName(QName name) {
108        _name = name;
109        _escapedName = Util.escape(name.getStringRep());
110    }
111
112    /**
113     * Set the do parameter optimization flag
114     */
115    public void setDoParameterOptimization(boolean flag) {
116        _doParameterOptimization = flag;
117    }
118
119    /**
120     * The contents of a <xsl:with-param> elements are either in the element's
121     * 'select' attribute (this has precedence) or in the element body.
122     */
123    public void parseContents(Parser parser) {
124        final String name = getAttribute("name");
125        if (name.length() > 0) {
126            if (!XML11Char.isXML11ValidQName(name)) {
127                ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, name,
128                                            this);
129                parser.reportError(Constants.ERROR, err);
130            }
131            setName(parser.getQNameIgnoreDefaultNs(name));
132        } else {
133            reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "name");
134        }
135
136        final String select = getAttribute("select");
137        if (select.length() > 0) {
138            _select = parser.parseExpression(this, "select", null);
139        }
140
141        parseChildren(parser);
142    }
143
144    /**
145     * Type-check either the select attribute or the element body, depending
146     * on which is in use.
147     */
148    public Type typeCheck(SymbolTable stable) throws TypeCheckError {
149        if (_select != null) {
150            final Type tselect = _select.typeCheck(stable);
151            if (tselect instanceof ReferenceType == false) {
152                _select = new CastExpr(_select, Type.Reference);
153            }
154        } else {
155            typeCheckContents(stable);
156        }
157        return Type.Void;
158    }
159
160    /**
161     * Compile the value of the parameter, which is either in an expression in
162     * a 'select' attribute, or in the with-param element's body
163     */
164    public void translateValue(ClassGenerator classGen,
165                               MethodGenerator methodGen)
166    {
167        // Compile expression is 'select' attribute if present
168        if (_select != null) {
169            _select.translate(classGen, methodGen);
170            _select.startIterator(classGen, methodGen);
171        // If not, compile result tree from parameter body if present.
172        // Store result tree into local variable for releasing it later
173        } else if (hasContents()) {
174            final InstructionList il = methodGen.getInstructionList();
175            compileResultTree(classGen, methodGen);
176            _domAdapter = methodGen.addLocalVariable2("@" + _escapedName,
177                                                      Type.ResultTree.toJCType(),
178                                                      il.getEnd());
179            il.append(DUP);
180            il.append(new ASTORE(_domAdapter.getIndex()));
181        // If neither are present then store empty string in parameter slot
182        } else {
183            final ConstantPoolGen cpg = classGen.getConstantPool();
184            final InstructionList il = methodGen.getInstructionList();
185            il.append(new PUSH(cpg, Constants.EMPTYSTRING));
186        }
187    }
188
189    /**
190     * This code generates a sequence of bytecodes that call the
191     * addParameter() method in AbstractTranslet. The method call will add
192     * (or update) the parameter frame with the new parameter value.
193     */
194    public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
195        final ConstantPoolGen cpg = classGen.getConstantPool();
196        final InstructionList il = methodGen.getInstructionList();
197
198        // Translate the value and put it on the stack
199        if (_doParameterOptimization) {
200            translateValue(classGen, methodGen);
201            return;
202        }
203
204        // Make name acceptable for use as field name in class
205        String name = Util.escape(getEscapedName());
206
207        // Load reference to the translet (method is in AbstractTranslet)
208        il.append(classGen.loadTranslet());
209
210        // Load the name of the parameter
211        il.append(new PUSH(cpg, name)); // TODO: namespace ?
212        // Generete the value of the parameter (use value in 'select' by def.)
213        translateValue(classGen, methodGen);
214        // Mark this parameter value is not being the default value
215        il.append(new PUSH(cpg, false));
216        // Pass the parameter to the template
217        il.append(new INVOKEVIRTUAL(cpg.addMethodref(TRANSLET_CLASS,
218                                                     ADD_PARAMETER,
219                                                     ADD_PARAMETER_SIG)));
220        il.append(POP); // cleanup stack
221    }
222
223    /**
224     * Release the compiled result tree.
225     */
226    public void releaseResultTree(ClassGenerator classGen,
227                                  MethodGenerator methodGen)
228    {
229        if (_domAdapter != null) {
230            final ConstantPoolGen cpg = classGen.getConstantPool();
231            final InstructionList il = methodGen.getInstructionList();
232            if (classGen.getStylesheet().callsNodeset() &&
233                classGen.getDOMClass().equals(MULTI_DOM_CLASS))
234            {
235                final int removeDA =
236                    cpg.addMethodref(MULTI_DOM_CLASS, "removeDOMAdapter",
237                                     "(" + DOM_ADAPTER_SIG + ")V");
238                il.append(methodGen.loadDOM());
239                il.append(new CHECKCAST(cpg.addClass(MULTI_DOM_CLASS)));
240                il.append(new ALOAD(_domAdapter.getIndex()));
241                il.append(new CHECKCAST(cpg.addClass(DOM_ADAPTER_CLASS)));
242                il.append(new INVOKEVIRTUAL(removeDA));
243            }
244            final int release =
245                cpg.addInterfaceMethodref(DOM_IMPL_CLASS, "release", "()V");
246            il.append(new ALOAD(_domAdapter.getIndex()));
247            il.append(new INVOKEINTERFACE(release, 1));
248            _domAdapter.setEnd(il.getEnd());
249            methodGen.removeLocalVariable(_domAdapter);
250            _domAdapter = null;
251        }
252    }
253}
254