001: /*
002: * Copyright (c) 2003 The Visigoth Software Society. All rights
003: * reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions
007: * are met:
008: *
009: * 1. Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: *
012: * 2. Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in
014: * the documentation and/or other materials provided with the
015: * distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowledgement:
019: * "This product includes software developed by the
020: * Visigoth Software Society (http://www.visigoths.org/)."
021: * Alternately, this acknowledgement may appear in the software itself,
022: * if and wherever such third-party acknowledgements normally appear.
023: *
024: * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
025: * project contributors may be used to endorse or promote products derived
026: * from this software without prior written permission. For written
027: * permission, please contact visigoths@visigoths.org.
028: *
029: * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
030: * nor may "FreeMarker" or "Visigoth" appear in their names
031: * without prior written permission of the Visigoth Software Society.
032: *
033: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
034: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
035: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
036: * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
037: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
038: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
039: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
040: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
041: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
042: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
043: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
044: * SUCH DAMAGE.
045: * ====================================================================
046: *
047: * This software consists of voluntary contributions made by many
048: * individuals on behalf of the Visigoth Software Society. For more
049: * information on the Visigoth Software Society, please see
050: * http://www.visigoths.org/
051: */
052:
053: package freemarker.core;
054:
055: import java.io.IOException;
056: import java.util.HashSet;
057: import java.util.Set;
058: import freemarker.template.*;
059:
060: /**
061: * An instruction that processes a list or foreach block
062: */
063: final class IteratorBlock extends TemplateElement {
064:
065: private Expression listExpression;
066: private String indexName;
067: private boolean isForEach;
068:
069: /**
070: * @param listExpression a variable referring to a sequence or collection
071: * @param indexName an arbitrary index variable name
072: * @param nestedBlock the nestedBlock to iterate over
073: */
074: IteratorBlock(Expression listExpression, String indexName,
075: TemplateElement nestedBlock, boolean isForEach) {
076: this .listExpression = listExpression;
077: this .indexName = indexName;
078: this .isForEach = isForEach;
079: this .nestedBlock = nestedBlock;
080: }
081:
082: void accept(Environment env) throws TemplateException, IOException {
083: TemplateModel baseModel = listExpression
084: .getAsTemplateModel(env);
085: if (baseModel == null) {
086: if (env.isClassicCompatible()) {
087: // Classic behavior of simply ignoring null references.
088: return;
089: }
090: assertNonNull(baseModel, listExpression, env);
091: }
092: env.visit(new Context(baseModel));
093: }
094:
095: public String getCanonicalForm() {
096: if (isForEach) {
097: StringBuffer buf = new StringBuffer("<#foreach ");
098: buf.append(indexName);
099: buf.append(" in ");
100: buf.append(listExpression.getCanonicalForm());
101: buf.append(">");
102: if (nestedBlock != null) {
103: buf.append(nestedBlock.getCanonicalForm());
104: }
105: buf.append("</#foreach>");
106: return buf.toString();
107: } else {
108: StringBuffer buf = new StringBuffer("<#list ");
109: buf.append(listExpression.getCanonicalForm());
110: buf.append(" as ");
111: buf.append(indexName);
112: buf.append(">");
113: if (nestedBlock != null) {
114: buf.append(nestedBlock.getCanonicalForm());
115: }
116: buf.append("</#list>");
117: return buf.toString();
118: }
119: }
120:
121: public String getDescription() {
122: if (isForEach) {
123: return "foreach " + indexName + " in " + listExpression;
124:
125: } else {
126: return "list " + listExpression + " as " + indexName;
127: }
128: }
129:
130: /**
131: * A helper class that holds the context of the loop.
132: */
133:
134: class Context implements LocalContext {
135: private boolean hasNext;
136: private TemplateModel loopVar;
137: private int index;
138: private Set variableNames = null;
139: private TemplateModel list;
140:
141: Context(TemplateModel list) {
142: this .list = list;
143: }
144:
145: void runLoop(Environment env) throws TemplateException,
146: IOException {
147: if (list instanceof TemplateCollectionModel) {
148: TemplateCollectionModel baseListModel = (TemplateCollectionModel) list;
149: TemplateModelIterator it = baseListModel.iterator();
150: hasNext = it.hasNext();
151: while (hasNext) {
152: loopVar = it.next();
153: hasNext = it.hasNext();
154: if (nestedBlock != null) {
155: env.visit(nestedBlock);
156: }
157: index++;
158: }
159: } else if (list instanceof TemplateSequenceModel) {
160: TemplateSequenceModel tsm = (TemplateSequenceModel) list;
161: int size = tsm.size();
162: for (index = 0; index < size; index++) {
163: loopVar = tsm.get(index);
164: hasNext = (size > index + 1);
165: if (nestedBlock != null) {
166: env.visit(nestedBlock);
167: }
168: }
169: } else if (env.isClassicCompatible()) {
170: loopVar = list;
171: if (nestedBlock != null) {
172: env.visit(nestedBlock);
173: }
174: } else {
175: throw invalidTypeException(list, listExpression, env,
176: "collection or sequence");
177: }
178: }
179:
180: public TemplateModel getLocalVariable(String name) {
181: if (name.startsWith(indexName)) {
182: switch (name.length() - indexName.length()) {
183: case 0:
184: return loopVar;
185: case 6:
186: if (name.endsWith("_index")) {
187: return new SimpleNumber(index);
188: }
189: break;
190: case 9:
191: if (name.endsWith("_has_next")) {
192: return hasNext ? TemplateBooleanModel.TRUE
193: : TemplateBooleanModel.FALSE;
194: }
195: break;
196: }
197: }
198: return null;
199: }
200:
201: public Set getLocalVariableNames() {
202: if (variableNames == null) {
203: variableNames = new HashSet();
204: variableNames.add(indexName);
205: variableNames.add(indexName + "_index");
206: variableNames.add(indexName + "_has_next");
207: }
208: return variableNames;
209: }
210: }
211: }
|