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