001: package org.apache.velocity.runtime.parser.node;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.lang.reflect.InvocationTargetException;
023:
024: import org.apache.velocity.app.event.EventHandlerUtil;
025: import org.apache.velocity.context.InternalContextAdapter;
026: import org.apache.velocity.exception.MethodInvocationException;
027: import org.apache.velocity.exception.TemplateInitException;
028: import org.apache.velocity.runtime.parser.Parser;
029: import org.apache.velocity.runtime.parser.ParserVisitor;
030: import org.apache.velocity.util.introspection.Info;
031: import org.apache.velocity.util.introspection.IntrospectionCacheData;
032: import org.apache.velocity.util.introspection.VelPropertyGet;
033:
034: /**
035: * ASTIdentifier.java
036: *
037: * Method support for identifiers : $foo
038: *
039: * mainly used by ASTRefrence
040: *
041: * Introspection is now moved to 'just in time' or at render / execution
042: * time. There are many reasons why this has to be done, but the
043: * primary two are thread safety, to remove any context-derived
044: * information from class member variables.
045: *
046: * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
047: * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
048: * @version $Id: ASTIdentifier.java 471381 2006-11-05 08:56:58Z wglass $
049: */
050: public class ASTIdentifier extends SimpleNode {
051: private String identifier = "";
052:
053: /**
054: * This is really immutable after the init, so keep one for this node
055: */
056: protected Info uberInfo;
057:
058: /**
059: * @param id
060: */
061: public ASTIdentifier(int id) {
062: super (id);
063: }
064:
065: /**
066: * @param p
067: * @param id
068: */
069: public ASTIdentifier(Parser p, int id) {
070: super (p, id);
071: }
072:
073: /**
074: * @see org.apache.velocity.runtime.parser.node.SimpleNode#jjtAccept(org.apache.velocity.runtime.parser.ParserVisitor, java.lang.Object)
075: */
076: public Object jjtAccept(ParserVisitor visitor, Object data) {
077: return visitor.visit(this , data);
078: }
079:
080: /**
081: * simple init - don't do anything that is context specific.
082: * just get what we need from the AST, which is static.
083: * @param context
084: * @param data
085: * @return The data object.
086: * @throws TemplateInitException
087: */
088: public Object init(InternalContextAdapter context, Object data)
089: throws TemplateInitException {
090: super .init(context, data);
091:
092: identifier = getFirstToken().image;
093:
094: uberInfo = new Info(context.getCurrentTemplateName(),
095: getLine(), getColumn());
096:
097: return data;
098: }
099:
100: /**
101: * @see org.apache.velocity.runtime.parser.node.SimpleNode#execute(java.lang.Object, org.apache.velocity.context.InternalContextAdapter)
102: */
103: public Object execute(Object o, InternalContextAdapter context)
104: throws MethodInvocationException {
105:
106: VelPropertyGet vg = null;
107:
108: try {
109: /*
110: * first, see if we have this information cached.
111: */
112:
113: IntrospectionCacheData icd = context.icacheGet(this );
114:
115: /*
116: * if we have the cache data and the class of the object we are
117: * invoked with is the same as that in the cache, then we must
118: * be allright. The last 'variable' is the method name, and
119: * that is fixed in the template :)
120: */
121:
122: if (icd != null && (o != null)
123: && (icd.contextData == o.getClass())) {
124: vg = (VelPropertyGet) icd.thingy;
125: } else {
126: /*
127: * otherwise, do the introspection, and cache it. Use the
128: * uberspector
129: */
130:
131: vg = rsvc.getUberspect().getPropertyGet(o, identifier,
132: uberInfo);
133:
134: if (vg != null && vg.isCacheable() && (o != null)) {
135: icd = new IntrospectionCacheData();
136: icd.contextData = o.getClass();
137: icd.thingy = vg;
138: context.icachePut(this , icd);
139: }
140: }
141: }
142:
143: /**
144: * pass through application level runtime exceptions
145: */
146: catch (RuntimeException e) {
147: throw e;
148: } catch (Exception e) {
149: log.error("ASTIdentifier.execute() : identifier = "
150: + identifier, e);
151: }
152:
153: /*
154: * we have no getter... punt...
155: */
156:
157: if (vg == null) {
158: return null;
159: }
160:
161: /*
162: * now try and execute. If we get a MIE, throw that
163: * as the app wants to get these. If not, log and punt.
164: */
165: try {
166: return vg.invoke(o);
167: } catch (InvocationTargetException ite) {
168: /*
169: * if we have an event cartridge, see if it wants to veto
170: * also, let non-Exception Throwables go...
171: */
172:
173: Throwable t = ite.getTargetException();
174: if (t instanceof Exception) {
175: try {
176: return EventHandlerUtil.methodException(rsvc,
177: context, o.getClass(), vg.getMethodName(),
178: (Exception) t);
179: }
180:
181: /**
182: * If the event handler throws an exception, then wrap it
183: * in a MethodInvocationException. Don't pass through RuntimeExceptions like other
184: * similar catchall code blocks.
185: */
186: catch (Exception e) {
187: throw new MethodInvocationException(
188: "Invocation of method '"
189: + vg.getMethodName()
190: + "'"
191: + " in "
192: + o.getClass()
193: + " threw exception "
194: + ite.getTargetException()
195: .toString(), ite
196: .getTargetException(), vg
197: .getMethodName(), context
198: .getCurrentTemplateName(), this
199: .getLine(), this .getColumn());
200: }
201: } else {
202: /*
203: * no event cartridge to override. Just throw
204: */
205:
206: throw new MethodInvocationException(
207: "Invocation of method '" + vg.getMethodName()
208: + "'" + " in " + o.getClass()
209: + " threw exception "
210: + ite.getTargetException().toString(),
211: ite.getTargetException(), vg.getMethodName(),
212: context.getCurrentTemplateName(), this
213: .getLine(), this .getColumn());
214:
215: }
216: } catch (IllegalArgumentException iae) {
217: return null;
218: }
219: /**
220: * pass through application level runtime exceptions
221: */
222: catch (RuntimeException e) {
223: throw e;
224: } catch (Exception e) {
225: log.error("ASTIdentifier() : exception invoking method "
226: + "for identifier '" + identifier + "' in "
227: + o.getClass() + " : " + e);
228: }
229:
230: return null;
231: }
232: }
|