org.paxml.bean.BeanTag.java Source code

Java tutorial

Introduction

Here is the source code for org.paxml.bean.BeanTag.java

Source

/**
 * This file is part of PaxmlCore.
 *
 * PaxmlCore is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PaxmlCore is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with PaxmlCore.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.paxml.bean;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.namespace.QName;

import org.paxml.core.Context;
import org.paxml.core.Namespaces;
import org.paxml.core.PaxmlRuntimeException;
import org.paxml.tag.IdExpression;
import org.paxml.tag.invoker.AbstractInvokerTag;
import org.paxml.util.ReflectUtils;
import org.paxml.util.ReflectUtils.PropertyDescriptorType;

/**
 * Base tag impl for a bean tag whose properties are settable directly from xml.
 * For example, if an invoker tag has attribute "abc" or sub element "abc", the
 * property "abc" of such a object will be set to the given value.
 * 
 * @author Xuetao Niu
 * 
 */
public abstract class BeanTag extends AbstractInvokerTag {
    /**
     * Property value visitor.
     * 
     * @author Xuetao Niu
     * 
     */
    private interface IPropertyValueReader {
        Object getValue(String name);
    }

    private final List<PropertyDescriptor> settableProperties = findSettableProperties(getClass());

    private Object value;

    static List<PropertyDescriptor> findSettableProperties(Class<? extends BeanTag> clazz) {
        List<PropertyDescriptor> list = new ArrayList<PropertyDescriptor>(0);
        for (PropertyDescriptor pd : ReflectUtils.getPropertyDescriptors(clazz, PropertyDescriptorType.SETTER)) {

            Class<?> ownerClass = pd.getWriteMethod().getDeclaringClass();
            if (BeanTag.class.equals(ownerClass) || ReflectUtils.isSubClass(ownerClass, BeanTag.class, true)) {
                list.add(pd);
            }
        }
        return list;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

    /**
     * Actually do the invocation.
     * 
     * @param context
     *            the execution context
     * @return the invocation result
     * @throws Exception
     *             any exception
     */
    protected abstract Object doInvoke(Context context) throws Exception;

    /**
     * {@inheritDoc}
     */
    @Override
    protected final Object invoke(final Context context) throws Exception {
        // cache the initial properties
        // final Map<String, Object> initialProperties = new HashMap<String,
        // Object>(settableProperties.size());
        // for (PropertyDescriptor pd : settableProperties) {
        // initialProperties.put(pd.getName(), getPropertyValue(pd.getName()));
        // }
        if (strictOnPropertyNames(context)) {
            Set<String> props = new HashSet<String>(settableProperties.size());
            for (PropertyDescriptor pd : settableProperties) {
                props.add(pd.getName());
            }
            assertNoExcessiveParameters(props, context);
        }
        preValueInjection();

        // put the properties from context
        populateProperties(false, new IPropertyValueReader() {

            public Object getValue(String name) {
                Object obj = context.getConst(name, true);
                return obj;
            }

        });

        afterPropertiesInjection(context);

        Object result = doInvoke(context);

        // restore the properties
        // populateProperties(true, new IPropertyValueReader() {
        //
        // public Object getValue(String name) {
        // return initialProperties.get(name);
        // }
        // });

        return result;
    }

    /**
     * Flag if the given params must match the property names.
     * 
     * @param context
     *            the context
     * @return true means must match, false means doesn't have to match.
     */
    protected boolean strictOnPropertyNames(Context context) {
        return true;
    }

    private void assertNoExcessiveParameters(Set<String> settablePropNames, Context context) {

        Set<String> ids = new HashSet<String>(context.getConstIds());
        ids.removeAll(settablePropNames);
        IdExpression idExp = getIdExpression();
        if (idExp != null) {
            // only remove the id if given with default namespace
            QName qn = idExp.getAttribute();
            // if (!Namespaces.ROOT.equals(qn.getNamespaceURI())) {
            ids.remove(qn.getLocalPart());
            // }
        }
        if (ids.size() > 0) {
            throw new PaxmlRuntimeException("Unsupported parameter(s) passed: " + ids
                    + ", the acceptable parameters are: " + settablePropNames);
        }
    }

    private void populateProperties(boolean force, IPropertyValueReader reader) {
        for (PropertyDescriptor pd : settableProperties) {

            Method setter = pd.getWriteMethod();
            Class<?> argType = setter.getParameterTypes()[0];

            final Object pvalue = reader.getValue(pd.getName());
            boolean doIt = true;
            Object targetValue = null;
            if (pvalue == null) {
                if (argType.isPrimitive()) {
                    doIt = false;
                } else {
                    doIt = force;
                }
            } else {
                targetValue = pvalue;
            }
            if (doIt) {
                ReflectUtils.callSetter(this, pd, targetValue);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Map<String, Object> inspectAttributes() {
        Map<String, Object> map = super.inspectAttributes();
        if (map == null) {
            map = new LinkedHashMap<String, Object>();
        }
        for (PropertyDescriptor pd : settableProperties) {
            map.put(pd.getName(), getPropertyValue(pd.getName()));
        }
        return map;
    }

    private Object getPropertyValue(String pname) {
        try {
            return org.apache.commons.beanutils.BeanUtils.getProperty(this, pname);
        } catch (NoSuchMethodException e) {
            throw new PaxmlRuntimeException(
                    "Propery '" + pname + "' has no getter in class: " + getClass().getName(), e);
        } catch (Exception e) {
            throw new PaxmlRuntimeException(
                    "Cannot get value for property '" + pname + "' from class: " + getClass().getName(), e);
        }
    }

    /**
     * Handler called before all value injections occur.
     */
    protected void preValueInjection() {
        // do nothing here
    }

    /**
     * Handler called after all value injections occur.
     * 
     * @param Context
     *            the context
     */
    protected void afterPropertiesInjection(Context context) {
        // do nothing here, let subclasses do stuff
    }
}