001: /*
002: * Copyright (c) 2002-2006 by OpenSymphony
003: * All rights reserved.
004: */
005: package com.opensymphony.xwork.util;
006:
007: import com.opensymphony.xwork.ObjectFactory;
008: import ognl.NullHandler;
009: import ognl.Ognl;
010: import ognl.OgnlRuntime;
011: import org.apache.commons.logging.Log;
012: import org.apache.commons.logging.LogFactory;
013:
014: import java.beans.PropertyDescriptor;
015: import java.util.*;
016:
017: /**
018: * <!-- START SNIPPET: javadoc -->
019: *
020: * Provided that the key {@link #CREATE_NULL_OBJECTS} is in the action context with a value of true (this key is set
021: * only during the execution of the {@link com.opensymphony.xwork.interceptor.ParametersInterceptor}), OGNL expressions
022: * that have caused a NullPointerException will be temporarily stopped for evaluation while the system automatically
023: * tries to solve the null references by automatically creating the object.
024: *
025: * <p/> The following rules are used when handling null references:
026: *
027: * <ul>
028: *
029: * <li>If the property is declared <i>exactly</i> as a {@link Collection} or {@link List}, then an ArrayList shall be
030: * returned and assigned to the null references.</li>
031: *
032: * <li>If the property is declared as a {@link Map}, then a HashMap will be returned and assigned to the null
033: * references.</li>
034: *
035: * <li>If the null property is a simple bean with a no-arg constructor, it will simply be created using the {@link
036: * ObjectFactory#buildBean(java.lang.Class, java.util.Map)} method.</li>
037: *
038: * </ul>
039: *
040: * <!-- END SNIPPET: javadoc -->
041: *
042: * <!-- START SNIPPET: example -->
043: *
044: * For example, if a form element has a text field named <b>person.name</b> and the expression <i>person</i> evaluates
045: * to null, then this class will be invoked. Because the <i>person</i> expression evaluates to a <i>Person</i> class, a
046: * new Person is created and assigned to the null reference. Finally, the name is set on that object and the overall
047: * effect is that the system automatically created a Person object for you, set it by calling setPerson() and then
048: * finally called getPerson().setName() as you would typically expect.
049: *
050: * <!-- END SNIPPET: example>
051: *
052: * @author Matt Ho
053: * @author Patrick Lightbody
054: */
055: public class InstantiatingNullHandler implements NullHandler {
056:
057: public static final String CREATE_NULL_OBJECTS = "xwork.NullHandler.createNullObjects";
058: private static final Log LOG = LogFactory
059: .getLog(InstantiatingNullHandler.class);
060:
061: public Object nullMethodResult(Map context, Object target,
062: String methodName, Object[] args) {
063: if (LOG.isDebugEnabled()) {
064: LOG.debug("Entering nullMethodResult ");
065: }
066:
067: return null;
068: }
069:
070: public Object nullPropertyValue(Map context, Object target,
071: Object property) {
072: if (LOG.isDebugEnabled()) {
073: LOG.debug("Entering nullPropertyValue [target=" + target
074: + ", property=" + property + "]");
075: }
076:
077: boolean c = OgnlContextState.isCreatingNullObjects(context);
078:
079: if (!c) {
080: return null;
081: }
082:
083: if ((target == null) || (property == null)) {
084: return null;
085: }
086:
087: try {
088: String propName = property.toString();
089: Object realTarget = OgnlUtil.getRealTarget(propName,
090: context, target);
091: Class clazz = null;
092:
093: if (realTarget != null) {
094: PropertyDescriptor pd = OgnlRuntime
095: .getPropertyDescriptor(realTarget.getClass(),
096: propName);
097: if (pd == null) {
098: return null;
099: }
100:
101: clazz = pd.getPropertyType();
102: }
103:
104: if (clazz == null) {
105: // can't do much here!
106: return null;
107: }
108:
109: Object param = createObject(clazz, realTarget, propName,
110: context);
111:
112: Ognl.setValue(propName, context, realTarget, param);
113:
114: return param;
115: } catch (Exception e) {
116: LOG
117: .error(
118: "Could not create and/or set value back on to object",
119: e);
120: }
121:
122: return null;
123: }
124:
125: private Object createObject(Class clazz, Object target,
126: String property, Map context) throws Exception {
127: if (Collection.class.isAssignableFrom(clazz)) {
128: return new ArrayList();
129: } else if (clazz == Map.class) {
130: return new HashMap();
131: }
132:
133: return ObjectFactory.getObjectFactory().buildBean(clazz,
134: context);
135: }
136: }
|