1 package org.apache.velocity.runtime.parser.node;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.io.IOException;
23 import java.io.Writer;
24
25 import org.apache.commons.lang.builder.ToStringBuilder;
26 import org.apache.velocity.context.InternalContextAdapter;
27 import org.apache.velocity.exception.MethodInvocationException;
28 import org.apache.velocity.exception.ParseErrorException;
29 import org.apache.velocity.exception.ResourceNotFoundException;
30 import org.apache.velocity.exception.TemplateInitException;
31 import org.apache.velocity.runtime.directive.Directive;
32 import org.apache.velocity.runtime.directive.RuntimeMacro;
33 import org.apache.velocity.runtime.directive.BlockMacro;
34 import org.apache.velocity.runtime.parser.ParseException;
35 import org.apache.velocity.runtime.parser.Parser;
36 import org.apache.velocity.util.ExceptionUtils;
37
38 /**
39 * This class is responsible for handling the pluggable
40 * directives in VTL.
41 *
42 * For example : #foreach()
43 *
44 * Please look at the Parser.jjt file which is
45 * what controls the generation of this class.
46 *
47 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
48 * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
49 * @author <a href="mailto:kav@kav.dk">Kasper Nielsen</a>
50 * @version $Id: ASTDirective.java 736677 2009-01-22 15:39:02Z nbubna $
51 */
52 public class ASTDirective extends SimpleNode
53 {
54 private Directive directive = null;
55 private String directiveName = "";
56 private boolean isDirective;
57 private boolean isInitialized;
58
59 /**
60 * @param id
61 */
62 public ASTDirective(int id)
63 {
64 super(id);
65 }
66
67 /**
68 * @param p
69 * @param id
70 */
71 public ASTDirective(Parser p, int id)
72 {
73 super(p, id);
74 }
75
76
77 /**
78 * @see org.apache.velocity.runtime.parser.node.SimpleNode#jjtAccept(org.apache.velocity.runtime.parser.node.ParserVisitor, java.lang.Object)
79 */
80 public Object jjtAccept(ParserVisitor visitor, Object data)
81 {
82 return visitor.visit(this, data);
83 }
84
85 /**
86 * @see org.apache.velocity.runtime.parser.node.SimpleNode#init(org.apache.velocity.context.InternalContextAdapter, java.lang.Object)
87 */
88 public synchronized Object init( InternalContextAdapter context, Object data)
89 throws TemplateInitException
90 {
91 /** method is synchronized to avoid concurrent directive initialization **/
92
93 if (!isInitialized)
94 {
95 super.init( context, data );
96
97 /*
98 * only do things that are not context dependent
99 */
100
101 if (parser.isDirective( directiveName ))
102 {
103 isDirective = true;
104
105 try
106 {
107 directive = (Directive) parser.getDirective( directiveName )
108 .getClass().newInstance();
109 }
110 catch (InstantiationException e)
111 {
112 throw ExceptionUtils.createRuntimeException("Couldn't initialize " +
113 "directive of class " +
114 parser.getDirective(directiveName).getClass().getName(),
115 e);
116 }
117 catch (IllegalAccessException e)
118 {
119 throw ExceptionUtils.createRuntimeException("Couldn't initialize " +
120 "directive of class " +
121 parser.getDirective(directiveName).getClass().getName(),
122 e);
123 }
124
125 directive.setLocation(getLine(), getColumn(), getTemplateName());
126 directive.init(rsvc, context,this);
127 }
128 else if( directiveName.startsWith("@") )
129 {
130 if( this.jjtGetNumChildren() > 0 )
131 {
132 // block macro call (normal macro call but has AST body)
133 directiveName = directiveName.substring(1);
134
135 directive = new BlockMacro(directiveName);
136 directive.setLocation(getLine(), getColumn(), getTemplateName());
137
138 try
139 {
140 directive.init( rsvc, context, this );
141 }
142 catch (TemplateInitException die)
143 {
144 throw new TemplateInitException(die.getMessage(),
145 (ParseException) die.getWrappedThrowable(),
146 die.getTemplateName(),
147 die.getColumnNumber() + getColumn(),
148 die.getLineNumber() + getLine());
149 }
150 isDirective = true;
151 }
152 else
153 {
154 // this is a fake block macro call without a body. e.g. #@foo
155 // just render as it is
156 isDirective = false;
157 }
158 }
159 else
160 {
161 /**
162 * Create a new RuntimeMacro
163 */
164 directive = new RuntimeMacro(directiveName);
165 directive.setLocation(getLine(), getColumn(), getTemplateName());
166
167 /**
168 * Initialize it
169 */
170 try
171 {
172 directive.init( rsvc, context, this );
173 }
174
175 /**
176 * correct the line/column number if an exception is caught
177 */
178 catch (TemplateInitException die)
179 {
180 throw new TemplateInitException(die.getMessage(),
181 (ParseException) die.getWrappedThrowable(),
182 die.getTemplateName(),
183 die.getColumnNumber() + getColumn(),
184 die.getLineNumber() + getLine());
185 }
186 isDirective = true;
187 }
188
189 isInitialized = true;
190 }
191
192 return data;
193 }
194
195 /**
196 * @see org.apache.velocity.runtime.parser.node.SimpleNode#render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer)
197 */
198 public boolean render( InternalContextAdapter context, Writer writer)
199 throws IOException,MethodInvocationException, ResourceNotFoundException, ParseErrorException
200 {
201 /*
202 * normal processing
203 */
204
205 if (isDirective)
206 {
207 directive.render(context, writer, this);
208 }
209 else
210 {
211 writer.write( "#");
212 writer.write( directiveName );
213 }
214
215 return true;
216 }
217
218 /**
219 * Sets the directive name. Used by the parser. This keeps us from having to
220 * dig it out of the token stream and gives the parse the change to override.
221 * @param str
222 */
223 public void setDirectiveName( String str )
224 {
225 directiveName = str;
226 }
227
228 /**
229 * Gets the name of this directive.
230 * @return The name of this directive.
231 */
232 public String getDirectiveName()
233 {
234 return directiveName;
235 }
236
237 /**
238 * @since 1.5
239 */
240 public String toString()
241 {
242 return new ToStringBuilder(this)
243 .appendSuper(super.toString())
244 .append("directiveName", getDirectiveName())
245 .toString();
246 }
247
248 }
249
250
251