com.google.code.pathlet.web.ognl.impl.InstantiatingNullHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.google.code.pathlet.web.ognl.impl.InstantiatingNullHandler.java

Source

/*
 * Copyright (c) 2002-2007 by OpenSymphony
 * All rights reserved.
 */
/*
 * Copyright 2010-2011 the original author or authors.
 *
 * 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 com.google.code.pathlet.web.ognl.impl;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ognl.NullHandler;
import ognl.Ognl;
import ognl.TypeConverter;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.code.pathlet.web.ognl.ConvertException;

/**
 * <!-- START SNIPPET: javadoc -->
 *
 * OGNL expressions
 * that have caused a NullPointerException will be temporarily stopped for evaluation while the system automatically
 * tries to solve the null references by automatically creating the object.
 *
 * <p/> The following rules are used when handling null references:
 *
 * <ul>
 *
 * <li>If the property is declared <i>exactly</i> as a {@link Collection} or {@link List}, then an ArrayList shall be
 * returned and assigned to the null references.</li>
 *
 * <li>If the property is declared as a {@link Map}, then a HashMap will be returned and assigned to the null
 * references.</li>
 *
 * <li>If the null property is a simple bean with a no-arg constructor, it will simply be created using the {@link
 * Object#newInstance()} method.</li>
 *
 * </ul>
 *
 * <!-- END SNIPPET: javadoc -->
 *
 * <!-- START SNIPPET: example -->
 *
 * For example, if a form element has a text field named <b>person.name</b> and the expression <i>person</i> evaluates
 * to null, then this class will be invoked. Because the <i>person</i> expression evaluates to a <i>Person</i> class, a
 * new Person is created and assigned to the null reference. Finally, the name is set on that object and the overall
 * effect is that the system automatically created a Person object for you, set it by calling setUsers() and then
 * finally called getUsers().setName() as you would typically expect.
 *
 * <!-- END SNIPPET: example>
 *
 * @author Charlie Zhang
 */
public class InstantiatingNullHandler implements NullHandler {

    private static final Log LOG = LogFactory.getLog(InstantiatingNullHandler.class);

    private ReflectionProvider reflectionProvider = new ReflectionProvider();
    //private ReflectionProvider reflectionProvider;
    //private ObjectFactory objectFactory;
    //private ObjectTypeDeterminer objectTypeDeterminer = new DefaultObjectTypeDeterminer();

    //@Inject
    //public void setObjectTypeDeterminer(ObjectTypeDeterminer det) {
    //    this.objectTypeDeterminer = det;
    //}

