struts-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mart...@apache.org
Subject svn commit: r894087 [15/46] - in /struts/xwork/trunk: ./ assembly/ assembly/src/ assembly/src/main/ assembly/src/main/assembly/ assembly/src/main/resources/ core/ core/src/ core/src/main/ core/src/main/java/ core/src/main/java/com/ core/src/main/java/c...
Date Sun, 27 Dec 2009 18:01:09 GMT
Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMapPropertyAccessor.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMapPropertyAccessor.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMapPropertyAccessor.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMapPropertyAccessor.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+
+package com.opensymphony.xwork2.ognl.accessor;
+
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import ognl.MapPropertyAccessor;
+import ognl.OgnlException;
+
+import java.util.Map;
+
+/**
+ * Implementation of PropertyAccessor that sets and gets properties by storing and looking
+ * up values in Maps.
+ *
+ * @author Gabriel Zimmerman
+ */
+public class XWorkMapPropertyAccessor extends MapPropertyAccessor {
+
+    private static final Logger LOG = LoggerFactory.getLogger(XWorkMapPropertyAccessor.class);
+
+    private static final String[] INDEX_ACCESS_PROPS = new String[]
+            {"size", "isEmpty", "keys", "values"};
+    
+    private XWorkConverter xworkConverter;
+    private ObjectFactory objectFactory;
+    private ObjectTypeDeterminer objectTypeDeterminer;
+    
+    @Inject
+    public void setXWorkConverter(XWorkConverter conv) {
+        this.xworkConverter = conv;
+    }
+    
+    @Inject
+    public void setObjectFactory(ObjectFactory fac) {
+        this.objectFactory = fac;
+    }
+    
+    @Inject
+    public void setObjectTypeDeterminer(ObjectTypeDeterminer ot) {
+        this.objectTypeDeterminer = ot;
+    }
+
+    @Override
+    public Object getProperty(Map context, Object target, Object name) throws OgnlException {
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Entering getProperty ("+context+","+target+","+name+")");
+        }
+
+        ReflectionContextState.updateCurrentPropertyPath(context, name);
+        // if this is one of the regular index access
+        // properties then just let the superclass deal with the
+        // get.
+        if (name instanceof String && contains(INDEX_ACCESS_PROPS, (String) name)) {
+            return super.getProperty(context, target, name);
+        }
+
+        Object result = null;
+
+        try{
+            result = super.getProperty(context, target, name);
+        } catch(ClassCastException ex){
+        }
+
+        if (result == null) {
+            //find the key class and convert the name to that class
+            Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+
+            String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+            if (lastClass == null || lastProperty == null) {
+                return super.getProperty(context, target, name);
+            }
+            Class keyClass = objectTypeDeterminer
+                    .getKeyClass(lastClass, lastProperty);
+
+            if (keyClass == null) {
+
+                keyClass = java.lang.String.class;
+            }
+            Object key = getKey(context, name);
+            Map map = (Map) target;
+            result = map.get(key);
+
+            if (result == null &&
+                    context.get(ReflectionContextState.CREATE_NULL_OBJECTS) != null
+                    &&  objectTypeDeterminer.shouldCreateIfNew(lastClass,lastProperty,target,null,false)) {
+                Class valueClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, key);
+
+                try {
+                    result = objectFactory.buildBean(valueClass, context);
+                    map.put(key, result);
+                } catch (Exception exc) {
+
+                }
+
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @param array
+     * @param name
+     */
+    private boolean contains(String[] array, String name) {
+        for (String anArray : array) {
+            if (anArray.equals(name)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
+        if (LOG.isDebugEnabled()) {
+     		LOG.debug("Entering setProperty("+context+","+target+","+name+","+value+")");
+     	}
+        
+        Object key = getKey(context, name);
+        Map map = (Map) target;
+        map.put(key, getValue(context, value));
+     }
+
+     private Object getValue(Map context, Object value) {
+         Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+         String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+         if (lastClass == null || lastProperty == null) {
+             return value;
+         }
+         Class elementClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, null);
+         if (elementClass == null) {
+             return value; // nothing is specified, we assume it will be the value passed in.
+         }
+         return xworkConverter.convertValue(context, value, elementClass);
+}
+
+    private Object getKey(Map context, Object name) {
+        Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+        String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+        if (lastClass == null || lastProperty == null) {
+            // return java.lang.String.class;
+            // commented out the above -- it makes absolutely no sense for when setting basic maps!
+            return name;
+        }
+        Class keyClass = objectTypeDeterminer.getKeyClass(lastClass, lastProperty);
+        if (keyClass == null) {
+            keyClass = java.lang.String.class;
+        }
+
+        return xworkConverter.convertValue(context, name, keyClass);
+
+    }
+}
+

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMapPropertyAccessor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMapPropertyAccessor.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMethodAccessor.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMethodAccessor.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMethodAccessor.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMethodAccessor.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.ognl.accessor;
+
+import java.beans.PropertyDescriptor;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+
+import ognl.MethodFailedException;
+import ognl.ObjectMethodAccessor;
+import ognl.OgnlContext;
+import ognl.OgnlRuntime;
+import ognl.PropertyAccessor;
+
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+
+
+/**
+ * Allows methods to be executed under normal cirumstances, except when {@link ReflectionContextState#DENY_METHOD_EXECUTION}
+ * is in the action context with a value of true.
+ *
+ * @author Patrick Lightbody
+ * @author tmjee
+ */
+public class XWorkMethodAccessor extends ObjectMethodAccessor {
+	
+	private static final Logger LOG = LoggerFactory.getLogger(XWorkMethodAccessor.class);
+
+    /**
+     * @deprecated Use {@link ReflectionContextState#DENY_METHOD_EXECUTION} instead
+     */
+    @Deprecated public static final String DENY_METHOD_EXECUTION = ReflectionContextState.DENY_METHOD_EXECUTION;
+    /**
+     * @deprecated Use {@link ReflectionContextState#DENY_INDEXED_ACCESS_EXECUTION} instead
+     */
+    @Deprecated public static final String DENY_INDEXED_ACCESS_EXECUTION = ReflectionContextState.DENY_INDEXED_ACCESS_EXECUTION;
+
+
+    @Override
+    public Object callMethod(Map context, Object object, String string, Object[] objects) throws MethodFailedException {
+
+        //Collection property accessing
+        //this if statement ensures that ognl
+        //statements of the form someBean.mySet('keyPropVal')
+        //return the set element with value of the keyProp given
+        
+        if (objects.length==1 
+                && context instanceof OgnlContext) {
+            try {
+              OgnlContext ogContext=(OgnlContext)context;
+              if (OgnlRuntime.hasSetProperty(ogContext, object, string))  {
+                  	PropertyDescriptor descriptor=OgnlRuntime.getPropertyDescriptor(object.getClass(), string);
+                  	Class propertyType=descriptor.getPropertyType();
+                  	if ((Collection.class).isAssignableFrom(propertyType)) {
+                  	    //go directly through OgnlRuntime here
+                  	    //so that property strings are not cleared
+                  	    //i.e. OgnlUtil should be used initially, OgnlRuntime
+                  	    //thereafter
+                  	    
+                  	    Object propVal=OgnlRuntime.getProperty(ogContext, object, string);
+                  	    //use the Collection property accessor instead of the individual property accessor, because 
+                  	    //in the case of Lists otherwise the index property could be used
+                  	    PropertyAccessor accessor=OgnlRuntime.getPropertyAccessor(Collection.class);
+                  	    ReflectionContextState.setGettingByKeyProperty(ogContext,true);
+                  	    return accessor.getProperty(ogContext,propVal,objects[0]);
+                  	}
+              }
+            }	catch (Exception oe) {
+                //this exception should theoretically never happen
+                //log it
+            	LOG.error("An unexpected exception occurred", oe);
+            }
+
+        }
+
+        //HACK - we pass indexed method access i.e. setXXX(A,B) pattern
+        if (
+                (objects.length == 2 && string.startsWith("set"))
+                        ||
+                        (objects.length == 1 && string.startsWith("get"))
+                ) {
+            Boolean exec = (Boolean) context.get(ReflectionContextState.DENY_INDEXED_ACCESS_EXECUTION);
+            boolean e = ((exec == null) ? false : exec.booleanValue());
+            if (!e) {
+                return callMethodWithDebugInfo(context, object, string, objects);
+            }
+        }
+        Boolean exec = (Boolean) context.get(ReflectionContextState.DENY_METHOD_EXECUTION);
+        boolean e = ((exec == null) ? false : exec.booleanValue());
+
+        if (!e) {
+            return callMethodWithDebugInfo(context, object, string, objects);
+        } else {
+            return null;
+        }
+    }
+
+	private Object callMethodWithDebugInfo(Map context, Object object, String methodName,
+			Object[] objects) throws MethodFailedException {
+		try {
+			return super.callMethod(context, object, methodName, objects);
+		}
+		catch(MethodFailedException e) {
+			if (LOG.isDebugEnabled()) {
+				if (!(e.getReason() instanceof NoSuchMethodException)) {
+					// the method exists on the target object, but something went wrong
+					String s = "Error calling method through OGNL: object: [#0] method: [#1] args: [#2]";
+					LOG.debug(s, e.getReason(), object.toString(), methodName, Arrays.toString(objects));
+				}
+			}
+			throw e;
+		}
+	}
+
+    @Override
+    public Object callStaticMethod(Map context, Class aClass, String string, Object[] objects) throws MethodFailedException {
+        Boolean exec = (Boolean) context.get(ReflectionContextState.DENY_METHOD_EXECUTION);
+        boolean e = ((exec == null) ? false : exec.booleanValue());
+
+        if (!e) {
+            return callStaticMethodWithDebugInfo(context, aClass, string, objects);
+        } else {
+            return null;
+        }
+    }
+
+	private Object callStaticMethodWithDebugInfo(Map context, Class aClass, String methodName,
+			Object[] objects) throws MethodFailedException {
+		try {
+			return super.callStaticMethod(context, aClass, methodName, objects);
+		}
+		catch(MethodFailedException e) {
+			if (LOG.isDebugEnabled()) {
+				if (!(e.getReason() instanceof NoSuchMethodException)) {
+					// the method exists on the target class, but something went wrong
+					String s = "Error calling method through OGNL, class: [#0] method: [#1] args: [#2]";
+					LOG.debug(s, e.getReason(), aClass.getName(), methodName, Arrays.toString(objects));
+				}
+			}
+			throw e;
+		}
+	}
+}

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMethodAccessor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMethodAccessor.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkObjectPropertyAccessor.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkObjectPropertyAccessor.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkObjectPropertyAccessor.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkObjectPropertyAccessor.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.ognl.accessor;
+
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import ognl.ObjectPropertyAccessor;
+import ognl.OgnlException;
+
+import java.util.Map;
+
+/**
+ * @author Gabe
+ */
+public class XWorkObjectPropertyAccessor extends ObjectPropertyAccessor {
+    @Override
+    public Object getProperty(Map context, Object target, Object oname)
+            throws OgnlException {
+        //set the last set objects in the context
+        //so if the next objects accessed are
+        //Maps or Collections they can use the information
+        //to determine conversion types
+        context.put(XWorkConverter.LAST_BEAN_CLASS_ACCESSED, target.getClass());
+        context.put(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED, oname.toString());
+        ReflectionContextState.updateCurrentPropertyPath(context, oname);
+        return super.getProperty(context, target, oname);
+    }
+}

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkObjectPropertyAccessor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkObjectPropertyAccessor.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/package.html
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/package.html?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/package.html (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/package.html Sun Dec 27 18:00:13 2009
@@ -0,0 +1 @@
+<body>Main XWork interfaces and classes.</body>

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/package.html
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/package.html
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/SpringObjectFactory.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/SpringObjectFactory.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/SpringObjectFactory.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/SpringObjectFactory.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.spring;
+
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.UnsatisfiedDependencyException;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Simple implementation of the ObjectFactory that makes use of Spring's application context if one has been configured,
+ * before falling back on the default mechanism of instantiating a new class using the class name. <p/> In order to use
+ * this class in your application, you will need to instantiate a copy of this class and set it as XWork's ObjectFactory
+ * before the xwork.xml file is parsed. In a servlet environment, this could be done using a ServletContextListener.
+ *
+ * @author Simon Stewart (sms@lateral.net)
+ */
+public class SpringObjectFactory extends ObjectFactory implements ApplicationContextAware {
+    private static final Logger LOG = LoggerFactory.getLogger(SpringObjectFactory.class);
+
+    protected ApplicationContext appContext;
+    protected AutowireCapableBeanFactory autoWiringFactory;
+    protected int autowireStrategy = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
+    private final Map<String, Object> classes = new HashMap<String, Object>();
+    private boolean useClassCache = true;
+    private boolean alwaysRespectAutowireStrategy = false;
+
+    @Inject(value="applicationContextPath",required=false)
+    public void setApplicationContextPath(String ctx) {
+        if (ctx != null) {
+            setApplicationContext(new ClassPathXmlApplicationContext(ctx));
+        }
+    }
+
+    /**
+     * Set the Spring ApplicationContext that should be used to look beans up with.
+     *
+     * @param appContext The Spring ApplicationContext that should be used to look beans up with.
+     */
+    public void setApplicationContext(ApplicationContext appContext)
+            throws BeansException {
+        this.appContext = appContext;
+        autoWiringFactory = findAutoWiringBeanFactory(this.appContext);
+    }
+
+    /**
+     * Sets the autowiring strategy
+     *
+     * @param autowireStrategy
+     */
+    public void setAutowireStrategy(int autowireStrategy) {
+        switch (autowireStrategy) {
+            case AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT:
+                LOG.info("Setting autowire strategy to autodetect");
+                this.autowireStrategy = autowireStrategy;
+                break;
+            case AutowireCapableBeanFactory.AUTOWIRE_BY_NAME:
+                LOG.info("Setting autowire strategy to name");
+                this.autowireStrategy = autowireStrategy;
+                break;
+            case AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE:
+                LOG.info("Setting autowire strategy to type");
+                this.autowireStrategy = autowireStrategy;
+                break;
+            case AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR:
+                LOG.info("Setting autowire strategy to constructor");
+                this.autowireStrategy = autowireStrategy;
+                break;
+            case AutowireCapableBeanFactory.AUTOWIRE_NO:
+                LOG.info("Setting autowire strategy to none");
+                this.autowireStrategy = autowireStrategy;
+                break;
+            default:
+                throw new IllegalStateException("Invalid autowire type set");
+        }
+    }
+
+    public int getAutowireStrategy() {
+        return autowireStrategy;
+    }
+
+
+    /**
+     * If the given context is assignable to AutowireCapbleBeanFactory or contains a parent or a factory that is, then
+     * set the autoWiringFactory appropriately.
+     *
+     * @param context
+     */
+    protected AutowireCapableBeanFactory findAutoWiringBeanFactory(ApplicationContext context) {
+        if (context instanceof AutowireCapableBeanFactory) {
+            // Check the context
+            return (AutowireCapableBeanFactory) context;
+        } else if (context instanceof ConfigurableApplicationContext) {
+            // Try and grab the beanFactory
+            return ((ConfigurableApplicationContext) context).getBeanFactory();
+        } else if (context.getParent() != null) {
+            // And if all else fails, try again with the parent context
+            return findAutoWiringBeanFactory(context.getParent());
+        }
+        return null;
+    }
+
+    /**
+     * Looks up beans using Spring's application context before falling back to the method defined in the {@link
+     * ObjectFactory}.
+     *
+     * @param beanName     The name of the bean to look up in the application context
+     * @param extraContext
+     * @return A bean from Spring or the result of calling the overridden
+     *         method.
+     * @throws Exception
+     */
+    @Override
+    public Object buildBean(String beanName, Map<String, Object> extraContext, boolean injectInternal) throws Exception {
+        Object o = null;
+        try {
+            o = appContext.getBean(beanName);
+        } catch (NoSuchBeanDefinitionException e) {
+            Class beanClazz = getClassInstance(beanName);
+            o = buildBean(beanClazz, extraContext);
+        }
+        if (injectInternal) {
+            injectInternalBeans(o);
+        }
+        return o;
+    }
+
+    /**
+     * @param clazz
+     * @param extraContext
+     * @throws Exception
+     */
+    @Override
+    public Object buildBean(Class clazz, Map<String, Object> extraContext) throws Exception {
+        Object bean;
+
+        try {
+            // Decide to follow autowire strategy or use the legacy approach which mixes injection strategies
+            if (alwaysRespectAutowireStrategy) {
+                // Leave the creation up to Spring
+                bean = autoWiringFactory.createBean(clazz, autowireStrategy, false);
+                injectApplicationContext(bean);
+                return injectInternalBeans(bean);
+            } else {
+                bean = autoWiringFactory.autowire(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
+                bean = autoWiringFactory.applyBeanPostProcessorsBeforeInitialization(bean, bean.getClass().getName());
+                // We don't need to call the init-method since one won't be registered.
+                bean = autoWiringFactory.applyBeanPostProcessorsAfterInitialization(bean, bean.getClass().getName());
+                return autoWireBean(bean, autoWiringFactory);
+            }
+        } catch (UnsatisfiedDependencyException e) {
+            if (LOG.isErrorEnabled())
+                LOG.error("Error building bean", e);
+            // Fall back
+            return autoWireBean(super.buildBean(clazz, extraContext), autoWiringFactory);
+        }
+    }
+
+    public Object autoWireBean(Object bean) {
+        return autoWireBean(bean, autoWiringFactory);
+    }
+
+    /**
+     * @param bean
+     * @param autoWiringFactory
+     */
+    public Object autoWireBean(Object bean, AutowireCapableBeanFactory autoWiringFactory) {
+        if (autoWiringFactory != null) {
+            autoWiringFactory.autowireBeanProperties(bean,
+                    autowireStrategy, false);
+        }
+        injectApplicationContext(bean);
+
+        injectInternalBeans(bean);
+
+        return bean;
+    }
+
+    private void injectApplicationContext(Object bean) {
+        if (bean instanceof ApplicationContextAware) {
+            ((ApplicationContextAware) bean).setApplicationContext(appContext);
+        }
+    }
+
+    public Class getClassInstance(String className) throws ClassNotFoundException {
+        Class clazz = null;
+        if (useClassCache) {
+            synchronized(classes) {
+                // this cache of classes is needed because Spring sucks at dealing with situations where the
+                // class instance changes
+                clazz = (Class) classes.get(className);
+            }
+        }
+
+        if (clazz == null) {
+            if (appContext.containsBean(className)) {
+                clazz = appContext.getBean(className).getClass();
+            } else {
+                clazz = super.getClassInstance(className);
+            }
+
+            if (useClassCache) {
+                synchronized(classes) {
+                    classes.put(className, clazz);
+                }
+            }
+        }
+
+        return clazz;
+    }
+
+    /**
+     * This method sets the ObjectFactory used by XWork to this object. It's best used as the "init-method" of a Spring
+     * bean definition in order to hook Spring and XWork together properly (as an alternative to the
+     * org.apache.struts2.spring.lifecycle.SpringObjectFactoryListener)
+     * @deprecated Since 2.1 as it isn't necessary
+     */
+    @Deprecated public void initObjectFactory() {
+        // not necessary anymore
+    }
+
+    /**
+     * Allows for ObjectFactory implementations that support
+     * Actions without no-arg constructors.
+     *
+     * @return false
+     */
+    @Override
+    public boolean isNoArgConstructorRequired() {
+        return false;
+    }
+
+    /**
+     *  Enable / disable caching of classes loaded by Spring.
+     *
+     * @param useClassCache
+     */
+    public void setUseClassCache(boolean useClassCache) {
+        this.useClassCache = useClassCache;
+    }
+
+    /**
+     * Determines if the autowire strategy is always followed when creating beans
+     *
+     * @param alwaysRespectAutowireStrategy True if the strategy is always used
+     */
+    public void setAlwaysRespectAutowireStrategy(boolean alwaysRespectAutowireStrategy) {
+        this.alwaysRespectAutowireStrategy = alwaysRespectAutowireStrategy;
+    }
+}

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/SpringObjectFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/SpringObjectFactory.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/SpringProxyableObjectFactory.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/SpringProxyableObjectFactory.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/SpringProxyableObjectFactory.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/SpringProxyableObjectFactory.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.spring;
+
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.ApplicationContext;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * SpringProxyableObjectFactory.
+ *
+ * @author Jason Carreira
+ */
+public class SpringProxyableObjectFactory extends SpringObjectFactory {
+    
+    private static final Logger LOG = LoggerFactory.getLogger(SpringProxyableObjectFactory.class);
+
+    private List<String> skipBeanNames = new ArrayList<String>();
+
+    @Override
+    public Object buildBean(String beanName, Map<String, Object> extraContext) throws Exception {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Building bean for name " + beanName);
+        }
+        if (!skipBeanNames.contains(beanName)) {
+            ApplicationContext anAppContext = getApplicationContext(extraContext);
+            try {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Trying the application context... appContext = " + anAppContext + ",\n bean name = " + beanName);
+                }
+                return anAppContext.getBean(beanName);
+            } catch (NoSuchBeanDefinitionException e) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Did not find bean definition for bean named " + beanName + ", creating a new one...");
+                }
+                if (autoWiringFactory instanceof BeanDefinitionRegistry) {
+                    try {
+                        Class clazz = Class.forName(beanName);
+                        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) autoWiringFactory;
+                        RootBeanDefinition def = new RootBeanDefinition(clazz, autowireStrategy);
+                        def.setSingleton(false);
+                         if (LOG.isDebugEnabled()) {
+                            LOG.debug("Registering a new bean definition for class " + beanName);
+                        }
+                        registry.registerBeanDefinition(beanName,def);
+                        try {
+                            return anAppContext.getBean(beanName);
+                        } catch (NoSuchBeanDefinitionException e2) {
+                            LOG.warn("Could not register new bean definition for bean " + beanName);
+                            skipBeanNames.add(beanName);
+                        }
+                    } catch (ClassNotFoundException e1) {
+                        skipBeanNames.add(beanName);
+                    }
+                }
+            }
+        }
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Returning autowired instance created by default ObjectFactory");
+        }
+        return autoWireBean(super.buildBean(beanName, extraContext), autoWiringFactory);
+    }
+
+    /**
+     * Subclasses may override this to return a different application context.
+     * Note that this application context should see any changes made to the
+     * <code>autoWiringFactory</code>, so the application context should be either
+     * the original or a child context of the original.
+     *
+     * @param context  provided context.
+     */
+    protected ApplicationContext getApplicationContext(Map<String, Object> context) {
+        return appContext;
+    }
+}
+

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/SpringProxyableObjectFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/SpringProxyableObjectFactory.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+
+package com.opensymphony.xwork2.spring.interceptor;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
+import com.opensymphony.xwork2.spring.SpringObjectFactory;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.web.context.WebApplicationContext;
+
+/**
+ * <!-- START SNIPPET: description -->
+ * TODO: Give a description of the Interceptor.
+ * <!-- END SNIPPET: description -->
+ *
+ * <!-- START SNIPPET: parameters -->
+ * TODO: Describe the paramters for this Interceptor.
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <!-- START SNIPPET: extending -->
+ * TODO: Discuss some possible extension of the Interceptor.
+ * <!-- END SNIPPET: extending -->
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * &lt;!-- TODO: Describe how the Interceptor reference will effect execution --&gt;
+ * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
+ *      TODO: fill in the interceptor reference.
+ *     &lt;interceptor-ref name=""/&gt;
+ *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
+ * &lt;/action&gt;
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ * 
+ * Autowires action classes to Spring beans.  The strategy for autowiring the beans can be configured
+ * by setting the parameter on the interceptor.  Actions that need access to the <code>ActionContext</code>
+ * can implements the <code>ApplicationContextAware</code> interface.  The context will also be placed on
+ * the action context under the APPLICATION_CONTEXT attribute.
+ *
+ * @author Simon Stewart
+ * @author Eric Hauser
+ */
+public class ActionAutowiringInterceptor extends AbstractInterceptor implements ApplicationContextAware {
+    private static final Logger LOG = LoggerFactory.getLogger(ActionAutowiringInterceptor.class);
+
+    public static final String APPLICATION_CONTEXT = "com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor.applicationContext";
+
+    private boolean initialized = false;
+    private ApplicationContext context;
+    private SpringObjectFactory factory;
+    private Integer autowireStrategy;
+
+    /**
+     * @param autowireStrategy
+     */
+    public void setAutowireStrategy(Integer autowireStrategy) {
+        this.autowireStrategy = autowireStrategy;
+    }
+
+    /**
+     * Looks for the <code>ApplicationContext</code> under the attribute that the Spring listener sets in
+     * the servlet context.  The configuration is done the first time here instead of in init() since the
+     * <code>ActionContext</code> is not available during <code>Interceptor</code> initialization.
+     * <p/>
+     * Autowires the action to Spring beans and places the <code>ApplicationContext</code>
+     * on the <code>ActionContext</code>
+     * <p/>
+     * TODO Should this check to see if the <code>SpringObjectFactory</code> has already been configured
+     * instead of instantiating a new one?  Or is there a good reason for the interceptor to have it's own
+     * factory?
+     *
+     * @param invocation
+     * @throws Exception
+     */
+    @Override public String intercept(ActionInvocation invocation) throws Exception {
+        if (!initialized) {
+            ApplicationContext applicationContext = (ApplicationContext) ActionContext.getContext().getApplication().get(
+                    WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
+
+            if (applicationContext == null) {
+                LOG.warn("ApplicationContext could not be found.  Action classes will not be autowired.");
+            } else {
+                setApplicationContext(applicationContext);
+                factory = new SpringObjectFactory();
+                factory.setApplicationContext(getApplicationContext());
+                if (autowireStrategy != null) {
+                    factory.setAutowireStrategy(autowireStrategy.intValue());
+                }
+            }
+            initialized = true;
+        }
+
+        if (factory != null) {
+            Object bean = invocation.getAction();
+            factory.autoWireBean(bean);
+    
+            ActionContext.getContext().put(APPLICATION_CONTEXT, context);
+        }
+        return invocation.invoke();
+    }
+
+    /**
+     * @param applicationContext
+     * @throws BeansException
+     */
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        context = applicationContext;
+    }
+
+    /**
+     * @return context
+     */
+    protected ApplicationContext getApplicationContext() {
+        return context;
+    }
+
+}

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/package.html
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/package.html?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/package.html (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/package.html Sun Dec 27 18:00:13 2009
@@ -0,0 +1 @@
+<body>Spring specific interceptor classes.</body>

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/package.html
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/package.html
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/package.html
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/package.html?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/package.html (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/package.html Sun Dec 27 18:00:13 2009
@@ -0,0 +1 @@
+<body>Spring ObjectFactory classes.</body>

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/package.html
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/spring/package.html
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/test/StubConfigurationProvider.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/test/StubConfigurationProvider.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/test/StubConfigurationProvider.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/test/StubConfigurationProvider.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,36 @@
+package com.opensymphony.xwork2.test;
+
+import com.opensymphony.xwork2.config.Configuration;
+import com.opensymphony.xwork2.config.ConfigurationException;
+import com.opensymphony.xwork2.config.ConfigurationProvider;
+import com.opensymphony.xwork2.inject.ContainerBuilder;
+import com.opensymphony.xwork2.util.location.LocatableProperties;
+
+public class StubConfigurationProvider implements ConfigurationProvider {
+
+    public void destroy() {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void init(Configuration configuration) throws ConfigurationException {
+        // TODO Auto-generated method stub
+    }
+
+    public void loadPackages() throws ConfigurationException {
+        // TODO Auto-generated method stub
+
+    }
+
+    public boolean needsReload() {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    public void register(ContainerBuilder builder, LocatableProperties props)
+            throws ConfigurationException {
+        // TODO Auto-generated method stub
+
+    }
+
+}

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/test/StubConfigurationProvider.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/test/StubConfigurationProvider.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.util;
+
+import java.io.File;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * <code>AnnotationUtils</code>
+ *
+ * Various utility methods dealing with annotations
+ *
+ * @author Rainer Hermanns
+ * @author Zsolt Szasz, zsolt at lorecraft dot com
+ * @author Dan Oxlade, dan d0t oxlade at gmail d0t c0m
+ * @version $Id$
+ */
+public class AnnotationUtils {
+
+    private static final Pattern SETTER_PATTERN = Pattern.compile("set([A-Z][A-Za-z0-9]*)$");
+    private static final Pattern GETTER_PATTERN = Pattern.compile("(get|is|has)([A-Z][A-Za-z0-9]*)$");
+
+
+
+    /**
+     * Adds all fields with the specified Annotation of class clazz and its superclasses to allFields
+     *
+     * @param annotationClass
+     * @param clazz
+     * @param allFields
+     */
+    public static void addAllFields(Class annotationClass, Class clazz, List<Field> allFields) {
+
+        if (clazz == null) {
+            return;
+        }
+
+        Field[] fields = clazz.getDeclaredFields();
+
+        for (Field field : fields) {
+            Annotation ann = field.getAnnotation(annotationClass);
+            if (ann!=null) {
+                allFields.add(field);
+            }
+        }
+        addAllFields(annotationClass, clazz.getSuperclass(), allFields);
+    }
+
+    /**
+     * Adds all methods with the specified Annotation of class clazz and its superclasses to allFields
+     *
+     * @param annotationClass
+     * @param clazz
+     * @param allMethods
+     */
+    public static void addAllMethods(Class annotationClass, Class clazz, List<Method> allMethods) {
+
+        if (clazz == null) {
+            return;
+        }
+
+        Method[] methods = clazz.getDeclaredMethods();
+
+        for (Method method : methods) {
+            Annotation ann = method.getAnnotation(annotationClass);
+            if (ann!=null) {
+                allMethods.add(method);
+            }
+        }
+        addAllMethods(annotationClass, clazz.getSuperclass(), allMethods);
+    }
+
+    /**
+     *
+     * @param clazz
+     * @param allInterfaces
+     */
+    public static void addAllInterfaces(Class clazz, List<Class> allInterfaces) {
+        if (clazz == null) {
+            return;
+        }
+
+        Class[] interfaces = clazz.getInterfaces();
+        allInterfaces.addAll(Arrays.asList(interfaces));
+        addAllInterfaces(clazz.getSuperclass(), allInterfaces);
+    }
+
+	/**
+	 * For the given <code>Class</code> get a collection of the the {@link AnnotatedElement}s 
+	 * that match the given <code>annotation</code>s or if no <code>annotation</code>s are 
+	 * specified then return all of the annotated elements of the given <code>Class</code>. 
+	 * Includes only the method level annotations.
+	 * 
+	 * @param clazz The {@link Class} to inspect
+	 * @param annotation the {@link Annotation}s to find
+	 * @return A {@link Collection}&lt;{@link AnnotatedElement}&gt; containing all of the
+	 *  method {@link AnnotatedElement}s matching the specified {@link Annotation}s
+	 */
+	public static final Collection<Method> getAnnotatedMethods(Class clazz, Class<? extends Annotation>... annotation){
+		Collection<Method> toReturn = new HashSet<Method>();
+		
+		for(Method m : clazz.getMethods()){
+			if( ArrayUtils.isNotEmpty(annotation) && isAnnotatedBy(m,annotation) ){
+				toReturn.add(m);
+			}else if( ArrayUtils.isEmpty(annotation) && ArrayUtils.isNotEmpty(m.getAnnotations())){
+				toReturn.add(m);
+			}
+		}
+		
+		return toReturn;
+	}
+
+	/**
+	 * Varargs version of <code>AnnotatedElement.isAnnotationPresent()</code>
+	 * @see AnnotatedElement
+	 */
+	public static final boolean isAnnotatedBy(AnnotatedElement annotatedElement, Class<? extends Annotation>... annotation) {
+		if(ArrayUtils.isEmpty(annotation)) return false;
+		
+		for( Class<? extends Annotation> c : annotation ){
+			if( annotatedElement.isAnnotationPresent(c) ) return true;
+		}
+		
+		return false;
+	}    
+    
+	/**
+	 * 
+	 * @deprecated since 2.0.4 use getAnnotatedMethods
+	 */
+    @Deprecated
+    public static List<Method> findAnnotatedMethods(Class clazz, Class<? extends Annotation> annotationClass) {
+        List<Method> methods = new ArrayList<Method>();
+        findRecursively(clazz, annotationClass, methods);
+        return methods;
+    }
+
+    /**
+     * 
+     * @deprecated since 2.0.4 use getAnnotatedMethods
+     */
+    @Deprecated
+    public static void findRecursively(Class clazz, Class<? extends Annotation> annotationClass, List<Method> methods) {
+        for (Method m : clazz.getDeclaredMethods()) {
+            if (m.getAnnotation(annotationClass) != null) { methods.add(0, m); }
+        }
+        if (clazz.getSuperclass() != Object.class) {
+            findRecursively(clazz.getSuperclass(), annotationClass, methods);
+        }
+    }
+
+    /**
+     * Returns the property name for a method.
+     * This method is independant from property fields.
+     *
+     * @param method The method to get the property name for.
+     * @return the property name for given method; null if non could be resolved.
+     */
+    public static String resolvePropertyName(Method method) {
+
+        Matcher matcher = SETTER_PATTERN.matcher(method.getName());
+        if (matcher.matches() && method.getParameterTypes().length == 1) {
+            String raw = matcher.group(1);
+            return raw.substring(0, 1).toLowerCase() + raw.substring(1);
+        }
+
+        matcher = GETTER_PATTERN.matcher(method.getName());
+        if (matcher.matches() && method.getParameterTypes().length == 0) {
+            String raw = matcher.group(2);
+            return raw.substring(0, 1).toLowerCase() + raw.substring(1);
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Retrieves all classes within a packages.
+     * TODO: this currently does not work with jars.
+     *
+     * @param pckgname
+     * @return Array of full qualified class names from this package.
+     */
+    public static String[] find(Class clazz, final String pckgname) {
+
+        List<String> classes = new ArrayList<String>();
+        String name = new String(pckgname);
+        if (!name.startsWith("/")) {
+            name = "/" + name;
+        }
+
+        name = name.replace('.', File.separatorChar);
+
+        final URL url = clazz.getResource(name);
+        final File directory = new File(url.getFile());
+
+        if (directory.exists()) {
+            final String[] files = directory.list();
+            for (String file : files) {
+                if (file.endsWith(".class")) {
+                    classes.add(pckgname + "." + file.substring(0, file.length() - 6));
+                }
+            }
+        }
+        return classes.toArray(new String[classes.size()]);
+    }
+}

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ArrayUtils.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ArrayUtils.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ArrayUtils.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ArrayUtils.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.util;
+
+/**
+ * @author Dan Oxlade, dan d0t oxlade at gmail d0t c0m
+ */
+public class ArrayUtils {
+
+    public static boolean isEmpty(Object[] array) {
+        return null == array || array.length == 0;
+    }
+
+    public static boolean isNotEmpty(Object[] array) {
+        return !isEmpty(array);
+    }
+
+}

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ArrayUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ArrayUtils.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClassLoaderUtil.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClassLoaderUtil.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClassLoaderUtil.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClassLoaderUtil.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.*;
+
+
+/**
+ * This class is extremely useful for loading resources and classes in a fault tolerant manner
+ * that works across different applications servers.
+ *
+ * It has come out of many months of frustrating use of multiple application servers at Atlassian,
+ * please don't change things unless you're sure they're not going to break in one server or another!
+ * 
+ * It was brought in from oscore trunk revision 147.
+ *
+ * @author $Author$
+ * @version $Revision$
+ */
+public class ClassLoaderUtil {
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    /**
+     * Load all resources with a given name, potentially aggregating all results 
+     * from the searched classloaders.  If no results are found, the resource name
+     * is prepended by '/' and tried again.
+     *
+     * This method will try to load the resources using the following methods (in order):
+     * <ul>
+     *  <li>From Thread.currentThread().getContextClassLoader()
+     *  <li>From ClassLoaderUtil.class.getClassLoader()
+     *  <li>callingClass.getClassLoader()
+     * </ul>
+     *
+     * @param resourceName The name of the resources to load
+     * @param callingClass The Class object of the calling object
+     */
+     public static Iterator<URL> getResources(String resourceName, Class callingClass, boolean aggregate) throws IOException {
+
+         AggregateIterator<URL> iterator = new AggregateIterator<URL>();
+
+         iterator.addEnumeration(Thread.currentThread().getContextClassLoader().getResources(resourceName));
+
+         if (!iterator.hasNext() || aggregate) {
+             iterator.addEnumeration(ClassLoaderUtil.class.getClassLoader().getResources(resourceName));
+         }
+
+         if (!iterator.hasNext() || aggregate) {
+             ClassLoader cl = callingClass.getClassLoader();
+
+             if (cl != null) {
+                 iterator.addEnumeration(cl.getResources(resourceName));
+             }
+         }
+
+         if (!iterator.hasNext() && (resourceName != null) && ((resourceName.length() == 0) || (resourceName.charAt(0) != '/'))) { 
+             return getResources('/' + resourceName, callingClass, aggregate);
+         }
+
+         return iterator;
+     }
+
+    /**
+    * Load a given resource.
+    *
+    * This method will try to load the resource using the following methods (in order):
+    * <ul>
+    *  <li>From Thread.currentThread().getContextClassLoader()
+    *  <li>From ClassLoaderUtil.class.getClassLoader()
+    *  <li>callingClass.getClassLoader()
+    * </ul>
+    *
+    * @param resourceName The name IllegalStateException("Unable to call ")of the resource to load
+    * @param callingClass The Class object of the calling object
+    */
+    public static URL getResource(String resourceName, Class callingClass) {
+        URL url = Thread.currentThread().getContextClassLoader().getResource(resourceName);
+
+        if (url == null) {
+            url = ClassLoaderUtil.class.getClassLoader().getResource(resourceName);
+        }
+
+        if (url == null) {
+            ClassLoader cl = callingClass.getClassLoader();
+
+            if (cl != null) {
+                url = cl.getResource(resourceName);
+            }
+        }
+
+        if ((url == null) && (resourceName != null) && ((resourceName.length() == 0) || (resourceName.charAt(0) != '/'))) { 
+            return getResource('/' + resourceName, callingClass);
+        }
+
+        return url;
+    }
+
+    /**
+    * This is a convenience method to load a resource as a stream.
+    *
+    * The algorithm used to find the resource is given in getResource()
+    *
+    * @param resourceName The name of the resource to load
+    * @param callingClass The Class object of the calling object
+    */
+    public static InputStream getResourceAsStream(String resourceName, Class callingClass) {
+        URL url = getResource(resourceName, callingClass);
+
+        try {
+            return (url != null) ? url.openStream() : null;
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    /**
+    * Load a class with a given name.
+    *
+    * It will try to load the class in the following order:
+    * <ul>
+    *  <li>From Thread.currentThread().getContextClassLoader()
+    *  <li>Using the basic Class.forName()
+    *  <li>From ClassLoaderUtil.class.getClassLoader()
+    *  <li>From the callingClass.getClassLoader()
+    * </ul>
+    *
+    * @param className The name of the class to load
+    * @param callingClass The Class object of the calling object
+    * @throws ClassNotFoundException If the class cannot be found anywhere.
+    */
+    public static Class loadClass(String className, Class callingClass) throws ClassNotFoundException {
+        try {
+            return Thread.currentThread().getContextClassLoader().loadClass(className);
+        } catch (ClassNotFoundException e) {
+            try {
+                return Class.forName(className);
+            } catch (ClassNotFoundException ex) {
+                try {
+                    return ClassLoaderUtil.class.getClassLoader().loadClass(className);
+                } catch (ClassNotFoundException exc) {
+                    return callingClass.getClassLoader().loadClass(className);
+                }
+            }
+        }
+    }
+
+    /**
+     * Aggregates Enumeration instances into one iterator and filters out duplicates.  Always keeps one
+     * ahead of the enumerator to protect against returning duplicates.
+     */
+    static class AggregateIterator<E> implements Iterator<E> {
+
+        LinkedList<Enumeration<E>> enums = new LinkedList<Enumeration<E>>();
+        Enumeration<E> cur = null;
+        E next = null;
+        Set<E> loaded = new HashSet<E>();
+
+        public AggregateIterator<E> addEnumeration(Enumeration<E> e) {
+            if (e.hasMoreElements()) {
+                if (cur == null) {
+                    cur = e;
+                    next = e.nextElement();
+                    loaded.add(next);
+                } else {
+                    enums.add(e);
+                }
+            }
+            return this;
+        }
+
+        public boolean hasNext() {
+            return (next != null);
+        }
+
+        public E next() {
+            if (next != null) {
+                E prev = next;
+                next = loadNext();
+                return prev;
+            } else {
+                throw new NoSuchElementException();
+            }
+        }
+
+        private Enumeration<E> determineCurrentEnumeration() {
+            if (cur != null && !cur.hasMoreElements()) {
+                if (enums.size() > 0) {
+                    cur = enums.removeLast();
+                } else {
+                    cur = null;
+                }
+            }
+            return cur;
+        }
+
+        private E loadNext() {
+            if (determineCurrentEnumeration() != null) {
+                E tmp = cur.nextElement();
+                int loadedSize = loaded.size();
+                while (loaded.contains(tmp)) {
+                    tmp = loadNext();
+                    if (tmp == null || loaded.size() > loadedSize) {
+                        break;
+                    }
+                }
+                if (tmp != null) {
+                    loaded.add(tmp);
+                }
+                return tmp;
+            }
+            return null;
+
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+}

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClassLoaderUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClassLoaderUtil.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClassPathFinder.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClassPathFinder.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClassPathFinder.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClassPathFinder.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,177 @@
+/*
+ * $Id$
+ *
+ * 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 com.opensymphony.xwork2.util;
+
+import com.opensymphony.xwork2.XWorkException;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.HashMap;
+import java.util.Vector;
+
+/**
+ * This class is an utility class that will search through the classpath
+ * for files whose names match the given pattern. The filename is tested
+ * using the given implementation of {@link com.opensymphony.xwork2.util.PatternMatcher} by default it 
+ * uses {@link com.opensymphony.xwork2.util.WildcardHelper}
+ *
+ * @version $Rev$ $Date$
+ */
+public class ClassPathFinder {
+	
+	/**
+     * The String pattern to test against.
+     */
+	private String pattern ;
+	
+	private int[] compiledPattern ;
+	
+	/**
+     * The PatternMatcher implementation to use
+     */
+	private PatternMatcher<int[]> patternMatcher = new WildcardHelper();
+	
+	private Vector<String> compared = new Vector<String>();
+	
+	/**
+	 * retrieves the pattern in use
+	 */
+	public String getPattern() {
+		return pattern;
+	}
+
+	/**
+	 * sets the String pattern for comparing filenames
+	 * @param pattern
+	 */
+	public void setPattern(String pattern) {
+		this.pattern = pattern;
+	}
+
+	/**
+     * Builds a {@link java.util.Vector} containing Strings which each name a file
+     * who's name matches the pattern set by setPattern(String). The classpath is 
+     * searched recursively, so use with caution.
+     *
+     * @return Vector<String> containing matching filenames
+     */
+	public Vector<String> findMatches() {
+		Vector<String> matches = new Vector<String>();
+		URLClassLoader cl = getURLClassLoader();
+		if (cl == null ) {
+			throw new XWorkException("unable to attain an URLClassLoader") ;
+		}
+		URL[] parentUrls = cl.getURLs();
+		compiledPattern = (int[]) patternMatcher.compilePattern(pattern);
+		for (URL url : parentUrls) {
+			if (!"file".equals(url.getProtocol())) {
+				continue ;
+			}
+			URI entryURI ;
+			try {
+				entryURI = url.toURI();
+			} catch (URISyntaxException e) {
+				continue;
+			}
+			File entry = new File(entryURI) ;
+			Vector<String> results = checkEntries(entry.list(), entry, "");
+			if (results != null ) {
+				matches.addAll(results);
+			}
+		}
+		return matches;
+	}
+	
+	private Vector<String> checkEntries(String[] entries, File parent, String prefix) {
+		
+		if (entries == null ) {
+			return null;
+		}
+		
+		Vector<String> matches = new Vector<String>();
+		for (String listEntry : entries) {
+			File tempFile ;
+			if (!"".equals(prefix) ) {
+				tempFile = new File(parent, prefix + "/" + listEntry);
+			}
+			else {
+				tempFile = new File(parent, listEntry);
+			}
+			if (tempFile.isDirectory() && 
+					!(".".equals(listEntry) || "..".equals(listEntry)) ) {
+				if	(!"".equals(prefix) ) {
+					matches.addAll(checkEntries(tempFile.list(), parent, prefix + "/" + listEntry));
+				}
+				else {
+					matches.addAll(checkEntries(tempFile.list(), parent, listEntry));
+				}
+			}
+			else {
+				
+				String entryToCheck ;
+				if ("".equals(prefix)) {
+					entryToCheck = listEntry ;
+				}
+				else {
+					entryToCheck = prefix + "/" + listEntry ;
+				}
+				
+				if (compared.contains(entryToCheck) ) {
+					continue;
+				}
+				else {
+					compared.add(entryToCheck) ;
+				}
+				
+				boolean doesMatch = patternMatcher.match(new HashMap<String,String>(), entryToCheck, compiledPattern);
+				if (doesMatch) {
+					matches.add(entryToCheck);
+				}
+			}
+		}
+		return matches ;
+	}
+
+	/**
+	 * sets the PatternMatcher implementation to use when comparing filenames
+	 * @param patternMatcher
+	 */
+	public void setPatternMatcher(PatternMatcher<int[]> patternMatcher) {
+		this.patternMatcher = patternMatcher;
+	}
+
+	private URLClassLoader getURLClassLoader() {
+		URLClassLoader ucl = null;
+		ClassLoader loader = Thread.currentThread().getContextClassLoader();
+		
+		if(! (loader instanceof URLClassLoader)) {
+			loader = ClassPathFinder.class.getClassLoader();
+			if (loader instanceof URLClassLoader) {
+				ucl = (URLClassLoader) loader ;
+			}
+		}
+		else {
+			ucl = (URLClassLoader) loader;
+		}
+		
+		return ucl ;
+	}
+}

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClassPathFinder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClassPathFinder.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClearableValueStack.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClearableValueStack.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClearableValueStack.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClearableValueStack.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,29 @@
+/*
+ * $Id$
+ *
+ * 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 com.opensymphony.xwork2.util;
+
+/**
+ * ValueStacks implementing this interface provide a way to remove values from
+ * their contexts.
+ */
+public interface ClearableValueStack {
+    /**
+     * Remove all values from the context
+     */
+    void clearContextValues();
+}

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClearableValueStack.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/ClearableValueStack.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/CompoundRoot.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/CompoundRoot.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/CompoundRoot.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/CompoundRoot.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * A Stack that is implemented using a List.
+ * 
+ * @author plightbo
+ * @version $Revision$
+ */
+public class CompoundRoot extends ArrayList {
+
+    public CompoundRoot() {
+    }
+
+    public CompoundRoot(List list) {
+        super(list);
+    }
+
+
+    public CompoundRoot cutStack(int index) {
+        return new CompoundRoot(subList(index, size()));
+    }
+
+    public Object peek() {
+        return get(0);
+    }
+
+    public Object pop() {
+        return remove(0);
+    }
+
+    public void push(Object o) {
+        add(0, o);
+    }
+}

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/CompoundRoot.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/CompoundRoot.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/CreateIfNull.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/CreateIfNull.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/CreateIfNull.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/CreateIfNull.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+
+package com.opensymphony.xwork2.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <!-- START SNIPPET: description -->
+ * <p/>Sets the CreateIfNull for type conversion.
+ * <!-- END SNIPPET: description -->
+ *
+ * <p/> <u>Annotation usage:</u>
+ *
+ * <!-- START SNIPPET: usage -->
+ * <p/>The CreateIfNull annotation must be applied at field or method level.
+ * <!-- END SNIPPET: usage -->
+ * <p/> <u>Annotation parameters:</u>
+ *
+ * <!-- START SNIPPET: parameters -->
+ * <table>
+ * <thead>
+ * <tr>
+ * <th>Parameter</th>
+ * <th>Required</th>
+ * <th>Default</th>
+ * <th>Description</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>value</td>
+ * <td>no</td>
+ * <td>false</td>
+ * <td>The CreateIfNull property value.</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <p/> <u>Example code:</u>
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * &#64;CreateIfNull( value = true )
+ * private List<User> users;
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ *
+ * @author Rainer Hermanns
+ * @version $Id$
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.METHOD})
+public @interface CreateIfNull {
+
+    /**
+     * The CreateIfNull value.
+     * Defaults to <tt>true</tt>.
+     */
+    boolean value() default true;
+}

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/CreateIfNull.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/CreateIfNull.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/DomHelper.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/DomHelper.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/DomHelper.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/DomHelper.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,366 @@
+/*
+ * Copyright 1999-2005 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 com.opensymphony.xwork2.util;
+
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.XWorkException;
+import com.opensymphony.xwork2.util.location.Location;
+import com.opensymphony.xwork2.util.location.LocationAttributes;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.xml.sax.*;
+import org.xml.sax.helpers.DefaultHandler;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import java.util.Map;
+
+/**
+ * Helper class to create and retrieve information from location-enabled
+ * DOM-trees.
+ *
+ * @since 1.2
+ */
+public class DomHelper {
+
+    private static final Logger LOG = LoggerFactory.getLogger(DomHelper.class);
+    
+    public static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
+
+    public static Location getLocationObject(Element element) {
+        return LocationAttributes.getLocation(element);
+    }
+
+    
+    /**
+     * Creates a W3C Document that remembers the location of each element in
+     * the source file. The location of element nodes can then be retrieved
+     * using the {@link #getLocationObject(Element)} method.
+     *
+     * @param inputSource the inputSource to read the document from
+     */
+    public static Document parse(InputSource inputSource) {
+        return parse(inputSource, null);
+    }
+    
+    
+    /**
+     * Creates a W3C Document that remembers the location of each element in
+     * the source file. The location of element nodes can then be retrieved
+     * using the {@link #getLocationObject(Element)} method.
+     *
+     * @param inputSource the inputSource to read the document from
+     * @param dtdMappings a map of DTD names and public ids
+     */
+    public static Document parse(InputSource inputSource, Map<String, String> dtdMappings) {
+                
+        SAXParserFactory factory = null;
+        String parserProp = System.getProperty("xwork.saxParserFactory");
+        if (parserProp != null) {
+            try {
+                Class clazz = ObjectFactory.getObjectFactory().getClassInstance(parserProp);
+                factory = (SAXParserFactory) clazz.newInstance();
+            }
+            catch (ClassNotFoundException e) {
+                LOG.error("Unable to load saxParserFactory set by system property 'xwork.saxParserFactory': " + parserProp, e);
+            }
+            catch (Exception e) {
+                LOG.error("Unable to load saxParserFactory set by system property 'xwork.saxParserFactory': " + parserProp, e);
+            }
+        }
+
+        if (factory == null) {
+            factory = SAXParserFactory.newInstance();
+        }
+
+        factory.setValidating((dtdMappings != null));
+        factory.setNamespaceAware(true);
+
+        SAXParser parser = null;
+        try {
+            parser = factory.newSAXParser();
+        } catch (Exception ex) {
+            throw new XWorkException("Unable to create SAX parser", ex);
+        }
+        
+        
+        DOMBuilder builder = new DOMBuilder();
+
+        // Enhance the sax stream with location information
+        ContentHandler locationHandler = new LocationAttributes.Pipe(builder);
+        
+        try {
+            parser.parse(inputSource, new StartHandler(locationHandler, dtdMappings));
+        } catch (Exception ex) {
+            throw new XWorkException(ex);
+        }
+        
+        return builder.getDocument();
+    }
+    
+    /**
+     * The <code>DOMBuilder</code> is a utility class that will generate a W3C
+     * DOM Document from SAX events.
+     *
+     * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
+     */
+    static public class DOMBuilder implements ContentHandler {
+    
+        /** The default transformer factory shared by all instances */
+        protected static SAXTransformerFactory FACTORY;
+    
+        /** The transformer factory */
+        protected SAXTransformerFactory factory;
+    
+        /** The result */
+        protected DOMResult result;
+    
+        /** The parentNode */
+        protected Node parentNode;
+        
+        protected ContentHandler nextHandler;
+    
+        static {
+            String parserProp = System.getProperty("xwork.saxTransformerFactory");
+            if (parserProp != null) {
+                try {
+                    Class clazz = ObjectFactory.getObjectFactory().getClassInstance(parserProp);
+                    FACTORY = (SAXTransformerFactory) clazz.newInstance();
+                }
+                catch (ClassNotFoundException e) {
+                    LOG.error("Unable to load SAXTransformerFactory set by system property 'xwork.saxTransformerFactory': " + parserProp, e);
+                }
+                catch (Exception e) {
+                    LOG.error("Unable to load SAXTransformerFactory set by system property 'xwork.saxTransformerFactory': " + parserProp, e);
+                }
+            }
+
+            if (FACTORY == null) {
+                 FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
+            }
+        }
+
+        /**
+         * Construct a new instance of this DOMBuilder.
+         */
+        public DOMBuilder() {
+            this((Node) null);
+        }
+    
+        /**
+         * Construct a new instance of this DOMBuilder.
+         */
+        public DOMBuilder(SAXTransformerFactory factory) {
+            this(factory, null);
+        }
+    
+        /**
+         * Constructs a new instance that appends nodes to the given parent node.
+         */
+        public DOMBuilder(Node parentNode) {
+            this(null, parentNode);
+        }
+    
+        /**
+         * Construct a new instance of this DOMBuilder.
+         */
+        public DOMBuilder(SAXTransformerFactory factory, Node parentNode) {
+            this.factory = factory == null? FACTORY: factory;
+            this.parentNode = parentNode;
+            setup();
+        }
+    
+        /**
+         * Setup this instance transformer and result objects.
+         */
+        private void setup() {
+            try {
+                TransformerHandler handler = this.factory.newTransformerHandler();
+                nextHandler = handler;
+                if (this.parentNode != null) {
+                    this.result = new DOMResult(this.parentNode);
+                } else {
+                    this.result = new DOMResult();
+                }
+                handler.setResult(this.result);
+            } catch (javax.xml.transform.TransformerException local) {
+                throw new XWorkException("Fatal-Error: Unable to get transformer handler", local);
+            }
+        }
+    
+        /**
+         * Return the newly built Document.
+         */
+        public Document getDocument() {
+            if (this.result == null || this.result.getNode() == null) {
+                return null;
+            } else if (this.result.getNode().getNodeType() == Node.DOCUMENT_NODE) {
+                return (Document) this.result.getNode();
+            } else {
+                return this.result.getNode().getOwnerDocument();
+            }
+        }
+    
+        public void setDocumentLocator(Locator locator) {
+            nextHandler.setDocumentLocator(locator);
+        }
+        
+        public void startDocument() throws SAXException {
+            nextHandler.startDocument();
+        }
+        
+        public void endDocument() throws SAXException {
+            nextHandler.endDocument();
+        }
+    
+        public void startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException {
+            nextHandler.startElement(uri, loc, raw, attrs);
+        }
+    
+        public void endElement(String arg0, String arg1, String arg2) throws SAXException {
+            nextHandler.endElement(arg0, arg1, arg2);
+        }
+    
+        public void startPrefixMapping(String arg0, String arg1) throws SAXException {
+            nextHandler.startPrefixMapping(arg0, arg1);
+        }
+    
+        public void endPrefixMapping(String arg0) throws SAXException {
+            nextHandler.endPrefixMapping(arg0);
+        }
+    
+        public void characters(char[] arg0, int arg1, int arg2) throws SAXException {
+            nextHandler.characters(arg0, arg1, arg2);
+        }
+    
+        public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException {
+            nextHandler.ignorableWhitespace(arg0, arg1, arg2);
+        }
+    
+        public void processingInstruction(String arg0, String arg1) throws SAXException {
+            nextHandler.processingInstruction(arg0, arg1);
+        }
+    
+        public void skippedEntity(String arg0) throws SAXException {
+            nextHandler.skippedEntity(arg0);
+        }
+    }
+    
+    public static class StartHandler extends DefaultHandler {
+        
+        private ContentHandler nextHandler;
+        private Map<String, String> dtdMappings;
+        
+        /**
+         * Create a filter that is chained to another handler.
+         * @param next the next handler in the chain.
+         */
+        public StartHandler(ContentHandler next, Map<String, String> dtdMappings) {
+            nextHandler = next;
+            this.dtdMappings = dtdMappings;
+        }
+
+        @Override
+        public void setDocumentLocator(Locator locator) {
+            nextHandler.setDocumentLocator(locator);
+        }
+        
+        @Override
+        public void startDocument() throws SAXException {
+            nextHandler.startDocument();
+        }
+        
+        @Override
+        public void endDocument() throws SAXException {
+            nextHandler.endDocument();
+        }
+
+        @Override
+        public void startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException {
+            nextHandler.startElement(uri, loc, raw, attrs);
+        }
+
+        @Override
+        public void endElement(String arg0, String arg1, String arg2) throws SAXException {
+            nextHandler.endElement(arg0, arg1, arg2);
+        }
+
+        @Override
+        public void startPrefixMapping(String arg0, String arg1) throws SAXException {
+            nextHandler.startPrefixMapping(arg0, arg1);
+        }
+
+        @Override
+        public void endPrefixMapping(String arg0) throws SAXException {
+            nextHandler.endPrefixMapping(arg0);
+        }
+
+        @Override
+        public void characters(char[] arg0, int arg1, int arg2) throws SAXException {
+            nextHandler.characters(arg0, arg1, arg2);
+        }
+
+        @Override
+        public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException {
+            nextHandler.ignorableWhitespace(arg0, arg1, arg2);
+        }
+
+        @Override
+        public void processingInstruction(String arg0, String arg1) throws SAXException {
+            nextHandler.processingInstruction(arg0, arg1);
+        }
+
+        @Override
+        public void skippedEntity(String arg0) throws SAXException {
+            nextHandler.skippedEntity(arg0);
+        }
+        
+        @Override
+        public InputSource resolveEntity(String publicId, String systemId) {
+            if (dtdMappings != null && dtdMappings.containsKey(publicId)) {
+                String val = dtdMappings.get(publicId).toString();
+                return new InputSource(ClassLoaderUtil.getResourceAsStream(val, DomHelper.class));
+            }
+            return null;
+        }
+        
+        @Override
+        public void warning(SAXParseException exception) {
+        }
+
+        @Override
+        public void error(SAXParseException exception) throws SAXException {
+            LOG.error(exception.getMessage() + " at (" + exception.getPublicId() + ":" + 
+                exception.getLineNumber() + ":" + exception.getColumnNumber() + ")", exception);
+            throw exception;
+        }
+
+        @Override
+        public void fatalError(SAXParseException exception) throws SAXException {
+            LOG.fatal(exception.getMessage() + " at (" + exception.getPublicId() + ":" + 
+                exception.getLineNumber() + ":" + exception.getColumnNumber() + ")", exception);
+            throw exception;
+        }
+    }
+
+}

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/DomHelper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/DomHelper.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL



Mime
View raw message