1 /*
2 * Copyright (c) 2003 The Visigoth Software Society. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * 3. The end-user documentation included with the redistribution, if
18 * any, must include the following acknowledgement:
19 * "This product includes software developed by the
20 * Visigoth Software Society (http://www.visigoths.org/)."
21 * Alternately, this acknowledgement may appear in the software itself,
22 * if and wherever such third-party acknowledgements normally appear.
23 *
24 * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
25 * project contributors may be used to endorse or promote products derived
26 * from this software without prior written permission. For written
27 * permission, please contact visigoths@visigoths.org.
28 *
29 * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
30 * nor may "FreeMarker" or "Visigoth" appear in their names
31 * without prior written permission of the Visigoth Software Society.
32 *
33 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
34 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36 * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
37 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
39 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
40 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
41 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
42 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
43 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 * SUCH DAMAGE.
45 * ====================================================================
46 *
47 * This software consists of voluntary contributions made by many
48 * individuals on behalf of the Visigoth Software Society. For more
49 * information on the Visigoth Software Society, please see
50 * http://www.visigoths.org/
51 */
52
53 package freemarker.core;
54
55 import java.io;
56 import java.util;
57
58 import freemarker.template;
59
60 /**
61 * An element for the unified macro/transform syntax.
62 */
63 final class UnifiedCall extends TemplateElement {
64
65 private Expression nameExp;
66 private Map namedArgs;
67 private List positionalArgs, bodyParameterNames;
68 boolean legacySyntax;
69
70 UnifiedCall(Expression nameExp,
71 Map namedArgs,
72 TemplateElement nestedBlock,
73 List bodyParameterNames)
74 {
75 this.nameExp = nameExp;
76 this.namedArgs = namedArgs;
77 this.nestedBlock = nestedBlock;
78 this.bodyParameterNames = bodyParameterNames;
79 }
80
81 UnifiedCall(Expression nameExp,
82 List positionalArgs,
83 TemplateElement nestedBlock,
84 List bodyParameterNames)
85 {
86 this.nameExp = nameExp;
87 this.positionalArgs = positionalArgs;
88 if (nestedBlock == TextBlock.EMPTY_BLOCK) {
89 nestedBlock = null;
90 }
91 this.nestedBlock = nestedBlock;
92 this.bodyParameterNames = bodyParameterNames;
93 }
94
95
96 void accept(Environment env) throws TemplateException, IOException {
97 TemplateModel tm = nameExp.getAsTemplateModel(env);
98 if (tm == Macro.DO_NOTHING_MACRO) return; // shortcut here.
99 if (tm instanceof Macro) {
100 Macro macro = (Macro) tm;
101 if (macro.isFunction && !legacySyntax) {
102 throw new TemplateException("Routine " + macro.getName() +
103 " is a function. A function can only be called " +
104 "within the evaluation of an expression.", env);
105 }
106 env.visit(macro, namedArgs, positionalArgs, bodyParameterNames,
107 nestedBlock);
108 }
109 else {
110 boolean isDirectiveModel = tm instanceof TemplateDirectiveModel;
111 if (isDirectiveModel || tm instanceof TemplateTransformModel) {
112 Map args;
113 if (namedArgs != null && !namedArgs.isEmpty()) {
114 args = new HashMap();
115 for (Iterator it = namedArgs.entrySet().iterator(); it.hasNext();) {
116 Map.Entry entry = (Map.Entry) it.next();
117 String key = (String) entry.getKey();
118 Expression valueExp = (Expression) entry.getValue();
119 TemplateModel value = valueExp.getAsTemplateModel(env);
120 args.put(key, value);
121 }
122 } else {
123 args = EmptyMap.instance;
124 }
125 if(isDirectiveModel) {
126 env.visit(nestedBlock, (TemplateDirectiveModel) tm, args,
127 bodyParameterNames);
128 }
129 else {
130 env.visit(nestedBlock, (TemplateTransformModel) tm, args);
131 }
132 }
133 else if (tm == null) {
134 throw new InvalidReferenceException(this.getStartLocation() + " " +
135 nameExp + " not found.", env);
136 } else {
137 throw new TemplateException(getStartLocation() + ": " + nameExp +
138 " is not a user-defined directive. It is a " +
139 tm.getClass().getName(), env);
140 }
141 }
142 }
143
144 public String getCanonicalForm() {
145 StringBuffer buf = new StringBuffer("<@");
146 buf.append(nameExp.getCanonicalForm());
147 if (positionalArgs != null) {
148 for (int i=0; i<positionalArgs.size(); i++) {
149 Expression arg = (Expression) positionalArgs.get(i);
150 if (i!=0) {
151 buf.append(',');
152 }
153 buf.append(' ');
154 buf.append(arg.getCanonicalForm());
155 }
156 }
157 else {
158 ArrayList keys = new ArrayList(namedArgs.keySet());
159 Collections.sort(keys);
160 for (int i=0; i<keys.size();i++) {
161 Expression arg = (Expression) namedArgs.get(keys.get(i));
162 buf.append(' ');
163 buf.append(keys.get(i));
164 buf.append('=');
165 buf.append(arg.getCanonicalForm());
166 }
167 }
168 if (nestedBlock == null) {
169 buf.append("/>");
170 }
171 else {
172 buf.append('>');
173 buf.append(nestedBlock.getCanonicalForm());
174 buf.append("</@");
175 if (nameExp instanceof Identifier || (nameExp instanceof Dot && ((Dot) nameExp).onlyHasIdentifiers())) {
176 buf.append(nameExp);
177 }
178 buf.append('>');
179 }
180 return buf.toString();
181 }
182
183 public String getDescription() {
184 return "user-directive " + nameExp;
185 }
186 /*
187 //REVISIT
188 boolean heedsOpeningWhitespace() {
189 return nestedBlock == null;
190 }
191
192 //REVISIT
193 boolean heedsTrailingWhitespace() {
194 return nestedBlock == null;
195 }*/
196 }