This Confluence has been LDAP enabled, if you are an ASF Committer, please use your LDAP Credentials to login. Any problems file an INFRA jira ticket please.

Page tree
Skip to end of metadata
Go to start of metadata

JUnit tests can be found here.

/*
 * Copyright 2003-2004 The Apache Software Foundation.
 *
 * Licensed 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.
 */

package org.apache.velocity.tools.generic.introspection;

import java.lang.reflect.Array;
import java.lang.reflect.Field;

import org.apache.velocity.util.introspection.Info;
import org.apache.velocity.util.introspection.UberspectImpl;
import org.apache.velocity.util.introspection.VelPropertyGet;
import org.apache.velocity.util.introspection.VelPropertySet;

/**
 * Uberspect implementation that exposes public fields.
 * Also exposes the explicit "length" field of arrays.
 *
 * <p>To use, tell Velocity to use this class for introspection
 * by adding the following to your velocity.properties:<br />
 *
 * <code>
 * runtime.introspector.uberspect = org.apache.velocity.tools.generic.introspection.PublicFieldUberspect
 * </code>
 * </p>
 *
 * @author <a href="mailto:shinobu@ieee.org">Shinobu Kawai</a>
 * @version $Id: $
 */
public class PublicFieldUberspect extends UberspectImpl
{

    /**
     * Default constructor.
     */
    public PublicFieldUberspect()
    {
    }

    /**
     * Property getter - returns VelPropertyGet appropos for #set($foo = $bar.woogie).
     * <br />
     * Returns a special {@link VelPropertyGet} for the <code>length</code> property of arrays.
     * Otherwise tries the regular routine.  If a getter was not found,
     * returns a {@link VelPropertyGet} that gets from public fields.
     *
     * @param obj the object
     * @param identifier the name of the property
     * @param i a bunch of information.
     * @return a valid <code>VelPropertyGet</code>, if it was found.
     * @throws Exception failed to create a valid <code>VelPropertyGet</code>.
     */
    public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i)
            throws Exception
    {
        Class clazz = obj.getClass();
        boolean isArray = clazz.isArray();
        boolean isLength = identifier.equals("length");
        if (isArray && isLength)
        {
            return new ArrayLengthGetter();
        }

        VelPropertyGet getter = super.getPropertyGet(obj, identifier, i);
        // there is no clean way to see if super succeeded
        // @see http://issues.apache.org/bugzilla/show_bug.cgi?id=31742
        try
        {
            getter.getMethodName();
            return getter;
        }
        catch (NullPointerException notFound)
        {
        }

        Field field = obj.getClass().getField(identifier);
        if (field != null)
        {
            return new PublicFieldGetter(field);
        }

        return null;
    }

    /**
     * Property setter - returns VelPropertySet appropos for #set($foo.bar = "geir").
     * <br />
     * First tries the regular routine.  If a setter was not found,
     * returns a {@link VelPropertySet} that sets to public fields.
     *
     * @param obj the object
     * @param identifier the name of the property
     * @param arg the value to set to the property
     * @param i a bunch of information.
     * @return a valid <code>VelPropertySet</code>, if it was found.
     * @throws Exception failed to create a valid <code>VelPropertySet</code>.
     */
    public VelPropertySet getPropertySet(Object obj, String identifier,
            Object arg, Info i) throws Exception
    {
        VelPropertySet setter = super.getPropertySet(obj, identifier, arg, i);
        if (setter != null)
        {
            return setter;
        }

        Field field = obj.getClass().getField(identifier);
        if (field != null)
        {
            return new PublicFieldSetter(field);
        }

        return null;
    }

    /**
     * Implementation of {@link VelPropertyGet} that gets from public fields.
     *
     * @author <a href="mailto:shinobu@ieee.org">Shinobu Kawai</a>
     * @version $Id: $
     */
    protected class PublicFieldGetter implements VelPropertyGet
    {
        /** The <code>Field</code> object representing the property. */
        private Field field = null;

        /**
         * Constructor.
         *
         * @param field The <code>Field</code> object representing the property.
         */
        public PublicFieldGetter(Field field)
        {
            this.field = field;
        }

        /**
         * Returns the value of the public field.
         *
         * @param o the object
         * @return the value
         * @throws Exception failed to get the value from the object
         */
        public Object invoke(Object o) throws Exception
        {
            return this.field.get(o);
        }

        /**
         * This class is cacheable, so it returns <code>true</code>.
         *
         * @return <code>true</code>.
         */
        public boolean isCacheable()
        {
            return true;
        }

        /**
         * Returns <code>"public field getter"</code>, since there is no method.
         *
         * @return <code>"public field getter"</code>
         */
        public String getMethodName()
        {
            return "public field getter";
        }
    }

    /**
     * Implementation of {@link VelPropertyGet} that gets length from arrays.
     *
     * @author <a href="mailto:shinobu@ieee.org">Shinobu Kawai</a>
     * @version $Id: $
     */
    protected class ArrayLengthGetter implements VelPropertyGet
    {
        /**
         * Constructor.
         */
        public ArrayLengthGetter()
        {
        }

        /**
         * Returns the length of the array.
         *
         * @param o the array
         * @return the length
         * @throws Exception failed to get the length from the array
         */
        public Object invoke(Object o) throws Exception
        {
            // Thanks to Eric Fixler for this refactor.
            return new Integer(Array.getLength(o));
        }

        /**
         * This class is cacheable, so it returns <code>true</code>.
         *
         * @return <code>true</code>.
         */
        public boolean isCacheable()
        {
            return true;
        }

        /**
         * Returns <code>"array length getter"</code>, since there is no method.
         *
         * @return <code>"array length getter"</code>
         */
        public String getMethodName()
        {
            return "array length getter";
        }
    }

    /**
     * Implementation of {@link VelPropertySet} that sets to public fields.
     *
     * @author <a href="mailto:shinobu@ieee.org">Shinobu Kawai</a>
     * @version $Id: $
     */
    protected class PublicFieldSetter implements VelPropertySet
    {
        /** The <code>Field</code> object representing the property. */
        private Field field = null;

        /**
         * Constructor.
         *
         * @param field The <code>Field</code> object representing the property.
         */
        public PublicFieldSetter(Field field)
        {
            this.field = field;
        }

        /**
         * Sets the value to the public field.
         *
         * @param o the object
         * @param value the value to set
         * @return always <code>null</code>
         * @throws Exception failed to set the value to the object
         */
        public Object invoke(Object o, Object value) throws Exception
        {
            this.field.set(o, value);
            return null;
        }

        /**
         * This class is cacheable, so it returns <code>true</code>.
         *
         * @return <code>true</code>.
         */
        public boolean isCacheable()
        {
            return true;
        }

        /**
         * Returns <code>"public field setter"</code>, since there is no method.
         *
         * @return <code>"public field setter"</code>
         */
        public String getMethodName()
        {
            return "public field setter";
        }
    }

}
  • No labels