    public Object nullMethodResult(Map context, Object target, String methodName, Object[] args) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Entering nullMethodResult ");
        }
        return null;
    }

    public Object nullPropertyValue(Map context, Object target, Object property) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Entering nullPropertyValue [target=" + target + ", property=" + property + "]");
        }

        if ((target == null) || (property == null)) {
            return null;
        }

        try {
            String propName = property.toString();

            Class clazz = null;

            if (target != null) {
                PropertyDescriptor[] descs = reflectionProvider.getPropertyDescriptors(target);

                for (PropertyDescriptor desc : descs) {
                    if (propName.equals(desc.getName())) {
                        clazz = desc.getPropertyType();
                    }
                }
            }

            if (clazz == null) {
                // can't do much here!
                return null;
            }

            Object param = createObject(clazz, target, propName, context);
            Ognl.setValue(propName, context, target, param);

            return param;
        } catch (Exception e) {
            LOG.error("Could not create and/or set value back on to object", e);
        }

        return null;
    }

    private Object createObject(Class clazz, Object target, String property, Map<String, Object> context)
            throws Exception {
        if (Collection.class.isAssignableFrom(clazz)) {
            TypeConverter typeConvert = Ognl.getTypeConverter(context);
            Class elementClazz = getClass(target.getClass(), property, true);
            if (elementClazz == null) {
                elementClazz = String.class;
            }
            return new IncreasableList(context, typeConvert, elementClazz);
        } else if (clazz == Map.class) {
            return new HashMap();
        }

        return clazz.newInstance();
    }

    /**
     * Returns the class for the given field via generic type check.
     *
     * @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
     * @param property    the property of the Map or Collection for the given parent class
     * @param element     <tt>true</tt> for indexed types and Maps.
     * @return Class of the specified field.
     */
    private Class getClass(Class parentClass, String property, boolean element) {

        try {

            Field field = reflectionProvider.getField(parentClass, property);

            Type genericType = null;

            // Check fields first
            if (field != null) {
                genericType = field.getGenericType();
            }

            // Try to get ParameterType from setter method
            if (genericType == null || !(genericType instanceof ParameterizedType)) {
                try {
                    Method setter = reflectionProvider.getSetMethod(parentClass, property);
                    genericType = setter.getGenericParameterTypes()[0];
                } catch (ReflectionException ognle) {
                    ; // ignore
                } catch (IntrospectionException ie) {
                    ; // ignore
                }
            }

            // Try to get ReturnType from getter method
            if (genericType == null || !(genericType instanceof ParameterizedType)) {
                try {
                    Method getter = reflectionProvider.getGetMethod(parentClass, property);
                    genericType = getter.getGenericReturnType();
                } catch (ReflectionException ognle) {
                    ; // ignore
                } catch (IntrospectionException ie) {
                    ; // ignore
                }
            }

            if (genericType instanceof ParameterizedType) {

                ParameterizedType type = (ParameterizedType) genericType;

                int index = (element && type.getRawType().toString().contains(Map.class.getName())) ? 1 : 0;

                Type resultType = type.getActualTypeArguments()[index];

                if (resultType instanceof ParameterizedType) {
                    return (Class) ((ParameterizedType) resultType).getRawType();
                }
                return (Class) resultType;

            }
        } catch (Exception e) {
            throw new ConvertException(e);
        }
        return null;
    }

    /*
    public Object nullMethodResult(Map<String, Object> context, Object target, String methodName, Object[] args) {
    if (LOG.isDebugEnabled()) {
        LOG.debug("Entering nullMethodResult ");
    }
        
    return null;
    }
        
    public Object nullPropertyValue(Map<String, Object> context, Object target, Object property) {
    if (LOG.isDebugEnabled()) {
        LOG.debug("Entering nullPropertyValue [target="+target+", property="+property+"]");
    }
        
    boolean c = ReflectionContextState.isCreatingNullObjects(context);
        
    if (!c) {
        return null;
    }
        
    if ((target == null) || (property == null)) {
        return null;
    }
        
    try {
        String propName = property.toString();
        Object realTarget = reflectionProvider.getRealTarget(propName, context, target);
        Class clazz = null;
        
        if (realTarget != null) {
            PropertyDescriptor pd = reflectionProvider.getPropertyDescriptor(realTarget.getClass(), propName);
            if (pd == null) {
                return null;
            }
        
            clazz = pd.getPropertyType();
        }
        
        if (clazz == null) {
            // can't do much here!
            return null;
        }
        
        Object param = createObject(clazz, realTarget, propName, context);
        
        reflectionProvider.setValue(propName, context, realTarget, param);
        
        return param;
    } catch (Exception e) {
        LOG.error("Could not create and/or set value back on to object", e);
    }
        
    return null;
    }
        
    private Object createObject(Class clazz, Object target, String property, Map<String, Object> context) throws Exception {
    if (Collection.class.isAssignableFrom(clazz)) {
        return new ArrayList();
    } else if (clazz == Map.class) {
        return new HashMap();
    } else if (clazz == EnumMap.class) {
        Class keyClass = objectTypeDeterminer.getKeyClass(target.getClass(), property);
        return new EnumMap(keyClass);
    }
        
    return objectFactory.buildBean(clazz, context);
    }
    */
}