View Javadoc

1   /*
2    * Copyright 2003-2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.velocity.tools.generic.introspection;
18  
19  import java.lang.reflect.Array;
20  import java.lang.reflect.Field;
21  
22  import org.apache.velocity.util.introspection.Info;
23  import org.apache.velocity.util.introspection.UberspectImpl;
24  import org.apache.velocity.util.introspection.VelPropertyGet;
25  import org.apache.velocity.util.introspection.VelPropertySet;
26  
27  /***
28   * Uberspect implementation that exposes public fields.
29   * Also exposes the explicit "length" field of arrays.
30   *
31   * <p>To use, tell Velocity to use this class for introspection
32   * by adding the following to your velocity.properties:<br />
33   *
34   * <code>
35   * runtime.introspector.uberspect = org.apache.velocity.tools.generic.introspection.PublicFieldUberspect
36   * </code>
37   * </p>
38   *
39   * @author <a href="mailto:shinobu@ieee.org">Shinobu Kawai</a>
40   * @version $Id: $
41   */
42  public class PublicFieldUberspect extends UberspectImpl
43  {
44  
45      /***
46       * Default constructor.
47       */
48      public PublicFieldUberspect()
49      {
50      }
51  
52      /***
53       * Property getter - returns VelPropertyGet appropos for #set($foo = $bar.woogie).
54       * <br />
55       * Returns a special {@link VelPropertyGet} for the <code>length</code> property of arrays.
56       * Otherwise tries the regular routine.  If a getter was not found,
57       * returns a {@link VelPropertyGet} that gets from public fields.
58       *
59       * @param obj the object
60       * @param identifier the name of the property
61       * @param i a bunch of information.
62       * @return a valid <code>VelPropertyGet</code>, if it was found.
63       * @throws Exception failed to create a valid <code>VelPropertyGet</code>.
64       */
65      public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i)
66              throws Exception
67      {
68          Class clazz = obj.getClass();
69          boolean isArray = clazz.isArray();
70          boolean isLength = identifier.equals("length");
71          if (isArray && isLength)
72          {
73              return new ArrayLengthGetter();
74          }
75  
76          VelPropertyGet getter = super.getPropertyGet(obj, identifier, i);
77          // there is no clean way to see if super succeeded
78          // @see http://issues.apache.org/bugzilla/show_bug.cgi?id=31742
79          try
80          {
81              getter.getMethodName();
82              return getter;
83          }
84          catch (NullPointerException notFound)
85          {
86          }
87  
88          Field field = obj.getClass().getField(identifier);
89          if (field != null)
90          {
91              return new PublicFieldGetter(field);
92          }
93  
94          return null;
95      }
96  
97      /***
98       * Property setter - returns VelPropertySet appropos for #set($foo.bar = "geir").
99       * <br />
100      * First tries the regular routine.  If a setter was not found,
101      * returns a {@link VelPropertySet} that sets to public fields.
102      *
103      * @param obj the object
104      * @param identifier the name of the property
105      * @param arg the value to set to the property
106      * @param i a bunch of information.
107      * @return a valid <code>VelPropertySet</code>, if it was found.
108      * @throws Exception failed to create a valid <code>VelPropertySet</code>.
109      */
110     public VelPropertySet getPropertySet(Object obj, String identifier,
111             Object arg, Info i) throws Exception
112     {
113         VelPropertySet setter = super.getPropertySet(obj, identifier, arg, i);
114         if (setter != null)
115         {
116             return setter;
117         }
118 
119         Field field = obj.getClass().getField(identifier);
120         if (field != null)
121         {
122             return new PublicFieldSetter(field);
123         }
124 
125         return null;
126     }
127 
128     /***
129      * Implementation of {@link VelPropertyGet} that gets from public fields.
130      *
131      * @author <a href="mailto:shinobu@ieee.org">Shinobu Kawai</a>
132      * @version $Id: $
133      */
134     protected class PublicFieldGetter implements VelPropertyGet
135     {
136         /*** The <code>Field</code> object representing the property. */
137         private Field field = null;
138 
139         /***
140          * Constructor.
141          *
142          * @param field The <code>Field</code> object representing the property.
143          */
144         public PublicFieldGetter(Field field)
145         {
146             this.field = field;
147         }
148 
149         /***
150          * Returns the value of the public field.
151          *
152          * @param o the object
153          * @return the value
154          * @throws Exception failed to get the value from the object
155          */
156         public Object invoke(Object o) throws Exception
157         {
158             return this.field.get(o);
159         }
160 
161         /***
162          * This class is cacheable, so it returns <code>true</code>.
163          *
164          * @return <code>true</code>.
165          */
166         public boolean isCacheable()
167         {
168             return true;
169         }
170 
171         /***
172          * Returns <code>"public field getter"</code>, since there is no method.
173          *
174          * @return <code>"public field getter"</code>
175          */
176         public String getMethodName()
177         {
178             return "public field getter";
179         }
180     }
181 
182     /***
183      * Implementation of {@link VelPropertyGet} that gets length from arrays.
184      *
185      * @author <a href="mailto:shinobu@ieee.org">Shinobu Kawai</a>
186      * @version $Id: $
187      */
188     protected class ArrayLengthGetter implements VelPropertyGet
189     {
190         /***
191          * Constructor.
192          */
193         public ArrayLengthGetter()
194         {
195         }
196 
197         /***
198          * Returns the length of the array.
199          *
200          * @param o the array
201          * @return the length
202          * @throws Exception failed to get the length from the array
203          */
204         public Object invoke(Object o) throws Exception
205         {
206             // Thanks to Eric Fixler for this refactor.
207             return new Integer(Array.getLength(o));
208         }
209 
210         /***
211          * This class is cacheable, so it returns <code>true</code>.
212          *
213          * @return <code>true</code>.
214          */
215         public boolean isCacheable()
216         {
217             return true;
218         }
219 
220         /***
221          * Returns <code>"array length getter"</code>, since there is no method.
222          *
223          * @return <code>"array length getter"</code>
224          */
225         public String getMethodName()
226         {
227             return "array length getter";
228         }
229     }
230 
231     /***
232      * Implementation of {@link VelPropertySet} that sets to public fields.
233      *
234      * @author <a href="mailto:shinobu@ieee.org">Shinobu Kawai</a>
235      * @version $Id: $
236      */
237     protected class PublicFieldSetter implements VelPropertySet
238     {
239         /*** The <code>Field</code> object representing the property. */
240         private Field field = null;
241 
242         /***
243          * Constructor.
244          *
245          * @param field The <code>Field</code> object representing the property.
246          */
247         public PublicFieldSetter(Field field)
248         {
249             this.field = field;
250         }
251 
252         /***
253          * Sets the value to the public field.
254          *
255          * @param o the object
256          * @param value the value to set
257          * @return always <code>null</code>
258          * @throws Exception failed to set the value to the object
259          */
260         public Object invoke(Object o, Object value) throws Exception
261         {
262             this.field.set(o, value);
263             return null;
264         }
265 
266         /***
267          * This class is cacheable, so it returns <code>true</code>.
268          *
269          * @return <code>true</code>.
270          */
271         public boolean isCacheable()
272         {
273             return true;
274         }
275 
276         /***
277          * Returns <code>"public field setter"</code>, since there is no method.
278          *
279          * @return <code>"public field setter"</code>
280          */
281         public String getMethodName()
282         {
283             return "public field setter";
284         }
285     }
286 
287 }