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.*;
057: import freemarker.template.*;
058:
059: /**
060: * An element representing a macro declaration.
061: */
062: public final class Macro extends TemplateElement implements
063: TemplateModel {
064: private final String name;
065: private final String[] argumentNames;
066: private Map args;
067: private String catchAll;
068: boolean isFunction;
069: static final Macro DO_NOTHING_MACRO = new Macro(".pass",
070: Collections.EMPTY_LIST,
071: freemarker.template.utility.Collections12.EMPTY_MAP,
072: TextBlock.EMPTY_BLOCK);
073:
074: Macro(String name, List argumentNames, Map args,
075: TemplateElement nestedBlock) {
076: this .name = name;
077: this .argumentNames = (String[]) argumentNames
078: .toArray(new String[argumentNames.size()]);
079: this .args = args;
080: this .nestedBlock = nestedBlock;
081: }
082:
083: public String getCatchAll() {
084: return catchAll;
085: }
086:
087: public void setCatchAll(String value) {
088: catchAll = value;
089: }
090:
091: public String[] getArgumentNames() {
092: return argumentNames;
093: }
094:
095: boolean hasArgNamed(String name) {
096: return args.containsKey(name);
097: }
098:
099: public String getName() {
100: return name;
101: }
102:
103: void accept(Environment env) {
104: env.visitMacroDef(this );
105: }
106:
107: public String getCanonicalForm() {
108: StringBuffer buf = new StringBuffer("<#macro ");
109: buf.append(name);
110: buf.append("(");
111: int size = argumentNames.length;
112: for (int i = 0; i < size; i++) {
113: buf.append(argumentNames[i]);
114: if (i != (size - 1)) {
115: buf.append(",");
116: }
117: }
118: buf.append(")>");
119: if (nestedBlock != null) {
120: buf.append(nestedBlock.getCanonicalForm());
121: }
122: buf.append("</#macro>");
123: return buf.toString();
124: }
125:
126: public String getDescription() {
127: return "macro " + name;
128: }
129:
130: public boolean isFunction() {
131: return isFunction;
132: }
133:
134: class Context implements LocalContext {
135: Environment.Namespace localVars;
136: TemplateElement body;
137: Environment.Namespace bodyNamespace;
138: List bodyParameterNames;
139: Context prevMacroContext;
140: ArrayList prevLocalContextStack;
141:
142: Context(Environment env, TemplateElement body,
143: List bodyParameterNames) {
144: this .localVars = env.new Namespace();
145: this .prevMacroContext = env.getCurrentMacroContext();
146: this .bodyNamespace = env.getCurrentNamespace();
147: this .prevLocalContextStack = env.getLocalContextStack();
148: this .body = body;
149: this .bodyParameterNames = bodyParameterNames;
150: }
151:
152: Macro getMacro() {
153: return Macro.this ;
154: }
155:
156: void runMacro(Environment env) throws TemplateException,
157: IOException {
158: sanityCheck(env);
159: // Set default values for unspecified parameters
160: if (nestedBlock != null) {
161: env.visit(nestedBlock);
162: }
163: }
164:
165: // Set default parameters, check if all the required parameters are defined.
166: void sanityCheck(Environment env) throws TemplateException {
167: for (Iterator it = args.entrySet().iterator(); it.hasNext();) {
168: Map.Entry entry = (Map.Entry) it.next();
169: String argName = (String) entry.getKey();
170: if (localVars.get(argName) == null) {
171: Expression valueExp = (Expression) entry.getValue();
172: if (valueExp != null) {
173: TemplateModel tm = valueExp
174: .getAsTemplateModel(env);
175: assertNonNull(tm, valueExp, env);
176: localVars.put(argName, tm);
177: } else {
178: throw new TemplateException(
179: "Error executing macro: " + name
180: + "\nrequired parameter: "
181: + argName
182: + " is not specified.", env);
183: }
184: }
185: }
186: }
187:
188: /**
189: * @return the local variable of the given name
190: * or null if it doesn't exist.
191: */
192: public TemplateModel getLocalVariable(String name)
193: throws TemplateModelException {
194: return localVars.get(name);
195: }
196:
197: Environment.Namespace getLocals() {
198: return localVars;
199: }
200:
201: /**
202: * Set a local variable in this macro
203: */
204: void setLocalVar(String name, TemplateModel var) {
205: localVars.put(name, var);
206: }
207:
208: public Set getLocalVariableNames()
209: throws TemplateModelException {
210: HashSet result = new HashSet();
211: for (TemplateModelIterator it = localVars.keys().iterator(); it
212: .hasNext();) {
213: result.add(it.next().toString());
214: }
215: return result;
216: }
217: }
218: }
|