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: CallTemplate.java 1225842 2011-12-30 15:14:35Z mrglavas $
020     */
021    
022    package org.apache.xalan.xsltc.compiler;
023    
024    import java.util.Vector;
025    
026    import org.apache.bcel.generic.ConstantPoolGen;
027    import org.apache.bcel.generic.INVOKEVIRTUAL;
028    import org.apache.bcel.generic.InstructionList;
029    import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
030    import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
031    import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
032    import org.apache.xalan.xsltc.compiler.util.Type;
033    import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
034    import org.apache.xalan.xsltc.compiler.util.Util;
035    import org.apache.xml.utils.XML11Char;
036    
037    /**
038     * @author Jacek Ambroziak
039     * @author Santiago Pericas-Geertsen
040     * @author Erwin Bolwidt <ejb@klomp.org>
041     */
042    final class CallTemplate extends Instruction {
043        
044        /**
045         * Name of template to call.
046         */
047        private QName _name;
048        
049        /** 
050         * The array of effective parameters in this CallTemplate. An object in 
051         * this array can be either a WithParam or a Param if no WithParam 
052         * exists for a particular parameter.
053         */
054        private Object[] _parameters = null;
055            
056        /**
057         * The corresponding template which this CallTemplate calls.
058         */
059        private Template _calleeTemplate = null;
060        
061        public void display(int indent) {
062            indent(indent);
063            System.out.print("CallTemplate");
064            Util.println(" name " + _name);
065            displayContents(indent + IndentIncrement);
066        }
067                    
068        public boolean hasWithParams() {
069            return elementCount() > 0;
070        }
071    
072        public void parseContents(Parser parser) {
073            final String name = getAttribute("name");
074            if (name.length() > 0) {
075                if (!XML11Char.isXML11ValidQName(name)) {
076                    ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, name, this);
077                    parser.reportError(Constants.ERROR, err);           
078                }                
079                _name = parser.getQNameIgnoreDefaultNs(name);
080            }
081            else {
082                reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "name");              
083            }
084            parseChildren(parser);
085        }
086                    
087        /**
088         * Verify that a template with this name exists.
089         */
090        public Type typeCheck(SymbolTable stable) throws TypeCheckError {
091            final Template template = stable.lookupTemplate(_name);
092            if (template != null) {
093                typeCheckContents(stable);
094            }
095            else {
096                ErrorMsg err = new ErrorMsg(ErrorMsg.TEMPLATE_UNDEF_ERR,_name,this);
097                throw new TypeCheckError(err);
098            }
099            return Type.Void;
100        }
101    
102        public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
103            final Stylesheet stylesheet = classGen.getStylesheet();
104            final ConstantPoolGen cpg = classGen.getConstantPool();
105            final InstructionList il = methodGen.getInstructionList();
106    
107            // If there are Params in the stylesheet or WithParams in this call?
108            if (stylesheet.hasLocalParams() || hasContents()) {
109                _calleeTemplate = getCalleeTemplate();
110                
111                // Build the parameter list if the called template is simple named
112                if (_calleeTemplate != null) {
113                    buildParameterList();
114                }
115                // This is only needed when the called template is not
116                // a simple named template.
117                else {
118                    // Push parameter frame
119                    final int push = cpg.addMethodref(TRANSLET_CLASS, 
120                                                      PUSH_PARAM_FRAME,
121                                                      PUSH_PARAM_FRAME_SIG);
122                    il.append(classGen.loadTranslet());
123                    il.append(new INVOKEVIRTUAL(push));
124                    translateContents(classGen, methodGen);
125                }
126            }
127    
128            // Generate a valid Java method name
129            final String className = stylesheet.getClassName();
130            String methodName = Util.escape(_name.toString());
131    
132            // Load standard arguments
133            il.append(classGen.loadTranslet());
134            il.append(methodGen.loadDOM());
135            il.append(methodGen.loadIterator());
136            il.append(methodGen.loadHandler());
137            il.append(methodGen.loadCurrentNode());
138            
139            // Initialize prefix of method signature
140            StringBuffer methodSig = new StringBuffer("(" + DOM_INTF_SIG 
141                + NODE_ITERATOR_SIG + TRANSLET_OUTPUT_SIG + NODE_SIG);
142            
143            // If calling a simply named template, push actual arguments
144            if (_calleeTemplate != null) {
145                Vector calleeParams = _calleeTemplate.getParameters();
146                int numParams = _parameters.length;
147                
148                for (int i = 0; i < numParams; i++) {
149                    SyntaxTreeNode node = (SyntaxTreeNode)_parameters[i];
150                    methodSig.append(OBJECT_SIG);   // append Object to signature
151                    
152                    // Push 'null' if Param to indicate no actual parameter specified
153                    if (node instanceof Param) {
154                        il.append(ACONST_NULL);
155                    }
156                    else {  // translate WithParam
157                        node.translate(classGen, methodGen);
158                    }
159                }
160            }
161    
162            // Complete signature and generate invokevirtual call
163            methodSig.append(")V");
164            il.append(new INVOKEVIRTUAL(cpg.addMethodref(className,
165                                                         methodName,
166                                                         methodSig.toString())));
167            
168            // Do not need to call Translet.popParamFrame() if we are
169            // calling a simple named template.
170            if (_calleeTemplate == null && (stylesheet.hasLocalParams() || hasContents())) {
171                // Pop parameter frame
172                final int pop = cpg.addMethodref(TRANSLET_CLASS,
173                                                 POP_PARAM_FRAME,
174                                                 POP_PARAM_FRAME_SIG);
175                il.append(classGen.loadTranslet());
176                il.append(new INVOKEVIRTUAL(pop));
177            }
178        }
179        
180        /**
181         * Return the simple named template which this CallTemplate calls.
182         * Return false if there is no matched template or the matched
183         * template is not a simple named template.
184         */
185        public Template getCalleeTemplate() {
186            Template foundTemplate
187                = getXSLTC().getParser().getSymbolTable().lookupTemplate(_name);
188    
189            return foundTemplate.isSimpleNamedTemplate() ? foundTemplate : null;
190        }
191        
192        /**
193         * Build the list of effective parameters in this CallTemplate.
194         * The parameters of the called template are put into the array first.
195         * Then we visit the WithParam children of this CallTemplate and replace
196         * the Param with a corresponding WithParam having the same name.
197         */
198        private void buildParameterList() {         
199            // Put the parameters from the called template into the array first.
200            // This is to ensure the order of the parameters.
201            Vector defaultParams = _calleeTemplate.getParameters();
202            int numParams = defaultParams.size();
203            _parameters = new Object[numParams];
204            for (int i = 0; i < numParams; i++) {
205                _parameters[i] = defaultParams.elementAt(i);
206            }
207                            
208            // Replace a Param with a WithParam if they have the same name.
209            int count = elementCount();
210            for (int i = 0; i < count; i++) {
211                Object node = elementAt(i);
212                
213                // Ignore if not WithParam
214                if (node instanceof WithParam) {
215                    WithParam withParam = (WithParam)node;
216                    QName name = withParam.getName();
217                    
218                    // Search for a Param with the same name
219                    for (int k = 0; k < numParams; k++) {
220                        Object object = _parameters[k];
221                        if (object instanceof Param 
222                            && ((Param)object).getName().equals(name)) {
223                            withParam.setDoParameterOptimization(true);
224                            _parameters[k] = withParam;
225                            break;
226                        }
227                        else if (object instanceof WithParam 
228                            && ((WithParam)object).getName().equals(name)) {
229                            withParam.setDoParameterOptimization(true);
230                            _parameters[k] = withParam;                     
231                            break;
232                        }
233                    }               
234                }
235            }
236         }
237    }
238