Source code: com/tripi/asp/ForEachNode.java
1 /**
2 * ArrowHead ASP Server
3 * This is a source file for the ArrowHead ASP Server - an 100% Java
4 * VBScript interpreter and ASP server.
5 *
6 * For more information, see http://www.tripi.com/arrowhead
7 *
8 * Copyright (C) 2002 Terence Haddock
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25 package com.tripi.asp;
26
27 import java.util.Enumeration;
28 import java.lang.reflect.Array;
29
30 import org.apache.log4j.Category;
31
32 /**
33 * The ForEachNode class handles the FOR EACH .. IN .. statement in
34 * VBScript.
35 *
36 * @author Terence Haddock
37 * @version 0.9
38 */
39 public class ForEachNode implements Node
40 {
41 /** The debugging class */
42 Category DBG = Category.getInstance(ForEachNode.class);
43
44 /** The identifier of the loop variable */
45 IdentNode ident;
46
47 /** The expression we are looping through */
48 Node expr;
49
50 /** The block to execute for each statement */
51 BlockNode block;
52
53 /**
54 * Constructor.
55 *
56 * @param ident The identifier to use as the loop variable.
57 * @param expr The expression to loop through.
58 * @param block The block of code to execute.
59 */
60 public ForEachNode(IdentNode ident, Node expr, BlockNode block)
61 {
62 this.ident = ident;
63 this.expr = expr;
64 this.block = block;
65 }
66
67 /**
68 * Get the ident we should use for the loop.
69 * @return ident for the loop.
70 */
71 public IdentNode getIdent()
72 {
73 return ident;
74 }
75
76 /**
77 * Get the expression we should use for the loop.
78 * @return expression for the loop
79 */
80 public Node getExpression()
81 {
82 return expr;
83 }
84
85 /**
86 * Get the block to use in the loop.
87 * @return block in the loop
88 */
89 public Node getBlock()
90 {
91 return block;
92 }
93
94 /**
95 * Dumps the visual representation of this script.
96 *
97 * @throws AspException is an error occurs.
98 * @see Node#dump()
99 */
100 public void dump() throws AspException
101 {
102 System.out.print("FOR EACH ");
103 ident.dump();
104 System.out.print(" IN ");
105 expr.dump();
106 System.out.println();
107 block.dump();
108 System.out.println("NEXT");
109 }
110
111 /**
112 * Prepares this statement for execution. Calls prepare on the block
113 * of statements contained within the for loop.
114 * @param context The context under which we are operating.
115 * @throws AspException if an error occurs.
116 * @see Node#prepare(AspContext)
117 */
118 public void prepare(AspContext context) throws AspException
119 {
120 block.prepare(context);
121 }
122
123 /**
124 * Executes this node.
125 * @param context The context under which we are operating.
126 * @return Return value of the expression
127 * @throws AspException if an error occurs.
128 * @see Node#execute(AspContext)
129 */
130 public Object execute(AspContext context) throws AspException
131 {
132 /* Obtain the value of the expression we are using for the loop */
133 Object obj = expr.execute(context);
134 if (DBG.isDebugEnabled()) {
135 DBG.debug("ForEach called on object: " + obj);
136 }
137 /* Dereference */
138 while (obj instanceof SimpleReference &&
139 !(obj instanceof ArrayNode) &&
140 !(obj instanceof JavaObjectNode) &&
141 !(obj instanceof SimpleMap))
142 {
143 if (DBG.isDebugEnabled()) DBG.debug("Dereferencing: " +
144 obj);
145 obj = ((SimpleReference)obj).getValue();
146 }
147 if (DBG.isDebugEnabled()) DBG.debug("Final object: " + obj);
148 /* Test if this is an instance of ArrayNode */
149 if (obj instanceof ArrayNode)
150 {
151 executeArrayNode((ArrayNode)obj, context);
152 return null;
153 }
154 /* Should be an instance of SimpleMap */
155 if (obj instanceof SimpleMap)
156 {
157 executeSimpleMap((SimpleMap)obj, context);
158 return null;
159 }
160 /* Test if this is an instance of JavaObjectNode
161 this test is last because the previous tests may override this one */
162 if (obj instanceof JavaObjectNode)
163 {
164 executeJavaObjectNode((JavaObjectNode)obj, context);
165 return null;
166 }
167 throw new AspException("FOR EACH called on non-array object: " + obj);
168 }
169
170 /**
171 * Internal function which handles the enumeration of JavaObjectNode
172 * objects. XXX This is probably a bad idea, and should be moved somewhere
173 * else (like a new class: JavaArrayNode).
174 * @param context AspContext
175 * @throws AspException on error
176 */
177 private void executeJavaObjectNode(JavaObjectNode javaObj, AspContext context)
178 throws AspException
179 {
180 /* Find the sub-object contained in this Java object */
181 Object array = javaObj.getSubObject();
182 /* This object should be a Java array */
183 if (!array.getClass().isArray())
184 throw new AspException("FOR EACH called on non-array object: " + javaObj);
185 /* Loop through all of the values */
186 for (int i = 0; i < Array.getLength(array); i++)
187 {
188 Object value = Types.coerceToNode(Array.get(array, i));
189
190 /* Set the ident to the current value */
191 context.setValue(ident, value);
192
193 /* Execute the block, handling Exit For exceptions */
194 try {
195 block.execute(context);
196 } catch (AspExitForException ex) {
197 return;
198 }
199 }
200 }
201
202 /**
203 * Internal function which handles the enumeration of ArrayNode
204 * object.
205 * @param context AspContext
206 * @throws AspException on error
207 */
208 private void executeArrayNode(ArrayNode array, AspContext context)
209 throws AspException
210 {
211 /* Obtain the upper bound of the array */
212 int upperBound = array.getUBOUND(1);
213 /* Loop through each value */
214 for (int i = 0; i <= upperBound; i++)
215 {
216 /* Create a parameter list of a single element to pass to array */
217 VarListNode vn = new VarListNode();
218 vn.append(new Integer(i));
219 /* Obtain the value at the specified index */
220 Object value = array.getIndex(vn, context);
221
222 /* Sets the ident to the current value */
223 context.setValue(ident, value);
224
225 try {
226 block.execute(context);
227 } catch (AspExitForException ex) {
228 return;
229 }
230 }
231 }
232
233 /**
234 * Internal function which handles the enumeration of SimpleMap
235 * object.
236 * @param context AspContext
237 * @throws AspException on error
238 */
239 private void executeSimpleMap(SimpleMap map, AspContext context)
240 throws AspException
241 {
242 /* Obtain the keys of the expression */
243 Enumeration keys = map.getKeys();
244 /* Loop through the elements, executing the expression for each value */
245 while (keys.hasMoreElements())
246 {
247 Object key = keys.nextElement();
248 /* Sets the ident to the current value */
249 context.setValue(ident, key);
250
251 try {
252 block.execute(context);
253 } catch (AspExitForException ex) {
254 return;
255 }
256 }
257 }
258 };
259