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: VariableBase.java 528589 2007-04-13 18:50:56Z zongaro $
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.Instruction;
028    import org.apache.bcel.generic.InstructionList;
029    import org.apache.bcel.generic.INVOKESPECIAL;
030    import org.apache.bcel.generic.LocalVariableGen;
031    import org.apache.bcel.generic.NEW;
032    import org.apache.bcel.generic.PUSH;
033    import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
034    import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
035    import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
036    import org.apache.xalan.xsltc.compiler.util.NodeSetType;
037    import org.apache.xalan.xsltc.compiler.util.Type;
038    import org.apache.xalan.xsltc.compiler.util.Util;
039    import org.apache.xml.utils.XML11Char;
040    
041    /**
042     * @author Jacek Ambroziak
043     * @author Santiago Pericas-Geertsen
044     * @author Morten Jorgensen
045     * @author Erwin Bolwidt <ejb@klomp.org>
046     * @author John Howard <JohnH@schemasoft.com>
047     */
048    class VariableBase extends TopLevelElement {
049    
050        protected QName       _name;            // The name of the variable.
051        protected String      _escapedName;        // The escaped qname of the variable.
052        protected Type        _type;            // The type of this variable.
053        protected boolean     _isLocal;         // True if the variable is local.
054        protected LocalVariableGen _local;      // Reference to JVM variable
055        protected Instruction _loadInstruction; // Instruction to load JVM variable
056        protected Instruction _storeInstruction; // Instruction to load JVM variable
057        protected Expression  _select;          // Reference to variable expression
058        protected String      select;           // Textual repr. of variable expr.
059    
060        // References to this variable (when local)
061        protected Vector      _refs = new Vector(2); 
062    
063        // Dependencies to other variables/parameters (for globals only)
064        protected Vector      _dependencies = null;
065    
066        // Used to make sure parameter field is not added twice
067        protected boolean    _ignore = false;
068    
069        /**
070         * Disable this variable/parameter
071         */
072        public void disable() {
073            _ignore = true;
074        }
075    
076        /**
077         * Add a reference to this variable. Called by VariableRef when an
078         * expression contains a reference to this variable.
079         */
080        public void addReference(VariableRefBase vref) {
081            _refs.addElement(vref);
082        }
083    
084        /**
085         * Map this variable to a register
086         */
087        public void mapRegister(MethodGenerator methodGen) {
088            if (_local == null) {
089                final String name = getEscapedName(); // TODO: namespace ?
090                final org.apache.bcel.generic.Type varType = _type.toJCType();
091                _local = methodGen.addLocalVariable2(name, varType, null);
092            }
093        }
094    
095        /**
096         * Remove the mapping of this variable to a register.
097         * Called when we leave the AST scope of the variable's declaration
098         */
099        public void unmapRegister(MethodGenerator methodGen) {
100            if (_local != null) {
101                _local.setEnd(methodGen.getInstructionList().getEnd());
102                methodGen.removeLocalVariable(_local);
103                _refs = null;
104                _local = null;
105            }
106        }
107    
108        /**
109         * Returns an instruction for loading the value of this variable onto 
110         * the JVM stack.
111         */
112        public Instruction loadInstruction() {
113            final Instruction instr = _loadInstruction;
114            if (_loadInstruction == null) {
115                _loadInstruction = _type.LOAD(_local.getIndex());
116            }
117            return _loadInstruction;
118        }
119    
120        /**
121         * Returns an instruction for storing a value from the JVM stack
122         * into this variable.
123         */
124        public Instruction storeInstruction() {
125            final Instruction instr = _storeInstruction;
126            if (_storeInstruction == null) {
127                _storeInstruction = _type.STORE(_local.getIndex());
128            }
129            return _storeInstruction;
130        }
131    
132        /**
133         * Returns the expression from this variable's select attribute (if any)
134         */
135        public Expression getExpression() {
136            return(_select);
137        }
138    
139        /**
140         * Display variable as single string
141         */
142        public String toString() {
143            return("variable("+_name+")");
144        }
145    
146        /**
147         * Display variable in a full AST dump
148         */
149        public void display(int indent) {
150            indent(indent);
151            System.out.println("Variable " + _name);
152            if (_select != null) { 
153                indent(indent + IndentIncrement);
154                System.out.println("select " + _select.toString());
155            }
156            displayContents(indent + IndentIncrement);
157        }
158    
159        /**
160         * Returns the type of the variable
161         */
162        public Type getType() {
163            return _type;
164        }
165    
166        /**
167         * Returns the name of the variable or parameter as it will occur in the
168         * compiled translet.
169         */
170        public QName getName() {
171            return _name;
172        }
173    
174        /**
175         * Returns the escaped qname of the variable or parameter 
176         */
177        public String getEscapedName() {
178            return _escapedName;
179        }
180    
181        /**
182         * Set the name of the variable or paremeter. Escape all special chars.
183         */
184        public void setName(QName name) {
185            _name = name;
186            _escapedName = Util.escape(name.getStringRep());
187        }
188    
189        /**
190         * Returns the true if the variable is local
191         */
192        public boolean isLocal() {
193            return _isLocal;
194        }
195    
196        /**
197         * Parse the contents of the <xsl:decimal-format> element.
198         */
199        public void parseContents(Parser parser) {
200            // Get the 'name attribute
201            String name = getAttribute("name");
202    
203            if (name.length() > 0) {
204                if (!XML11Char.isXML11ValidQName(name)) {
205                    ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, name, this);
206                    parser.reportError(Constants.ERROR, err);           
207                }   
208                setName(parser.getQNameIgnoreDefaultNs(name));
209            }
210            else
211                reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "name");
212    
213            // Check whether variable/param of the same name is already in scope
214            VariableBase other = parser.lookupVariable(_name);
215            if ((other != null) && (other.getParent() == getParent())) {
216                reportError(this, parser, ErrorMsg.VARIABLE_REDEF_ERR, name);
217            }
218            
219            select = getAttribute("select");
220            if (select.length() > 0) {
221                _select = getParser().parseExpression(this, "select", null);
222                if (_select.isDummy()) {
223                    reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "select");
224                    return;
225                }
226            }
227    
228            // Children must be parsed first -> static scoping
229            parseChildren(parser);
230        }
231    
232        /**
233         * Compile the value of the variable, which is either in an expression in
234         * a 'select' attribute, or in the variable elements body
235         */
236        public void translateValue(ClassGenerator classGen,
237                                   MethodGenerator methodGen) {
238            // Compile expression is 'select' attribute if present
239            if (_select != null) {
240                _select.translate(classGen, methodGen);
241                // Create a CachedNodeListIterator for select expressions
242                // in a variable or parameter.
243                if (_select.getType() instanceof NodeSetType) {
244                    final ConstantPoolGen cpg = classGen.getConstantPool();
245                    final InstructionList il = methodGen.getInstructionList();
246                    
247                    final int initCNI = cpg.addMethodref(CACHED_NODE_LIST_ITERATOR_CLASS,
248                                                "<init>",
249                                                "("
250                                                +NODE_ITERATOR_SIG
251                                                +")V");
252                    il.append(new NEW(cpg.addClass(CACHED_NODE_LIST_ITERATOR_CLASS)));
253                    il.append(DUP_X1);
254                    il.append(SWAP);
255    
256                    il.append(new INVOKESPECIAL(initCNI));
257                }
258                _select.startIterator(classGen, methodGen);
259            }
260            // If not, compile result tree from parameter body if present.
261            else if (hasContents()) {
262                compileResultTree(classGen, methodGen);
263            }
264            // If neither are present then store empty string in variable
265            else {
266                final ConstantPoolGen cpg = classGen.getConstantPool();
267                final InstructionList il = methodGen.getInstructionList();
268                il.append(new PUSH(cpg, Constants.EMPTYSTRING));
269            }
270        }
271    
272    }