View Javadoc

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