1 | package org.apache.velocity.runtime.directive; |
2 | |
3 | /* |
4 | * Copyright 2000-2001,2004 The Apache Software Foundation. |
5 | * |
6 | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | * you may not use this file except in compliance with the License. |
8 | * You may obtain a copy of the License at |
9 | * |
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | * |
12 | * Unless required by applicable law or agreed to in writing, software |
13 | * distributed under the License is distributed on an "AS IS" BASIS, |
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | * See the License for the specific language governing permissions and |
16 | * limitations under the License. |
17 | */ |
18 | |
19 | import java.io.Writer; |
20 | import java.io.IOException; |
21 | |
22 | import java.util.Iterator; |
23 | |
24 | import org.apache.velocity.runtime.RuntimeServices; |
25 | import org.apache.velocity.runtime.RuntimeConstants; |
26 | |
27 | import org.apache.velocity.context.InternalContextAdapter; |
28 | |
29 | import org.apache.velocity.runtime.parser.node.Node; |
30 | |
31 | import org.apache.velocity.exception.MethodInvocationException; |
32 | import org.apache.velocity.exception.ParseErrorException; |
33 | import org.apache.velocity.exception.ResourceNotFoundException; |
34 | |
35 | import org.apache.velocity.util.introspection.Info; |
36 | |
37 | /** |
38 | * Foreach directive used for moving through arrays, |
39 | * or objects that provide an Iterator. |
40 | * |
41 | * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a> |
42 | * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> |
43 | * @version $Id: Foreach.java,v 1.42.4.1 2004/03/03 23:22:55 geirm Exp $ |
44 | */ |
45 | public class Foreach extends Directive |
46 | { |
47 | /** |
48 | * Return name of this directive. |
49 | */ |
50 | public String getName() |
51 | { |
52 | return "foreach"; |
53 | } |
54 | |
55 | /** |
56 | * Return type of this directive. |
57 | */ |
58 | public int getType() |
59 | { |
60 | return BLOCK; |
61 | } |
62 | |
63 | /** |
64 | * The name of the variable to use when placing |
65 | * the counter value into the context. Right |
66 | * now the default is $velocityCount. |
67 | */ |
68 | private String counterName; |
69 | |
70 | /** |
71 | * What value to start the loop counter at. |
72 | */ |
73 | private int counterInitialValue; |
74 | |
75 | /** |
76 | * The reference name used to access each |
77 | * of the elements in the list object. It |
78 | * is the $item in the following: |
79 | * |
80 | * #foreach ($item in $list) |
81 | * |
82 | * This can be used class wide because |
83 | * it is immutable. |
84 | */ |
85 | private String elementKey; |
86 | |
87 | /** |
88 | * immutable, so create in init |
89 | */ |
90 | protected Info uberInfo; |
91 | |
92 | /** |
93 | * simple init - init the tree and get the elementKey from |
94 | * the AST |
95 | */ |
96 | public void init(RuntimeServices rs, InternalContextAdapter context, Node node) |
97 | throws Exception |
98 | { |
99 | super.init(rs, context, node); |
100 | |
101 | counterName = rsvc.getString(RuntimeConstants.COUNTER_NAME); |
102 | counterInitialValue = rsvc.getInt(RuntimeConstants.COUNTER_INITIAL_VALUE); |
103 | |
104 | /* |
105 | * this is really the only thing we can do here as everything |
106 | * else is context sensitive |
107 | */ |
108 | |
109 | elementKey = node.jjtGetChild(0).getFirstToken().image.substring(1); |
110 | |
111 | /* |
112 | * make an uberinfo - saves new's later on |
113 | */ |
114 | |
115 | uberInfo = new Info(context.getCurrentTemplateName(), |
116 | getLine(),getColumn()); |
117 | } |
118 | |
119 | /** |
120 | * renders the #foreach() block |
121 | */ |
122 | public boolean render(InternalContextAdapter context, |
123 | Writer writer, Node node) |
124 | throws IOException, MethodInvocationException, ResourceNotFoundException, |
125 | ParseErrorException |
126 | { |
127 | /* |
128 | * do our introspection to see what our collection is |
129 | */ |
130 | |
131 | Object listObject = node.jjtGetChild(2).value(context); |
132 | |
133 | if (listObject == null) |
134 | return false; |
135 | |
136 | Iterator i = null; |
137 | |
138 | try |
139 | { |
140 | i = rsvc.getUberspect().getIterator(listObject, uberInfo); |
141 | } |
142 | catch(Exception ee) |
143 | { |
144 | System.out.println(ee); |
145 | } |
146 | |
147 | if (i == null) |
148 | { |
149 | return false; |
150 | } |
151 | |
152 | int counter = counterInitialValue; |
153 | |
154 | /* |
155 | * save the element key if there is one, |
156 | * and the loop counter |
157 | */ |
158 | |
159 | Object o = context.get(elementKey); |
160 | Object ctr = context.get( counterName); |
161 | |
162 | while (i.hasNext()) |
163 | { |
164 | context.put( counterName , new Integer(counter)); |
165 | context.put(elementKey,i.next()); |
166 | node.jjtGetChild(3).render(context, writer); |
167 | counter++; |
168 | } |
169 | |
170 | /* |
171 | * restores the loop counter (if we were nested) |
172 | * if we have one, else just removes |
173 | */ |
174 | |
175 | if (ctr != null) |
176 | { |
177 | context.put(counterName, ctr); |
178 | } |
179 | else |
180 | { |
181 | context.remove(counterName); |
182 | } |
183 | |
184 | |
185 | /* |
186 | * restores element key if exists |
187 | * otherwise just removes |
188 | */ |
189 | |
190 | if (o != null) |
191 | { |
192 | context.put(elementKey, o); |
193 | } |
194 | else |
195 | { |
196 | context.remove(elementKey); |
197 | } |
198 | |
199 | return true; |
200 | } |
201 | } |