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 }