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: ForEach.java 468650 2006-10-28 07:03:30Z minchau $
020     */
021    
022    package org.apache.xalan.xsltc.compiler;
023    
024    import java.util.Enumeration;
025    import java.util.Vector;
026    
027    import org.apache.bcel.generic.BranchHandle;
028    import org.apache.bcel.generic.ConstantPoolGen;
029    import org.apache.bcel.generic.GOTO;
030    import org.apache.bcel.generic.IFGT;
031    import org.apache.bcel.generic.InstructionHandle;
032    import org.apache.bcel.generic.InstructionList;
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.NodeType;
038    import org.apache.xalan.xsltc.compiler.util.ReferenceType;
039    import org.apache.xalan.xsltc.compiler.util.ResultTreeType;
040    import org.apache.xalan.xsltc.compiler.util.Type;
041    import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
042    import org.apache.xalan.xsltc.compiler.util.Util;
043    
044    /**
045     * @author Jacek Ambroziak
046     * @author Santiago Pericas-Geertsen
047     * @author Morten Jorgensen
048     */
049    final class ForEach extends Instruction {
050    
051        private Expression _select;
052        private Type       _type;
053    
054        public void display(int indent) {
055            indent(indent);
056            Util.println("ForEach");
057            indent(indent + IndentIncrement);
058            Util.println("select " + _select.toString());
059            displayContents(indent + IndentIncrement);
060        }
061                    
062        public void parseContents(Parser parser) {
063            _select = parser.parseExpression(this, "select", null);
064    
065            parseChildren(parser);
066    
067            // make sure required attribute(s) have been set
068            if (_select.isDummy()) {
069                reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "select");
070            }
071        }
072            
073        public Type typeCheck(SymbolTable stable) throws TypeCheckError {
074            _type = _select.typeCheck(stable);
075    
076            if (_type instanceof ReferenceType || _type instanceof NodeType) {
077                _select = new CastExpr(_select, Type.NodeSet);
078                typeCheckContents(stable);
079                return Type.Void;
080            }
081            if (_type instanceof NodeSetType||_type instanceof ResultTreeType) {
082                typeCheckContents(stable);
083                return Type.Void;
084            }
085            throw new TypeCheckError(this);
086        }
087    
088        public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
089            final ConstantPoolGen cpg = classGen.getConstantPool();
090            final InstructionList il = methodGen.getInstructionList();
091    
092            // Save current node and current iterator on the stack
093            il.append(methodGen.loadCurrentNode());
094            il.append(methodGen.loadIterator());
095                    
096            // Collect sort objects associated with this instruction
097            final Vector sortObjects = new Vector();
098            Enumeration children = elements();
099            while (children.hasMoreElements()) {
100                final Object child = children.nextElement();
101                if (child instanceof Sort) {
102                    sortObjects.addElement(child);
103                }
104            }
105    
106            if ((_type != null) && (_type instanceof ResultTreeType)) {
107                // Store existing DOM on stack - must be restored when loop is done
108                il.append(methodGen.loadDOM());
109    
110                // <xsl:sort> cannot be applied to a result tree - issue warning
111                if (sortObjects.size() > 0) {
112                    ErrorMsg msg = new ErrorMsg(ErrorMsg.RESULT_TREE_SORT_ERR,this);
113                    getParser().reportError(WARNING, msg);
114                }
115    
116                // Put the result tree on the stack (DOM)
117                _select.translate(classGen, methodGen);
118                // Get an iterator for the whole DOM - excluding the root node
119                _type.translateTo(classGen, methodGen, Type.NodeSet);
120                // Store the result tree as the default DOM
121                il.append(SWAP);
122                il.append(methodGen.storeDOM());
123            }
124            else {
125                // Compile node iterator
126                if (sortObjects.size() > 0) {
127                    Sort.translateSortIterator(classGen, methodGen,
128                                               _select, sortObjects);
129                }
130                else {
131                    _select.translate(classGen, methodGen);
132                }
133    
134                if (_type instanceof ReferenceType == false) {
135                    il.append(methodGen.loadContextNode());
136                    il.append(methodGen.setStartNode());
137                }
138            }
139    
140    
141            // Overwrite current iterator
142            il.append(methodGen.storeIterator());
143    
144            // Give local variables (if any) default values before starting loop
145            initializeVariables(classGen, methodGen);
146    
147            final BranchHandle nextNode = il.append(new GOTO(null));
148            final InstructionHandle loop = il.append(NOP);
149    
150            translateContents(classGen, methodGen);
151                        
152            nextNode.setTarget(il.append(methodGen.loadIterator()));
153            il.append(methodGen.nextNode());
154            il.append(DUP);
155            il.append(methodGen.storeCurrentNode());
156            il.append(new IFGT(loop));
157    
158            // Restore current DOM (if result tree was used instead for this loop)
159            if ((_type != null) && (_type instanceof ResultTreeType)) {
160                il.append(methodGen.storeDOM());        
161            }
162    
163            // Restore current node and current iterator from the stack
164            il.append(methodGen.storeIterator());
165            il.append(methodGen.storeCurrentNode());
166        }
167    
168        /**
169         * The code that is generated by nested for-each loops can appear to some
170         * JVMs as if it is accessing un-initialized variables. We must add some
171         * code that pushes the default variable value on the stack and pops it
172         * into the variable slot. This is done by the Variable.initialize()
173         * method. The code that we compile for this loop looks like this:
174         *
175         *           initialize iterator
176         *           initialize variables <-- HERE!!!
177         *           goto   Iterate
178         *  Loop:    :
179         *           : (code for <xsl:for-each> contents)
180         *           :
181         *  Iterate: node = iterator.next();
182         *           if (node != END) goto Loop
183         */
184        public void initializeVariables(ClassGenerator classGen,
185                                       MethodGenerator methodGen) {
186            final int n = elementCount();
187            for (int i = 0; i < n; i++) {
188                final Object child = getContents().elementAt(i);
189                if (child instanceof Variable) {
190                    Variable var = (Variable)child;
191                    var.initialize(classGen, methodGen);
192                }
193            }
194        }
195    
196    }