You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by nb...@apache.org on 2007/08/21 05:05:31 UTC
svn commit: r567921 -
/velocity/tools/branches/2.x/src/main/java/org/apache/velocity/tools/generic/FieldTool.java
Author: nbubna
Date: Mon Aug 20 20:05:31 2007
New Revision: 567921
URL: http://svn.apache.org/viewvc?rev=567921&view=rev
Log:
add new tool for public static field access (taken in part from Engine's FieldMethodizer, but made into a proper Tool)
Added:
velocity/tools/branches/2.x/src/main/java/org/apache/velocity/tools/generic/FieldTool.java (with props)
Added: velocity/tools/branches/2.x/src/main/java/org/apache/velocity/tools/generic/FieldTool.java
URL: http://svn.apache.org/viewvc/velocity/tools/branches/2.x/src/main/java/org/apache/velocity/tools/generic/FieldTool.java?rev=567921&view=auto
==============================================================================
--- velocity/tools/branches/2.x/src/main/java/org/apache/velocity/tools/generic/FieldTool.java (added)
+++ velocity/tools/branches/2.x/src/main/java/org/apache/velocity/tools/generic/FieldTool.java Mon Aug 20 20:05:31 2007
@@ -0,0 +1,324 @@
+package org.apache.velocity.tools.generic;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.velocity.runtime.log.Log;
+import org.apache.velocity.tools.ClassUtils;
+import org.apache.velocity.tools.config.DefaultKey;
+
+/**
+ * <p>
+ * This is a simple tools class to allow easy access to static fields in a class,
+ * such as string constants from within a template. Velocity will not introspect
+ * for class fields (and won't in the future :), but writing setter/getter methods
+ * to do this is a pain, so use this if you really have to access fields.
+ *
+ * <p>
+ * <pre>
+ * Example uses in a template:
+ * ## here we access a constant in a class include in the configuration
+ * $field.COUNTER_NAME
+ *
+ * ## here we dynamically lookup a class' fields to find another constant
+ * $field.in("org.com.SomeClass").ANOTHER_CONSTANT
+ *
+ * ## here we pass an object instance in (an Integer in this case) and
+ * ## retrieve a static constant from that instance's class
+ * $field.in(0).MIN_VALUE
+ *
+ * ## by default, once we've searched a class' fields, those fields stay
+ * ## available in the tool (change this by storeDynamicLookups="false")
+ * ## so here we get another constant from the Integer class
+ * $field.MAX_VALUE
+ *
+ *
+ * Example tools.xml config:
+ * <tools>
+ * <toolbox scope="application">
+ * <tool class="org.apache.velocity.tools.generic.FieldTool"
+ * include="org.apache.velocity.runtime.RuntimeConstants,com.org.MyConstants"/>
+ * </toolbox>
+ * </tools>
+ * </pre></p>
+ *
+ * <p>
+ * Right now, this tool only gives access to <code>public static</code> fields.
+ * It seems that anything else is too dangerous. This is for convenient access
+ * to 'constants'. If you have fields that aren't <code>static</code>,
+ * handle them by explicitly placing them into the context or writing a getter
+ * method.
+ *
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @author Nathan Bubna
+ * @since VelocityTools 2.0
+ * @version $Id: FieldTool.java 463298 2006-10-12 16:10:32Z henning $
+ */
+@DefaultKey("field")
+public class FieldTool extends AbstractLockConfig
+{
+ /**
+ * The key used for specifying which classes should be inspected
+ * for public static methods to be made available.
+ */
+ public static final String INCLUDE_KEY = "include";
+
+ /**
+ * The key used for specifying whether or not the tool should store
+ * fields in classes dynamically looked up from within a template.
+ * The default value is true.
+ */
+ public static final String STORE_DYNAMIC_KEY = "storeDynamicLookups";
+
+ protected Log log;
+ protected HashMap storage = new HashMap();
+ protected boolean storeDynamicLookups = true;
+
+ protected void configure(ValueParser values)
+ {
+ // see if there's a log in there
+ this.log = (Log)values.getValue("log");
+
+ // retrieve any classnames to be inspected and inspect them
+ // *before* setting the storeDynamicLookups property!
+ String[] classnames = values.getStrings(INCLUDE_KEY);
+ if (classnames != null)
+ {
+ for (String classname : classnames)
+ {
+ // make sure we get results for each classname
+ // since these come from the configuration, it's
+ // an error if they're invalid
+ if (in(classname) == null)
+ {
+ // shame that ClassNotFoundException is checked...
+ throw new RuntimeException("Could not find "+classname+" in the classpath");
+ }
+ }
+ }
+
+ // find out whether or not we should store dynamic lookups
+ this.storeDynamicLookups =
+ values.getBoolean(STORE_DYNAMIC_KEY, this.storeDynamicLookups);
+ }
+
+
+ /**
+ * Returns the value for the specified field name as found
+ * in the stored {@link Map} of field names to values (or placeholders).
+ * Returns {@code null} if there is no matching field.
+ */
+ public Object get(String name)
+ {
+ Object o = storage.get(name);
+ // if it was not a final field, get the current value
+ if (o instanceof MutableField)
+ {
+ return ((MutableField)o).getValue();
+ }
+ // otherwise, we should have stored the value directly
+ return o;
+ }
+
+ /**
+ * Returns a {@link FieldToolSub} holding a {@link Map}
+ * of all the public static field names to values (or a placeholder
+ * if the value is not final) for the specified class(name). If the
+ * {@link Class} with the specified name cannot be loaded, this will
+ * return {@code null}, rather than throw an exception.
+ *
+ * @see #in(Class clazz)
+ */
+ public FieldToolSub in(String classname)
+ {
+ try
+ {
+ return in(ClassUtils.getClass(classname));
+ }
+ catch (ClassNotFoundException cnfe)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Returns a {@link FieldToolSub} holding a {@link Map}
+ * of all the public static field names to values (or a placeholder
+ * if the value is not final) for the {@link Class} of the
+ * specified Object.
+ * @see #in(Class clazz)
+ */
+ public FieldToolSub in(Object instance)
+ {
+ if (instance == null)
+ {
+ return null;
+ }
+ return in(instance.getClass());
+ }
+
+ /**
+ * Returns a {@link FieldToolSub} holding a {@link Map}
+ * of all the public static field names to values (or a placeholder
+ * if the value is not final) for the specified {@link Class}.
+ */
+ public FieldToolSub in(Class clazz)
+ {
+ if (clazz == null)
+ {
+ return null;
+ }
+
+ Map<String,Object> results = inspect(clazz);
+ if (storeDynamicLookups && !results.isEmpty())
+ {
+ storage.putAll(results);
+ }
+ return new FieldToolSub(results);
+ }
+
+
+ /**
+ * Looks for all public, static fields in the specified class and
+ * stores their value (if final) or else a {@link MutableField} for
+ * in a {@link Map} under the fields' names. This will never return
+ * null, only an empty Map if there are no public static fields.
+ */
+ protected Map<String,Object> inspect(Class clazz)
+ {
+ Map<String,Object> results = new HashMap<String,Object>();
+ for(Field field : clazz.getFields())
+ {
+ // ignore anything non-public or non-static
+ int mod = field.getModifiers();
+ if (Modifier.isStatic(mod) && Modifier.isPublic(mod))
+ {
+ // if the field is final
+ if (Modifier.isFinal(mod))
+ {
+ // just get the value now
+ results.put(field.getName(), retrieve(field, clazz, log));
+ }
+ else
+ {
+ // put a wrapper with easy access
+ results.put(field.getName(),
+ new MutableField(field, clazz, log));
+ }
+ }
+ }
+ return results;
+ }
+
+ /**
+ * Retrieves and returns the value of the specified {@link Field}
+ * in the specified {@link Class}. If {@link Log} is provided, then
+ * access errors will be logged, otherwise this will fail silently
+ * and return {@code null}.
+ */
+ protected static Object retrieve(Field field, Class clazz, Log log)
+ {
+ try
+ {
+ return field.get(clazz);
+ }
+ catch(IllegalAccessException iae)
+ {
+ if (log != null)
+ {
+ log.warn("IllegalAccessException while trying to access " + field.getName(), iae);
+ }
+ return null;
+ }
+ }
+
+
+
+ /**
+ * Holds a {@link Map} of results for a particular class.
+ * This exists simply to enable the $field.in("class.Name").FOO
+ * syntax, even when storeDynamicLookups is set to false.
+ * NOTE: we can't simply return the results Map when the in()
+ * methods are called, because the Map contains placeholders
+ * for any mutable fields found. We want to put off reading non-final
+ * field values to the last moment, in case their values change.
+ */
+ public static class FieldToolSub
+ {
+ private Map<String,Object> results;
+
+ public FieldToolSub(Map<String,Object> results)
+ {
+ if (results == null)
+ {
+ throw new NullPointerException("Cannot create sub with null field results map");
+ }
+ this.results = results;
+ }
+
+ public Object get(String name)
+ {
+ Object o = results.get(name);
+ // if it was not a final field, get the current value
+ if (o instanceof MutableField)
+ {
+ return ((MutableField)o).getValue();
+ }
+ // otherwise, we should have stored the value directly
+ return o;
+ }
+ }
+
+
+
+ /**
+ * Holds a {@link Field} and {@link Class} reference for later
+ * retrieval of the value of a field that is not final and may
+ * change at different lookups. If a {@link Log} is passed in,
+ * then this will log errors, otherwise it will fail silently.
+ */
+ public static class MutableField
+ {
+ private Class clazz;
+ private Field field;
+ private Log log;
+
+ public MutableField(Field f, Class c, Log l)
+ {
+ if (f == null || c == null)
+ {
+ throw new NullPointerException("Both Class and Field must NOT be null");
+ }
+
+ field = f;
+ clazz = c;
+ log = l;
+ }
+
+ public Object getValue()
+ {
+ return FieldTool.retrieve(field, clazz, log);
+ }
+ }
+
+}
Propchange: velocity/tools/branches/2.x/src/main/java/org/apache/velocity/tools/generic/FieldTool.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: velocity/tools/branches/2.x/src/main/java/org/apache/velocity/tools/generic/FieldTool.java
------------------------------------------------------------------------------
svn:keywords = Revision
Propchange: velocity/tools/branches/2.x/src/main/java/org/apache/velocity/tools/generic/FieldTool.java
------------------------------------------------------------------------------
svn:mime-type = text/plain