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.lang.reflect.InvocationTargetException;
23  
24  import org.apache.velocity.app.event.EventHandlerUtil;
25  import org.apache.velocity.context.InternalContextAdapter;
26  import org.apache.velocity.exception.MethodInvocationException;
27  import org.apache.velocity.exception.TemplateInitException;
28  import org.apache.velocity.runtime.parser.Parser;
29  import org.apache.velocity.runtime.parser.ParserVisitor;
30  import org.apache.velocity.util.introspection.Info;
31  import org.apache.velocity.util.introspection.IntrospectionCacheData;
32  import org.apache.velocity.util.introspection.VelPropertyGet;
33  
34  /**
35   *  ASTIdentifier.java
36   *
37   *  Method support for identifiers :  $foo
38   *
39   *  mainly used by ASTRefrence
40   *
41   *  Introspection is now moved to 'just in time' or at render / execution
42   *  time. There are many reasons why this has to be done, but the
43   *  primary two are   thread safety, to remove any context-derived
44   *  information from class member  variables.
45   *
46   * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
47   * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
48   * @version $Id: ASTIdentifier.java 471381 2006-11-05 08:56:58Z wglass $
49   */
50  public class ASTIdentifier extends SimpleNode
51  {
52      private String identifier = "";
53  
54      /**
55       *  This is really immutable after the init, so keep one for this node
56       */
57      protected Info uberInfo;
58  
59      /**
60       * @param id
61       */
62      public ASTIdentifier(int id)
63      {
64          super(id);
65      }
66  
67      /**
68       * @param p
69       * @param id
70       */
71      public ASTIdentifier(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.ParserVisitor, java.lang.Object)
79       */
80      public Object jjtAccept(ParserVisitor visitor, Object data)
81      {
82          return visitor.visit(this, data);
83      }
84  
85      /**
86       *  simple init - don't do anything that is context specific.
87       *  just get what we need from the AST, which is static.
88       * @param context
89       * @param data
90       * @return The data object.
91       * @throws TemplateInitException
92       */
93      public  Object init(InternalContextAdapter context, Object data)
94          throws TemplateInitException
95      {
96          super.init(context, data);
97  
98          identifier = getFirstToken().image;
99  
100         uberInfo = new Info(context.getCurrentTemplateName(),
101                 getLine(), getColumn());
102 
103         return data;
104     }
105 
106     /**
107      * @see org.apache.velocity.runtime.parser.node.SimpleNode#execute(java.lang.Object, org.apache.velocity.context.InternalContextAdapter)
108      */
109     public Object execute(Object o, InternalContextAdapter context)
110         throws MethodInvocationException
111     {
112 
113         VelPropertyGet vg = null;
114 
115         try
116         {
117             /*
118              *  first, see if we have this information cached.
119              */
120 
121             IntrospectionCacheData icd = context.icacheGet(this);
122 
123             /*
124              * if we have the cache data and the class of the object we are
125              * invoked with is the same as that in the cache, then we must
126              * be allright.  The last 'variable' is the method name, and
127              * that is fixed in the template :)
128              */
129 
130             if ( icd != null && (o != null) && (icd.contextData == o.getClass()) )
131             {
132                 vg = (VelPropertyGet) icd.thingy;
133             }
134             else
135             {
136                 /*
137                  *  otherwise, do the introspection, and cache it.  Use the
138                  *  uberspector
139                  */
140 
141                 vg = rsvc.getUberspect().getPropertyGet(o,identifier, uberInfo);
142 
143                 if (vg != null && vg.isCacheable() && (o != null))
144                 {
145                     icd = new IntrospectionCacheData();
146                     icd.contextData = o.getClass();
147                     icd.thingy = vg;
148                     context.icachePut(this,icd);
149                 }
150             }
151         }
152 
153         /**
154          * pass through application level runtime exceptions
155          */
156         catch( RuntimeException e )
157         {
158             throw e;
159         }
160         catch(Exception e)
161         {
162             log.error("ASTIdentifier.execute() : identifier = "+identifier, e);
163         }
164 
165         /*
166          *  we have no getter... punt...
167          */
168 
169         if (vg == null)
170         {
171             return null;
172         }
173 
174         /*
175          *  now try and execute.  If we get a MIE, throw that
176          *  as the app wants to get these.  If not, log and punt.
177          */
178         try
179         {
180             return vg.invoke(o);
181         }
182         catch(InvocationTargetException ite)
183         {
184             /*
185              *  if we have an event cartridge, see if it wants to veto
186              *  also, let non-Exception Throwables go...
187              */
188 
189             Throwable t = ite.getTargetException();
190             if (t instanceof Exception)
191             {
192                 try
193                 {
194                     return EventHandlerUtil.methodException(rsvc, context, o.getClass(), vg.getMethodName(),
195                             (Exception) t);
196                 }
197 
198                 /**
199                  * If the event handler throws an exception, then wrap it
200                  * in a MethodInvocationException.  Don't pass through RuntimeExceptions like other
201                  * similar catchall code blocks.
202                  */
203                 catch( Exception e )
204                 {
205                     throw new MethodInvocationException(
206                       "Invocation of method '" + vg.getMethodName() + "'"
207                       + " in  " + o.getClass()
208                       + " threw exception "
209                       + ite.getTargetException().toString(),
210                       ite.getTargetException(), vg.getMethodName(), context.getCurrentTemplateName(), this.getLine(), this.getColumn());
211                 }
212             }
213             else
214             {
215                 /*
216                  * no event cartridge to override. Just throw
217                  */
218 
219                 throw  new MethodInvocationException(
220                 "Invocation of method '" + vg.getMethodName() + "'"
221                 + " in  " + o.getClass()
222                 + " threw exception "
223                 + ite.getTargetException().toString(),
224                 ite.getTargetException(), vg.getMethodName(), context.getCurrentTemplateName(), this.getLine(), this.getColumn());
225 
226 
227             }
228         }
229         catch(IllegalArgumentException iae)
230         {
231             return null;
232         }
233         /**
234          * pass through application level runtime exceptions
235          */
236         catch( RuntimeException e )
237         {
238             throw e;
239         }
240         catch(Exception e)
241         {
242             log.error("ASTIdentifier() : exception invoking method "
243                         + "for identifier '" + identifier + "' in "
244                         + o.getClass() + " : " + e);
245         }
246 
247         return null;
248     }
249 }