You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2011/12/02 17:33:45 UTC
svn commit: r1209569 [18/50] - in /struts/struts2/branches/STRUTS_3_X:
apps/blank/src/main/java/example/ apps/blank/src/test/java/example/
apps/jboss-blank/src/main/java/example/
apps/jboss-blank/src/test/java/example/ apps/mailreader/src/main/java/mai...
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/Conversion.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/Conversion.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/Conversion.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/Conversion.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts2.xwork2.conversion.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <!-- START SNIPPET: description -->
+ * <p/>A marker annotation for type conversions at Type level.
+ * <!-- END SNIPPET: description -->
+ *
+ * <p/> <u>Annotation usage:</u>
+ *
+ * <!-- START SNIPPET: usage -->
+ * <p/>The Conversion annotation must be applied at Type 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>conversion</td>
+ * <td>no</td>
+ * <td> </td>
+ * <td>used for Type Conversions applied at Type level.</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <p/> <u>Example code:</u>
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * @Conversion()
+ * public class ConversionAction implements Action {
+ * }
+ *
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ *
+ * @author Rainer Hermanns
+ * @version $Id: Conversion.java 1209415 2011-12-02 11:24:48Z lukaszlenart $
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Conversion {
+
+ /**
+ * Allow Type Conversions being applied at Type level.
+ */
+ TypeConversion[] conversions() default {};
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/ConversionRule.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/ConversionRule.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/ConversionRule.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/ConversionRule.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts2.xwork2.conversion.annotations;
+
+/**
+ * <code>ConversionRule</code>
+ *
+ * @author Rainer Hermanns
+ * @version $Id: ConversionRule.java 1209415 2011-12-02 11:24:48Z lukaszlenart $
+ */
+public enum ConversionRule {
+
+ PROPERTY, COLLECTION, MAP, KEY, KEY_PROPERTY, ELEMENT, CREATE_IF_NULL;
+
+ @Override
+ public String toString() {
+ return super.toString().toUpperCase();
+ }
+}
+
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/ConversionType.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/ConversionType.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/ConversionType.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/ConversionType.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts2.xwork2.conversion.annotations;
+
+/**
+ * <code>ConversionType</code>
+ *
+ * @author <a href="mailto:hermanns@aixcept.de">Rainer Hermanns</a>
+ * @version $Id: ConversionType.java 1209415 2011-12-02 11:24:48Z lukaszlenart $
+ */
+public enum ConversionType {
+
+
+ APPLICATION, CLASS;
+
+ @Override
+ public String toString() {
+ return super.toString().toUpperCase();
+ }
+
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/TypeConversion.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/TypeConversion.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/TypeConversion.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/TypeConversion.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts2.xwork2.conversion.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <!-- START SNIPPET: description -->
+ * <p/>This annotation is used for class and application wide conversion rules.
+ * <p>
+ * Class wide conversion:<br/>
+ * The conversion rules will be assembled in a file called <code>XXXAction-conversion.properties</code>
+ * within the same package as the related action class.
+ * Set type to: <code>type = ConversionType.CLASS</code>
+ * </p>
+ * <p>
+ * Allication wide conversion:<br/>
+ * The conversion rules will be assembled within the <code>xwork-conversion.properties</code> file within the classpath root.
+ * Set type to: <code>type = ConversionType.APPLICATION</code>
+ * <p/>
+ * <!-- END SNIPPET: description -->
+ *
+ * <p/> <u>Annotation usage:</u>
+ *
+ * <!-- START SNIPPET: usage -->
+ * The TypeConversion annotation can be applied at property and 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>key</td>
+ * <td>no</td>
+ * <td>The annotated property/key name</td>
+ * <td>The optional property name mostly used within TYPE level annotations.</td>
+ * </tr>
+ * <tr>
+ * <td>type</td>
+ * <td>no</td>
+ * <td>ConversionType.CLASS</td>
+ * <td>Enum value of ConversionType. Determines whether the conversion should be applied at application or class level.</td>
+ * </tr>
+ * <tr>
+ * <td>rule</td>
+ * <td>no</td>
+ * <td>ConversionRule.PROPERTY</td>
+ * <td>Enum value of ConversionRule. The ConversionRule can be a property, a Collection or a Map.</td>
+ * </tr>
+ * <tr>
+ * <td>converter</td>
+ * <td>either this or value</td>
+ * <td> </td>
+ * <td>The class name of the TypeConverter to be used as converter.</td>
+ * </tr>
+ * <tr>
+ * <td>value</td>
+ * <td>either converter or this</td>
+ * <td> </td>
+ * <td>The value to set for ConversionRule.KEY_PROPERTY.</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <p/> <u>Example code:</u>
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * @Conversion()
+ * public class ConversionAction implements Action {
+ *
+ * private String convertInt;
+ *
+ * private String convertDouble;
+ * private List users = null;
+ *
+ * private HashMap keyValues = null;
+ *
+ * @TypeConversion(type = ConversionType.APPLICATION, converter = "org.apache.struts2.xwork2.util.XWorkBasicConverter")
+ * public void setConvertInt( String convertInt ) {
+ * this.convertInt = convertInt;
+ * }
+ *
+ * @TypeConversion(converter = "org.apache.struts2.xwork2.util.XWorkBasicConverter")
+ * public void setConvertDouble( String convertDouble ) {
+ * this.convertDouble = convertDouble;
+ * }
+ *
+ * @TypeConversion(rule = ConversionRule.COLLECTION, converter = "java.util.String")
+ * public void setUsers( List users ) {
+ * this.users = users;
+ * }
+ *
+ * @TypeConversion(rule = ConversionRule.MAP, converter = "java.math.BigInteger")
+ * public void setKeyValues( HashMap keyValues ) {
+ * this.keyValues = keyValues;
+ * }
+ *
+ * @TypeConversion(type = ConversionType.APPLICATION, property = "java.util.Date", converter = "org.apache.struts2.xwork2.util.XWorkBasicConverter")
+ * public String execute() throws Exception {
+ * return SUCCESS;
+ * }
+ * }
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ *
+ * @author Rainer Hermanns
+ * @version $Id: TypeConversion.java 1209415 2011-12-02 11:24:48Z lukaszlenart $
+ */
+@Target({ ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface TypeConversion {
+
+ /**
+ * The optional key name used within TYPE level annotations.
+ * Defaults to the property name.
+ */
+ String key() default "";
+
+ /**
+ * The ConversionType can be either APPLICATION or CLASS.
+ * Defaults to CLASS.
+ *
+ * Note: If you use ConversionType.APPLICATION, you can not set a value!
+ */
+ ConversionType type() default ConversionType.CLASS;
+
+ /**
+ * The ConversionRule can be a PROPERTY, KEY, KEY_PROPERTY, ELEMENT, COLLECTION (deprecated) or a MAP.
+ * Note: Collection and Map vonversion rules can be determined via org.apache.struts2.xwork2.util.DefaultObjectTypeDeterminer.
+ *
+ * @see org.apache.struts2.xwork2.conversion.impl.DefaultObjectTypeDeterminer
+ */
+ ConversionRule rule() default ConversionRule.PROPERTY;
+
+ /**
+ * The class of the TypeConverter to be used as converter.
+ *
+ * Note: This can not be used with ConversionRule.KEY_PROPERTY!
+ */
+ String converter() default "";
+
+ /**
+ * If used with ConversionRule.KEY_PROPERTY specify a value here!
+ *
+ * Note: If you use ConversionType.APPLICATION, you can not set a value!
+ */
+ String value() default "";
+
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/package.html
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/package.html?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/package.html (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/annotations/package.html Fri Dec 2 16:33:03 2011
@@ -0,0 +1 @@
+<body>Type conversion annotations.</body>
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/AnnotationXWorkConverter.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/AnnotationXWorkConverter.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/AnnotationXWorkConverter.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/AnnotationXWorkConverter.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts2.xwork2.conversion.impl;
+
+/**
+ * <!-- START SNIPPET: javadoc -->
+ * <p/>
+ * Type conversion is great for situations where you need to turn a String in to a more complex object. Because the web
+ * is type-agnostic (everything is a string in HTTP), XWork's type conversion features are very useful. For instance,
+ * if you were prompting a user to enter in coordinates in the form of a string (such as "3, 22"), you could have
+ * XWork do the conversion both from String to Point and from Point to String.
+ * <p/>
+ * <p/> Using this "point" example, if your action (or another compound object in which you are setting properties on)
+ * has a corresponding ClassName-conversion.properties file, XWork will use the configured type converters for
+ * conversion to and from strings. So turning "3, 22" in to new Point(3, 22) is done by merely adding the following
+ * entry to <b>ClassName-conversion.properties</b> (Note that the PointConverter should impl the TypeConverter
+ * interface):
+ * <p/>
+ * <p/><b>point = com.acme.PointConverter</b>
+ * <p/>
+ * <p/> Your type converter should be sure to check what class type it is being requested to convert. Because it is used
+ * for both to and from strings, you will need to split the conversion method in to two parts: one that turns Strings in
+ * to Points, and one that turns Points in to Strings.
+ * <p/>
+ * <p/> After this is done, you can now reference your point (using <ww:property value="post"/> in JSP or ${point}
+ * in FreeMarker) and it will be printed as "3, 22" again. As such, if you submit this back to an action, it will be
+ * converted back to a Point once again.
+ * <p/>
+ * <p/> In some situations you may wish to apply a type converter globally. This can be done by editing the file
+ * <b>xwork-conversion.properties</b> in the root of your class path (typically WEB-INF/classes) and providing a
+ * property in the form of the class name of the object you wish to convert on the left hand side and the class name of
+ * the type converter on the right hand side. For example, providing a type converter for all Point objects would mean
+ * adding the following entry:
+ * <p/>
+ * <p/><b>com.acme.Point = com.acme.PointConverter</b>
+ * <p/>
+ * <!-- END SNIPPET: javadoc -->
+ * <p/>
+ * <p/>
+ * <p/>
+ * <!-- START SNIPPET: i18n-note -->
+ * <p/>
+ * Type conversion should not be used as a substitute for i18n. It is not recommended to use this feature to print out
+ * properly formatted dates. Rather, you should use the i18n features of XWork (and consult the JavaDocs for JDK's
+ * MessageFormat object) to see how a properly formatted date should be displayed.
+ * <p/>
+ * <!-- END SNIPPET: i18n-note -->
+ * <p/>
+ * <p/>
+ * <p/>
+ * <!-- START SNIPPET: error-reporting -->
+ * <p/>
+ * Any error that occurs during type conversion may or may not wish to be reported. For example, reporting that the
+ * input "abc" could not be converted to a number might be important. On the other hand, reporting that an empty string,
+ * "", cannot be converted to a number might not be important - especially in a web environment where it is hard to
+ * distinguish between a user not entering a value vs. entering a blank value.
+ * <p/>
+ * <p/> By default, all conversion errors are reported using the generic i18n key <b>xwork.default.invalid.fieldvalue</b>,
+ * which you can override (the default text is <i>Invalid field value for field "xxx"</i>, where xxx is the field name)
+ * in your global i18n resource bundle.
+ * <p/>
+ * <p/>However, sometimes you may wish to override this message on a per-field basis. You can do this by adding an i18n
+ * key associated with just your action (Action.properties) using the pattern <b>invalid.fieldvalue.xxx</b>, where xxx
+ * is the field name.
+ * <p/>
+ * <p/>It is important to know that none of these errors are actually reported directly. Rather, they are added to a map
+ * called <i>conversionErrors</i> in the ActionContext. There are several ways this map can then be accessed and the
+ * errors can be reported accordingly.
+ * <p/>
+ * <!-- END SNIPPET: error-reporting -->
+ *
+ * @author <a href="mailto:plightbo@gmail.com">Pat Lightbody</a>
+ * @author Rainer Hermanns
+ * @see XWorkConverter
+ * @deprecated Since XWork 2.0.4, the implementation of XWorkConverter handles the processing of annotations.
+ */
+@Deprecated public class AnnotationXWorkConverter extends XWorkConverter {
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/DefaultObjectTypeDeterminer.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/DefaultObjectTypeDeterminer.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/DefaultObjectTypeDeterminer.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/DefaultObjectTypeDeterminer.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,360 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts2.xwork2.conversion.impl;
+
+import org.apache.struts2.xwork2.conversion.ObjectTypeDeterminer;
+import org.apache.struts2.xwork2.inject.Inject;
+import org.apache.struts2.xwork2.util.CreateIfNull;
+import org.apache.struts2.xwork2.util.Element;
+import org.apache.struts2.xwork2.util.Key;
+import org.apache.struts2.xwork2.util.KeyProperty;
+import org.apache.struts2.xwork2.util.logging.Logger;
+import org.apache.struts2.xwork2.util.logging.LoggerFactory;
+import org.apache.struts2.xwork2.util.reflection.ReflectionException;
+import org.apache.struts2.xwork2.util.reflection.ReflectionProvider;
+
+import java.beans.IntrospectionException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+/**
+ * <!-- START SNIPPET: javadoc -->
+ *
+ * This {@link ObjectTypeDeterminer} looks at the <b>Class-conversion.properties</b> for entries that indicated what
+ * objects are contained within Maps and Collections. For Collections, such as Lists, the element is specified using the
+ * pattern <b>Element_xxx</b>, where xxx is the field name of the collection property in your action or object. For
+ * Maps, both the key and the value may be specified by using the pattern <b>Key_xxx</b> and <b>Element_xxx</b>,
+ * respectively.
+ *
+ * <p/> From WebWork 2.1.x, the <b>Collection_xxx</b> format is still supported and honored, although it is deprecated
+ * and will be removed eventually.
+ *
+ * <!-- END SNIPPET: javadoc -->
+ *
+ *
+ * @author Gabriel Zimmerman
+ */
+public class DefaultObjectTypeDeterminer implements ObjectTypeDeterminer {
+
+
+ protected static final Logger LOG = LoggerFactory.getLogger(DefaultObjectTypeDeterminer.class);
+
+ public static final String KEY_PREFIX = "Key_";
+ public static final String ELEMENT_PREFIX = "Element_";
+ public static final String KEY_PROPERTY_PREFIX = "KeyProperty_";
+ public static final String CREATE_IF_NULL_PREFIX = "CreateIfNull_";
+ public static final String DEPRECATED_ELEMENT_PREFIX = "Collection_";
+
+ private ReflectionProvider reflectionProvider;
+ private XWorkConverter xworkConverter;
+
+ @Inject
+ public DefaultObjectTypeDeterminer(@Inject XWorkConverter conv, @Inject ReflectionProvider prov) {
+ this.reflectionProvider = prov;
+ this.xworkConverter = conv;
+
+ }
+
+ /**
+ * Determines the key class by looking for the value of @Key annotation for the given class.
+ * If no annotation is found, the key class is determined by using the generic parametrics.
+ *
+ * As fallback, it determines the key class by looking for the value of Key_${property} in the properties
+ * file for the given class.
+ *
+ * @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
+ * @param property the property of the Map or Collection for the given parent class
+ * @see org.apache.struts2.xwork2.conversion.ObjectTypeDeterminer#getKeyClass(Class, String)
+ */
+ public Class getKeyClass(Class parentClass, String property) {
+ Key annotation = getAnnotation(parentClass, property, Key.class);
+
+ if (annotation != null) {
+ return annotation.value();
+ }
+
+ Class clazz = getClass(parentClass, property, false);
+
+ if (clazz != null) {
+ return clazz;
+ }
+
+ return (Class) xworkConverter.getConverter(parentClass, KEY_PREFIX + property);
+ }
+
+
+ /**
+ * Determines the element class by looking for the value of @Element annotation for the given
+ * class.
+ * If no annotation is found, the element class is determined by using the generic parametrics.
+ *
+ * As fallback, it determines the key class by looking for the value of Element_${property} in the properties
+ * file for the given class. Also looks for the deprecated Collection_${property}
+ *
+ * @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
+ * @param property the property of the Map or Collection for the given parent class
+ * @see org.apache.struts2.xwork2.conversion.ObjectTypeDeterminer#getElementClass(Class, String, Object)
+ */
+ public Class getElementClass(Class parentClass, String property, Object key) {
+ Element annotation = getAnnotation(parentClass, property, Element.class);
+
+ if (annotation != null) {
+ return annotation.value();
+ }
+
+ Class clazz = getClass(parentClass, property, true);
+
+ if (clazz != null) {
+ return clazz;
+ }
+
+ clazz = (Class) xworkConverter.getConverter(parentClass, ELEMENT_PREFIX + property);
+
+ if (clazz == null) {
+ clazz = (Class) xworkConverter
+ .getConverter(parentClass, DEPRECATED_ELEMENT_PREFIX + property);
+
+ if (LOG.isInfoEnabled() && clazz != null) {
+ LOG.info("The Collection_xxx pattern for collection type conversion is deprecated. Please use Element_xxx!");
+ }
+ }
+ return clazz;
+
+ }
+
+
+ /**
+ * Determines the key property for a Collection by getting it from the @KeyProperty annotation.
+ *
+ * As fallback, it determines the String key property for a Collection by getting it from the conversion properties
+ * file using the KeyProperty_ prefix. KeyProperty_${property}=somePropertyOfBeansInTheSet
+ *
+ * @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
+ * @param property the property of the Map or Collection for the given parent class
+ * @see org.apache.struts2.xwork2.conversion.ObjectTypeDeterminer#getKeyProperty(Class, String)
+ */
+ public String getKeyProperty(Class parentClass, String property) {
+ KeyProperty annotation = getAnnotation(parentClass, property, KeyProperty.class);
+
+ if (annotation != null) {
+ return annotation.value();
+ }
+
+ return (String) xworkConverter.getConverter(parentClass, KEY_PROPERTY_PREFIX + property);
+ }
+
+
+ /**
+ * Determines the createIfNull property for a Collection or Map by getting it from the @CreateIfNull annotation.
+ *
+ * As fallback, it determines the boolean CreateIfNull property for a Collection or Map by getting it from the
+ * conversion properties file using the CreateIfNull_ prefix. CreateIfNull_${property}=true|false
+ *
+ * @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
+ * @param property the property of the Map or Collection for the given parent class
+ * @param target the target object
+ * @param keyProperty the keyProperty value
+ * @param isIndexAccessed <tt>true</tt>, if the collection or map is accessed via index, <tt>false</tt> otherwise.
+ * @return <tt>true</tt>, if the Collection or Map should be created, <tt>false</tt> otherwise.
+ * @see org.apache.struts2.xwork2.conversion.ObjectTypeDeterminer#getKeyProperty(Class, String)
+ */
+ public boolean shouldCreateIfNew(Class parentClass,
+ String property,
+ Object target,
+ String keyProperty,
+ boolean isIndexAccessed) {
+
+ CreateIfNull annotation = getAnnotation(parentClass, property, CreateIfNull.class);
+
+ if (annotation != null) {
+ return annotation.value();
+ }
+
+ String configValue = (String) xworkConverter.getConverter(parentClass, CREATE_IF_NULL_PREFIX + property);
+ //check if a value is in the config
+ if (configValue!=null) {
+ if ("true".equalsIgnoreCase(configValue)) {
+ return true;
+ }
+ if ("false".equalsIgnoreCase(configValue)) {
+ return false;
+ }
+ }
+
+ //default values depend on target type
+ //and whether this is accessed by an index
+ //in the case of List
+ if ((target instanceof Map) || isIndexAccessed) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ /**
+ * Retrieves an annotation for the specified property of field, setter or getter.
+ *
+ * @param <T> the annotation type to be retrieved
+ * @param parentClass the class
+ * @param property the property
+ * @param annotationClass the annotation
+ * @return the field or setter/getter annotation or <code>null</code> if not found
+ */
+ protected <T extends Annotation> T getAnnotation(Class parentClass, String property, Class<T> annotationClass) {
+ T annotation = null;
+ Field field = reflectionProvider.getField(parentClass, property);
+
+ if (field != null) {
+ annotation = field.getAnnotation(annotationClass);
+ }
+ if (annotation == null) { // HINT: try with setter
+ annotation = getAnnotationFromSetter(parentClass, property, annotationClass);
+ }
+ if (annotation == null) { // HINT: try with getter
+ annotation = getAnnotationFromGetter(parentClass, property, annotationClass);
+ }
+
+ return annotation;
+ }
+
+ /**
+ * Retrieves an annotation for the specified field of getter.
+ *
+ * @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
+ * @param property the property of the Map or Collection for the given parent class
+ * @param annotationClass The annotation
+ * @return concrete Annotation instance or <tt>null</tt> if none could be retrieved.
+ */
+ private <T extends Annotation>T getAnnotationFromGetter(Class parentClass, String property, Class<T> annotationClass) {
+ try {
+ Method getter = reflectionProvider.getGetMethod(parentClass, property);
+
+ if (getter != null) {
+ return getter.getAnnotation(annotationClass);
+ }
+ }
+ catch (ReflectionException ognle) {
+ ; // ignore
+ }
+ catch (IntrospectionException ie) {
+ ; // ignore
+ }
+ return null;
+ }
+
+ /**
+ * Retrieves an annotation for the specified field of setter.
+ *
+ * @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
+ * @param property the property of the Map or Collection for the given parent class
+ * @param annotationClass The annotation
+ * @return concrete Annotation instance or <tt>null</tt> if none could be retrieved.
+ */
+ private <T extends Annotation>T getAnnotationFromSetter(Class parentClass, String property, Class<T> annotationClass) {
+ try {
+ Method setter = reflectionProvider.getSetMethod(parentClass, property);
+
+ if (setter != null) {
+ return setter.getAnnotation(annotationClass);
+ }
+ }
+ catch (ReflectionException ognle) {
+ ; // ignore
+ }
+ catch (IntrospectionException ie) {
+ ; // ignore
+ }
+ return null;
+ }
+
+ /**
+ * Returns the class for the given field via generic type check.
+ *
+ * @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
+ * @param property the property of the Map or Collection for the given parent class
+ * @param element <tt>true</tt> for indexed types and Maps.
+ * @return Class of the specified field.
+ */
+ private Class getClass(Class parentClass, String property, boolean element) {
+
+
+ try {
+
+ Field field = reflectionProvider.getField(parentClass, property);
+
+ Type genericType = null;
+
+ // Check fields first
+ if (field != null) {
+ genericType = field.getGenericType();
+ }
+
+ // Try to get ParameterType from setter method
+ if (genericType == null || !(genericType instanceof ParameterizedType)) {
+ try {
+ Method setter = reflectionProvider.getSetMethod(parentClass, property);
+ genericType = setter.getGenericParameterTypes()[0];
+ }
+ catch (ReflectionException ognle) {
+ ; // ignore
+ }
+ catch (IntrospectionException ie) {
+ ; // ignore
+ }
+ }
+
+ // Try to get ReturnType from getter method
+ if (genericType == null || !(genericType instanceof ParameterizedType)) {
+ try {
+ Method getter = reflectionProvider.getGetMethod(parentClass, property);
+ genericType = getter.getGenericReturnType();
+ }
+ catch (ReflectionException ognle) {
+ ; // ignore
+ }
+ catch (IntrospectionException ie) {
+ ; // ignore
+ }
+ }
+
+ if (genericType instanceof ParameterizedType) {
+
+
+ ParameterizedType type = (ParameterizedType) genericType;
+
+ int index = (element && type.getRawType().toString().contains(Map.class.getName())) ? 1 : 0;
+
+ Type resultType = type.getActualTypeArguments()[index];
+
+ if ( resultType instanceof ParameterizedType) {
+ return (Class) ((ParameterizedType) resultType).getRawType();
+ }
+ return (Class) resultType;
+
+ }
+ } catch (Exception e) {
+ if ( LOG.isDebugEnabled()) {
+ LOG.debug("Error while retrieving generic property class for property=" + property, e);
+ }
+ }
+ return null;
+ }
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/DefaultTypeConverter.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/DefaultTypeConverter.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/DefaultTypeConverter.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/DefaultTypeConverter.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,328 @@
+//--------------------------------------------------------------------------
+// Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// Neither the name of the Drew Davidson nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+// DAMAGE.
+//--------------------------------------------------------------------------
+package org.apache.struts2.xwork2.conversion.impl;
+
+import org.apache.struts2.xwork2.conversion.TypeConverter;
+import org.apache.struts2.xwork2.ognl.XWorkTypeConverterWrapper;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Member;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Default type conversion. Converts among numeric types and also strings. Contains the basic
+ * type mapping code from OGNL.
+ *
+ * @author Luke Blanshard (blanshlu@netscape.net)
+ * @author Drew Davidson (drew@ognl.org)
+ */
+public class DefaultTypeConverter implements TypeConverter {
+ private static final String NULL_STRING = "null";
+
+ private final Map<Class, Object> primitiveDefaults;
+
+ public DefaultTypeConverter() {
+ Map<Class, Object> map = new HashMap<Class, Object>();
+ map.put(Boolean.TYPE, Boolean.FALSE);
+ map.put(Byte.TYPE, Byte.valueOf((byte) 0));
+ map.put(Short.TYPE, Short.valueOf((short) 0));
+ map.put(Character.TYPE, new Character((char) 0));
+ map.put(Integer.TYPE, Integer.valueOf(0));
+ map.put(Long.TYPE, Long.valueOf(0L));
+ map.put(Float.TYPE, new Float(0.0f));
+ map.put(Double.TYPE, new Double(0.0));
+ map.put(BigInteger.class, new BigInteger("0"));
+ map.put(BigDecimal.class, new BigDecimal(0.0));
+ primitiveDefaults = Collections.unmodifiableMap(map);
+ }
+
+ public Object convertValue(Map<String, Object> context, Object value, Class toType) {
+ return convertValue(value, toType);
+ }
+
+ public Object convertValue(Map<String, Object> context, Object target, Member member,
+ String propertyName, Object value, Class toType) {
+ return convertValue(context, value, toType);
+ }
+
+ public TypeConverter getTypeConverter( Map<String, Object> context )
+ {
+ Object obj = context.get(TypeConverter.TYPE_CONVERTER_CONTEXT_KEY);
+ if (obj instanceof TypeConverter) {
+ return (TypeConverter) obj;
+
+ // for backwards-compatibility
+ } else if (obj instanceof ognl.TypeConverter) {
+ return new XWorkTypeConverterWrapper((ognl.TypeConverter) obj);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the value converted numerically to the given class type
+ *
+ * This method also detects when arrays are being converted and converts the
+ * components of one array to the type of the other.
+ *
+ * @param value
+ * an object to be converted to the given type
+ * @param toType
+ * class type to be converted to
+ * @return converted value of the type given, or value if the value cannot
+ * be converted to the given type.
+ */
+ public Object convertValue(Object value, Class toType) {
+ Object result = null;
+
+ if (value != null) {
+ /* If array -> array then convert components of array individually */
+ if (value.getClass().isArray() && toType.isArray()) {
+ Class componentType = toType.getComponentType();
+
+ result = Array.newInstance(componentType, Array
+ .getLength(value));
+ for (int i = 0, icount = Array.getLength(value); i < icount; i++) {
+ Array.set(result, i, convertValue(Array.get(value, i),
+ componentType));
+ }
+ } else {
+ if ((toType == Integer.class) || (toType == Integer.TYPE))
+ result = Integer.valueOf((int) longValue(value));
+ if ((toType == Double.class) || (toType == Double.TYPE))
+ result = new Double(doubleValue(value));
+ if ((toType == Boolean.class) || (toType == Boolean.TYPE))
+ result = booleanValue(value) ? Boolean.TRUE : Boolean.FALSE;
+ if ((toType == Byte.class) || (toType == Byte.TYPE))
+ result = Byte.valueOf((byte) longValue(value));
+ if ((toType == Character.class) || (toType == Character.TYPE))
+ result = new Character((char) longValue(value));
+ if ((toType == Short.class) || (toType == Short.TYPE))
+ result = Short.valueOf((short) longValue(value));
+ if ((toType == Long.class) || (toType == Long.TYPE))
+ result = Long.valueOf(longValue(value));
+ if ((toType == Float.class) || (toType == Float.TYPE))
+ result = new Float(doubleValue(value));
+ if (toType == BigInteger.class)
+ result = bigIntValue(value);
+ if (toType == BigDecimal.class)
+ result = bigDecValue(value);
+ if (toType == String.class)
+ result = stringValue(value);
+ if (Enum.class.isAssignableFrom(toType))
+ result = enumValue((Class<Enum>)toType, value);
+ }
+ } else {
+ if (toType.isPrimitive()) {
+ result = primitiveDefaults.get(toType);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Evaluates the given object as a boolean: if it is a Boolean object, it's
+ * easy; if it's a Number or a Character, returns true for non-zero objects;
+ * and otherwise returns true for non-null objects.
+ *
+ * @param value
+ * an object to interpret as a boolean
+ * @return the boolean value implied by the given object
+ */
+ public static boolean booleanValue(Object value) {
+ if (value == null)
+ return false;
+ Class c = value.getClass();
+ if (c == Boolean.class)
+ return ((Boolean) value).booleanValue();
+ // if ( c == String.class )
+ // return ((String)value).length() > 0;
+ if (c == Character.class)
+ return ((Character) value).charValue() != 0;
+ if (value instanceof Number)
+ return ((Number) value).doubleValue() != 0;
+ return true; // non-null
+ }
+
+ public Enum<?> enumValue(Class toClass, Object o) {
+ Enum<?> result = null;
+ if (o == null) {
+ result = null;
+ } else if (o instanceof String[]) {
+ result = Enum.valueOf(toClass, ((String[]) o)[0]);
+ } else if (o instanceof String) {
+ result = Enum.valueOf(toClass, (String) o);
+ }
+ return result;
+ }
+
+ /**
+ * Evaluates the given object as a long integer.
+ *
+ * @param value
+ * an object to interpret as a long integer
+ * @return the long integer value implied by the given object
+ * @throws NumberFormatException
+ * if the given object can't be understood as a long integer
+ */
+ public static long longValue(Object value) throws NumberFormatException {
+ if (value == null)
+ return 0L;
+ Class c = value.getClass();
+ if (c.getSuperclass() == Number.class)
+ return ((Number) value).longValue();
+ if (c == Boolean.class)
+ return ((Boolean) value).booleanValue() ? 1 : 0;
+ if (c == Character.class)
+ return ((Character) value).charValue();
+ return Long.parseLong(stringValue(value, true));
+ }
+
+ /**
+ * Evaluates the given object as a double-precision floating-point number.
+ *
+ * @param value
+ * an object to interpret as a double
+ * @return the double value implied by the given object
+ * @throws NumberFormatException
+ * if the given object can't be understood as a double
+ */
+ public static double doubleValue(Object value) throws NumberFormatException {
+ if (value == null)
+ return 0.0;
+ Class c = value.getClass();
+ if (c.getSuperclass() == Number.class)
+ return ((Number) value).doubleValue();
+ if (c == Boolean.class)
+ return ((Boolean) value).booleanValue() ? 1 : 0;
+ if (c == Character.class)
+ return ((Character) value).charValue();
+ String s = stringValue(value, true);
+
+ return (s.length() == 0) ? 0.0 : Double.parseDouble(s);
+ /*
+ * For 1.1 parseDouble() is not available
+ */
+ // return Double.valueOf( value.toString() ).doubleValue();
+ }
+
+ /**
+ * Evaluates the given object as a BigInteger.
+ *
+ * @param value
+ * an object to interpret as a BigInteger
+ * @return the BigInteger value implied by the given object
+ * @throws NumberFormatException
+ * if the given object can't be understood as a BigInteger
+ */
+ public static BigInteger bigIntValue(Object value)
+ throws NumberFormatException {
+ if (value == null)
+ return BigInteger.valueOf(0L);
+ Class c = value.getClass();
+ if (c == BigInteger.class)
+ return (BigInteger) value;
+ if (c == BigDecimal.class)
+ return ((BigDecimal) value).toBigInteger();
+ if (c.getSuperclass() == Number.class)
+ return BigInteger.valueOf(((Number) value).longValue());
+ if (c == Boolean.class)
+ return BigInteger.valueOf(((Boolean) value).booleanValue() ? 1 : 0);
+ if (c == Character.class)
+ return BigInteger.valueOf(((Character) value).charValue());
+ return new BigInteger(stringValue(value, true));
+ }
+
+ /**
+ * Evaluates the given object as a BigDecimal.
+ *
+ * @param value
+ * an object to interpret as a BigDecimal
+ * @return the BigDecimal value implied by the given object
+ * @throws NumberFormatException
+ * if the given object can't be understood as a BigDecimal
+ */
+ public static BigDecimal bigDecValue(Object value)
+ throws NumberFormatException {
+ if (value == null)
+ return BigDecimal.valueOf(0L);
+ Class c = value.getClass();
+ if (c == BigDecimal.class)
+ return (BigDecimal) value;
+ if (c == BigInteger.class)
+ return new BigDecimal((BigInteger) value);
+ if (c.getSuperclass() == Number.class)
+ return new BigDecimal(((Number) value).doubleValue());
+ if (c == Boolean.class)
+ return BigDecimal.valueOf(((Boolean) value).booleanValue() ? 1 : 0);
+ if (c == Character.class)
+ return BigDecimal.valueOf(((Character) value).charValue());
+ return new BigDecimal(stringValue(value, true));
+ }
+
+ /**
+ * Evaluates the given object as a String and trims it if the trim flag is
+ * true.
+ *
+ * @param value
+ * an object to interpret as a String
+ * @return the String value implied by the given object as returned by the
+ * toString() method, or "null" if the object is null.
+ */
+ public static String stringValue(Object value, boolean trim) {
+ String result;
+
+ if (value == null) {
+ result = NULL_STRING;
+ } else {
+ result = value.toString();
+ if (trim) {
+ result = result.trim();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Evaluates the given object as a String.
+ *
+ * @param value
+ * an object to interpret as a String
+ * @return the String value implied by the given object as returned by the
+ * toString() method, or "null" if the object is null.
+ */
+ public static String stringValue(Object value) {
+ return stringValue(value, false);
+ }
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/EnumTypeConverter.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/EnumTypeConverter.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/EnumTypeConverter.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/EnumTypeConverter.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.struts2.xwork2.conversion.impl;
+
+import java.util.Map;
+
+
+/**
+ * <code>EnumTypeConverter</code>
+ *
+ * <!-- START SNIPPET: description -->
+ * This class converts java 5 enums to String and from String[] to enum.
+ * <p/>
+ * One of Java 5's improvements is providing enumeration facility.
+ * Up to now, there existed no enumerations. The only way to simulate was the so-called int Enum pattern:
+ * {code}
+ * public static final int SEASON_WINTER = 0;
+ * public static final int SEASON_SPRING = 1;
+ * public static final int SEASON_SUMMER = 2;
+ * public static final int SEASON_FALL = 3;
+ * {code}
+ * <p/>
+ * Java 5.0 now provides the following construct:
+ * {code}
+ * public static enum Season { WINTER, SPRING, SUMMER, FALL };
+ * {code}
+ * <!-- END SNIPPET: description -->
+ *
+ * <!-- START SNIPPET: example -->
+ * h3. Implementing Java 5 Enumeration Type Conversion
+ * <p/>
+ * 1. myAction-conversion.properties*
+ * <p/>
+ * Place a myAction-conversion.properties-file in the path of your Action.
+ * Add the following entry to the properties-file:
+ * {code}
+ * nameOfYourField=fullyClassifiedNameOfYourConverter
+ * {code}
+ *
+ * <p/>
+ * 2. myAction.java*
+ * Your action contains the _enumeration_:
+ * {code}
+ * public enum Criticality {DEBUG, INFO, WARNING, ERROR, FATAL}
+ * {code}
+ *
+ * * Your action contains the _private field_:
+ * {code}
+ * private myEnum myFieldForEnum;
+ * {code}
+ *
+ * Your action contains _getters and setters_ for your field:
+ * {code}
+ * public myEnum getCriticality() {
+ * return myFieldForEnum;
+ * }
+ *
+ * public void setCriticality(myEnum myFieldForEnum) {
+ * this.myFieldForEnum= myFieldForEnum;
+ * }
+ * {code}
+ * <p/>
+ * 3. JSP*
+ * <p/>
+ * In your jsp you can access an enumeration value just normal by using the known <ww:property>-Tag:
+ * {code}
+ * <ww:property value="myField"/>
+ * {code}
+ * <!-- END SNIPPET: example -->
+ *
+ * @author Tamara Cattivelli
+ * @author <a href="mailto:hermanns@aixcept.de">Rainer Hermanns</a>
+ * @version $Id: EnumTypeConverter.java 1209415 2011-12-02 11:24:48Z lukaszlenart $
+ * @deprecated Since Struts 2.1.0 as enum support is now built into XWork
+ */
+@Deprecated public class EnumTypeConverter extends DefaultTypeConverter {
+
+ /**
+ * Converts the given object to a given type. How this is to be done is implemented in toClass. The OGNL context, o
+ * and toClass are given. This method should be able to handle conversion in general without any context or object
+ * specified.
+ *
+ * @param context - OGNL context under which the conversion is being done
+ * @param o - the object to be converted
+ * @param toClass - the class that contains the code to convert to enumeration
+ * @return Converted value of type declared in toClass or TypeConverter.NoConversionPossible to indicate that the
+ * conversion was not possible.
+ */
+ @Override
+ public Object convertValue(Map<String, Object> context, Object o, Class toClass) {
+ if (o instanceof String[]) {
+ return convertFromString(((String[]) o)[0], toClass);
+ } else if (o instanceof String) {
+ return convertFromString((String) o, toClass);
+ }
+
+ return super.convertValue(context, o, toClass);
+ }
+
+ /**
+ * Converts one or more String values to the specified class.
+ * @param value - the String values to be converted, such as those submitted from an HTML form
+ * @param toClass - the class to convert to
+ * @return the converted object
+ */
+ public java.lang.Enum convertFromString(String value, Class toClass) {
+ return Enum.valueOf(toClass, value);
+ }
+
+}
\ No newline at end of file
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/GenericsObjectTypeDeterminer.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/GenericsObjectTypeDeterminer.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/GenericsObjectTypeDeterminer.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/GenericsObjectTypeDeterminer.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.struts2.xwork2.conversion.impl;
+
+import org.apache.struts2.xwork2.util.reflection.ReflectionProvider;
+
+
+/**
+ * GenericsObjectTypeDeterminer
+ *
+ * @author Patrick Lightbody
+ * @author Rainer Hermanns
+ * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
+ *
+ * @deprecated Use DefaultObjectTypeDeterminer instead. Since XWork 2.0.4 the DefaultObjectTypeDeterminer handles the
+ * annotation processing.
+ */
+@Deprecated public class GenericsObjectTypeDeterminer extends DefaultObjectTypeDeterminer {
+
+ public GenericsObjectTypeDeterminer(XWorkConverter conv,
+ XWorkBasicConverter basicConv, ReflectionProvider prov) {
+ super(conv, prov);
+ }
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/InstantiatingNullHandler.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/InstantiatingNullHandler.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/InstantiatingNullHandler.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/InstantiatingNullHandler.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts2.xwork2.conversion.impl;
+
+import org.apache.struts2.xwork2.ObjectFactory;
+import org.apache.struts2.xwork2.conversion.NullHandler;
+import org.apache.struts2.xwork2.conversion.ObjectTypeDeterminer;
+import org.apache.struts2.xwork2.inject.Inject;
+import org.apache.struts2.xwork2.util.logging.Logger;
+import org.apache.struts2.xwork2.util.logging.LoggerFactory;
+import org.apache.struts2.xwork2.util.reflection.ReflectionContextState;
+import org.apache.struts2.xwork2.util.reflection.ReflectionProvider;
+
+import java.beans.PropertyDescriptor;
+import java.util.*;
+
+
+/**
+ * <!-- START SNIPPET: javadoc -->
+ *
+ * Provided that the key {@link org.apache.struts2.xwork2.util.reflection.ReflectionContextState#CREATE_NULL_OBJECTS} is in the action context with a value of true (this key is set
+ * only during the execution of the {@link org.apache.struts2.xwork2.interceptor.ParametersInterceptor}), OGNL expressions
+ * that have caused a NullPointerException will be temporarily stopped for evaluation while the system automatically
+ * tries to solve the null references by automatically creating the object.
+ *
+ * <p/> The following rules are used when handling null references:
+ *
+ * <ul>
+ *
+ * <li>If the property is declared <i>exactly</i> as a {@link Collection} or {@link List}, then an ArrayList shall be
+ * returned and assigned to the null references.</li>
+ *
+ * <li>If the property is declared as a {@link Map}, then a HashMap will be returned and assigned to the null
+ * references.</li>
+ *
+ * <li>If the null property is a simple bean with a no-arg constructor, it will simply be created using the {@link
+ * ObjectFactory#buildBean(java.lang.Class, java.util.Map)} method.</li>
+ *
+ * </ul>
+ *
+ * <!-- END SNIPPET: javadoc -->
+ *
+ * <!-- START SNIPPET: example -->
+ *
+ * For example, if a form element has a text field named <b>person.name</b> and the expression <i>person</i> evaluates
+ * to null, then this class will be invoked. Because the <i>person</i> expression evaluates to a <i>Person</i> class, a
+ * new Person is created and assigned to the null reference. Finally, the name is set on that object and the overall
+ * effect is that the system automatically created a Person object for you, set it by calling setUsers() and then
+ * finally called getUsers().setName() as you would typically expect.
+ *
+ * <!-- END SNIPPET: example>
+ *
+ * @author Matt Ho
+ * @author Patrick Lightbody
+ */
+public class InstantiatingNullHandler implements NullHandler {
+
+ /**
+ * @deprecated Use {@link org.apache.struts2.xwork2.util.reflection.ReflectionContextState#CREATE_NULL_OBJECTS} instead
+ */
+ @Deprecated public static final String CREATE_NULL_OBJECTS = ReflectionContextState.CREATE_NULL_OBJECTS;
+ private static final Logger LOG = LoggerFactory.getLogger(InstantiatingNullHandler.class);
+ private ReflectionProvider reflectionProvider;
+ private ObjectFactory objectFactory;
+ private ObjectTypeDeterminer objectTypeDeterminer;
+
+ @Inject
+ public void setObjectTypeDeterminer(ObjectTypeDeterminer det) {
+ this.objectTypeDeterminer = det;
+ }
+
+ @Inject
+ public void setReflectionProvider(ReflectionProvider prov) {
+ this.reflectionProvider = prov;
+ }
+
+ @Inject
+ public void setObjectFactory(ObjectFactory fac) {
+ this.objectFactory = fac;
+ }
+
+ public Object nullMethodResult(Map<String, Object> context, Object target, String methodName, Object[] args) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Entering nullMethodResult ");
+ }
+
+ return null;
+ }
+
+ public Object nullPropertyValue(Map<String, Object> context, Object target, Object property) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Entering nullPropertyValue [target="+target+", property="+property+"]");
+ }
+
+ boolean c = ReflectionContextState.isCreatingNullObjects(context);
+
+ if (!c) {
+ return null;
+ }
+
+ if ((target == null) || (property == null)) {
+ return null;
+ }
+
+ try {
+ String propName = property.toString();
+ Object realTarget = reflectionProvider.getRealTarget(propName, context, target);
+ Class clazz = null;
+
+ if (realTarget != null) {
+ PropertyDescriptor pd = reflectionProvider.getPropertyDescriptor(realTarget.getClass(), propName);
+ if (pd == null) {
+ return null;
+ }
+
+ clazz = pd.getPropertyType();
+ }
+
+ if (clazz == null) {
+ // can't do much here!
+ return null;
+ }
+
+ Object param = createObject(clazz, realTarget, propName, context);
+
+ reflectionProvider.setValue(propName, context, realTarget, param);
+
+ return param;
+ } catch (Exception e) {
+ LOG.error("Could not create and/or set value back on to object", e);
+ }
+
+ return null;
+ }
+
+ private Object createObject(Class clazz, Object target, String property, Map<String, Object> context) throws Exception {
+ if (Collection.class.isAssignableFrom(clazz)) {
+ return new ArrayList();
+ } else if (clazz == Map.class) {
+ return new HashMap();
+ } else if (clazz == EnumMap.class) {
+ Class keyClass = objectTypeDeterminer.getKeyClass(target.getClass(), property);
+ return new EnumMap(keyClass);
+ }
+
+ return objectFactory.buildBean(clazz, context);
+ }
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/XWorkBasicConverter.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/XWorkBasicConverter.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/XWorkBasicConverter.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/conversion/impl/XWorkBasicConverter.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,583 @@
+/*
+ * Copyright 2002-2007,2009 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts2.xwork2.conversion.impl;
+
+import org.apache.struts2.xwork2.ActionContext;
+import org.apache.struts2.xwork2.ObjectFactory;
+import org.apache.struts2.xwork2.XWorkException;
+import org.apache.struts2.xwork2.conversion.ObjectTypeDeterminer;
+import org.apache.struts2.xwork2.conversion.TypeConverter;
+import org.apache.struts2.xwork2.inject.Inject;
+import org.apache.struts2.xwork2.util.XWorkList;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Member;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.*;
+import java.util.*;
+
+import org.apache.commons.lang.StringUtils;
+
+
+/**
+ * <!-- START SNIPPET: javadoc -->
+ * <p/>
+ * XWork will automatically handle the most common type conversion for you. This includes support for converting to
+ * and from Strings for each of the following:
+ * <p/>
+ * <ul>
+ * <li>String</li>
+ * <li>boolean / Boolean</li>
+ * <li>char / Character</li>
+ * <li>int / Integer, float / Float, long / Long, double / Double</li>
+ * <li>dates - uses the SHORT format for the Locale associated with the current request</li>
+ * <li>arrays - assuming the individual strings can be coverted to the individual items</li>
+ * <li>collections - if not object type can be determined, it is assumed to be a String and a new ArrayList is
+ * created</li>
+ * </ul>
+ * <p/> Note that with arrays the type conversion will defer to the type of the array elements and try to convert each
+ * item individually. As with any other type conversion, if the conversion can't be performed the standard type
+ * conversion error reporting is used to indicate a problem occured while processing the type conversion.
+ * <p/>
+ * <!-- END SNIPPET: javadoc -->
+ *
+ * @author <a href="mailto:plightbo@gmail.com">Pat Lightbody</a>
+ * @author Mike Mosiewicz
+ * @author Rainer Hermanns
+ * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
+ */
+public class XWorkBasicConverter extends DefaultTypeConverter {
+
+ private static String MILLISECOND_FORMAT = ".SSS";
+
+ private ObjectTypeDeterminer objectTypeDeterminer;
+ private XWorkConverter xworkConverter;
+ private ObjectFactory objectFactory;
+
+ @Inject
+ public void setObjectTypeDeterminer(ObjectTypeDeterminer det) {
+ this.objectTypeDeterminer = det;
+ }
+
+ @Inject
+ public void setXWorkConverter(XWorkConverter conv) {
+ this.xworkConverter = conv;
+ }
+
+ @Inject
+ public void setObjectFactory(ObjectFactory fac) {
+ this.objectFactory = fac;
+ }
+
+ @Override
+ public Object convertValue(Map<String, Object> context, Object o, Member member, String s, Object value, Class toType) {
+ Object result = null;
+
+ if (value == null || toType.isAssignableFrom(value.getClass())) {
+ // no need to convert at all, right?
+ return value;
+ }
+
+ if (toType == String.class) {
+ /* the code below has been disabled as it causes sideffects in Struts2 (XW-512)
+ // if input (value) is a number then use special conversion method (XW-490)
+ Class inputType = value.getClass();
+ if (Number.class.isAssignableFrom(inputType)) {
+ result = doConvertFromNumberToString(context, value, inputType);
+ if (result != null) {
+ return result;
+ }
+ }*/
+ // okay use default string conversion
+ result = doConvertToString(context, value);
+ } else if (toType == boolean.class) {
+ result = doConvertToBoolean(value);
+ } else if (toType == Boolean.class) {
+ result = doConvertToBoolean(value);
+ } else if (toType.isArray()) {
+ result = doConvertToArray(context, o, member, s, value, toType);
+ } else if (Date.class.isAssignableFrom(toType)) {
+ result = doConvertToDate(context, value, toType);
+ } else if (Calendar.class.isAssignableFrom(toType)) {
+ Date dateResult = (Date) doConvertToDate(context, value, Date.class);
+ if (dateResult != null) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(dateResult);
+ result = calendar;
+ }
+ } else if (Collection.class.isAssignableFrom(toType)) {
+ result = doConvertToCollection(context, o, member, s, value, toType);
+ } else if (toType == Character.class) {
+ result = doConvertToCharacter(value);
+ } else if (toType == char.class) {
+ result = doConvertToCharacter(value);
+ } else if (Number.class.isAssignableFrom(toType) || toType.isPrimitive()) {
+ result = doConvertToNumber(context, value, toType);
+ } else if (toType == Class.class) {
+ result = doConvertToClass(value);
+ }
+
+ if (result == null) {
+ if (value instanceof Object[]) {
+ Object[] array = (Object[]) value;
+
+ if (array.length >= 1) {
+ value = array[0];
+ } else {
+ value = null;
+ }
+
+ // let's try to convert the first element only
+ result = convertValue(context, o, member, s, value, toType);
+ } else if (!"".equals(value)) { // we've already tried the types we know
+ result = super.convertValue(context, value, toType);
+ }
+
+ if (result == null && value != null && !"".equals(value)) {
+ throw new XWorkException("Cannot create type " + toType + " from value " + value);
+ }
+ }
+
+ return result;
+ }
+
+ private Locale getLocale(Map<String, Object> context) {
+ if (context == null) {
+ return Locale.getDefault();
+ }
+
+ Locale locale = (Locale) context.get(ActionContext.LOCALE);
+
+ if (locale == null) {
+ locale = Locale.getDefault();
+ }
+
+ return locale;
+ }
+
+ /**
+ * Creates a Collection of the specified type.
+ *
+ * @param fromObject
+ * @param propertyName
+ * @param toType the type of Collection to create
+ * @param memberType the type of object elements in this collection must be
+ * @param size the initial size of the collection (ignored if 0 or less)
+ * @return a Collection of the specified type
+ */
+ private Collection createCollection(Object fromObject, String propertyName, Class toType, Class memberType, int size) {
+// try {
+// Object original = Ognl.getValue(OgnlUtil.compile(propertyName),fromObject);
+// if (original instanceof Collection) {
+// Collection coll = (Collection) original;
+// coll.clear();
+// return coll;
+// }
+// } catch (Exception e) {
+// // fail back to creating a new one
+// }
+
+ Collection result;
+
+ if (toType == Set.class) {
+ if (size > 0) {
+ result = new HashSet(size);
+ } else {
+ result = new HashSet();
+ }
+ } else if (toType == SortedSet.class) {
+ result = new TreeSet();
+ } else {
+ if (size > 0) {
+ result = new XWorkList(objectFactory, xworkConverter, memberType, size);
+ } else {
+ result = new XWorkList(objectFactory, xworkConverter, memberType);
+ }
+ }
+
+ return result;
+ }
+
+ private Object doConvertToArray(Map<String, Object> context, Object o, Member member, String s, Object value, Class toType) {
+ Object result = null;
+ Class componentType = toType.getComponentType();
+
+ if (componentType != null) {
+ TypeConverter converter = getTypeConverter(context);
+
+ if (value.getClass().isArray()) {
+ int length = Array.getLength(value);
+ result = Array.newInstance(componentType, length);
+
+ for (int i = 0; i < length; i++) {
+ Object valueItem = Array.get(value, i);
+ Array.set(result, i, converter.convertValue(context, o, member, s, valueItem, componentType));
+ }
+ } else {
+ result = Array.newInstance(componentType, 1);
+ Array.set(result, 0, converter.convertValue(context, o, member, s, value, componentType));
+ }
+ }
+
+ return result;
+ }
+
+ private Object doConvertToCharacter(Object value) {
+ if (value instanceof String) {
+ String cStr = (String) value;
+
+ return (cStr.length() > 0) ? new Character(cStr.charAt(0)) : null;
+ }
+
+ return null;
+ }
+
+ private Object doConvertToBoolean(Object value) {
+ if (value instanceof String) {
+ String bStr = (String) value;
+
+ return Boolean.valueOf(bStr);
+ }
+
+ return null;
+ }
+
+ private Class doConvertToClass(Object value) {
+ Class clazz = null;
+
+ if (value instanceof String && value != null && ((String) value).length() > 0) {
+ try {
+ clazz = Class.forName((String) value);
+ } catch (ClassNotFoundException e) {
+ throw new XWorkException(e.getLocalizedMessage(), e);
+ }
+ }
+
+ return clazz;
+ }
+
+ private Collection doConvertToCollection(Map<String, Object> context, Object o, Member member, String prop, Object value, Class toType) {
+ Collection result;
+ Class memberType = String.class;
+
+ if (o != null) {
+ //memberType = (Class) XWorkConverter.getInstance().getConverter(o.getClass(), XWorkConverter.CONVERSION_COLLECTION_PREFIX + prop);
+ memberType = objectTypeDeterminer.getElementClass(o.getClass(), prop, null);
+
+ if (memberType == null) {
+ memberType = String.class;
+ }
+ }
+
+ if (toType.isAssignableFrom(value.getClass())) {
+ // no need to do anything
+ result = (Collection) value;
+ } else if (value.getClass().isArray()) {
+ Object[] objArray = (Object[]) value;
+ TypeConverter converter = getTypeConverter(context);
+ result = createCollection(o, prop, toType, memberType, objArray.length);
+
+ for (Object anObjArray : objArray) {
+ result.add(converter.convertValue(context, o, member, prop, anObjArray, memberType));
+ }
+ } else if (Collection.class.isAssignableFrom(value.getClass())) {
+ Collection col = (Collection) value;
+ TypeConverter converter = getTypeConverter(context);
+ result = createCollection(o, prop, toType, memberType, col.size());
+
+ for (Object aCol : col) {
+ result.add(converter.convertValue(context, o, member, prop, aCol, memberType));
+ }
+ } else {
+ result = createCollection(o, prop, toType, memberType, -1);
+ result.add(value);
+ }
+
+ return result;
+ }
+
+ private Object doConvertToDate(Map<String, Object> context, Object value, Class toType) {
+ Date result = null;
+
+ if (value instanceof String && value != null && ((String) value).length() > 0) {
+ String sa = (String) value;
+ Locale locale = getLocale(context);
+
+ DateFormat df = null;
+ if (java.sql.Time.class == toType) {
+ df = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale);
+ } else if (java.sql.Timestamp.class == toType) {
+ Date check = null;
+ SimpleDateFormat dtfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT,
+ DateFormat.MEDIUM,
+ locale);
+ SimpleDateFormat fullfmt = new SimpleDateFormat(dtfmt.toPattern() + MILLISECOND_FORMAT,
+ locale);
+
+ SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT,
+ locale);
+
+ SimpleDateFormat[] fmts = {fullfmt, dtfmt, dfmt};
+ for (SimpleDateFormat fmt : fmts) {
+ try {
+ check = fmt.parse(sa);
+ df = fmt;
+ if (check != null) {
+ break;
+ }
+ } catch (ParseException ignore) {
+ }
+ }
+ } else if (java.util.Date.class == toType) {
+ Date check = null;
+ DateFormat[] dfs = getDateFormats(locale);
+ for (DateFormat df1 : dfs) {
+ try {
+ check = df1.parse(sa);
+ df = df1;
+ if (check != null) {
+ break;
+ }
+ }
+ catch (ParseException ignore) {
+ }
+ }
+ }
+ //final fallback for dates without time
+ if (df == null) {
+ df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
+ }
+ try {
+ df.setLenient(false); // let's use strict parsing (XW-341)
+ result = df.parse(sa);
+ if (!(Date.class == toType)) {
+ try {
+ Constructor constructor = toType.getConstructor(new Class[]{long.class});
+ return constructor.newInstance(new Object[]{Long.valueOf(result.getTime())});
+ } catch (Exception e) {
+ throw new XWorkException("Couldn't create class " + toType + " using default (long) constructor", e);
+ }
+ }
+ } catch (ParseException e) {
+ throw new XWorkException("Could not parse date", e);
+ }
+ } else if (Date.class.isAssignableFrom(value.getClass())) {
+ result = (Date) value;
+ }
+ return result;
+ }
+
+ private DateFormat[] getDateFormats(Locale locale) {
+ DateFormat dt1 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG, locale);
+ DateFormat dt2 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale);
+ DateFormat dt3 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale);
+
+ DateFormat d1 = DateFormat.getDateInstance(DateFormat.SHORT, locale);
+ DateFormat d2 = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
+ DateFormat d3 = DateFormat.getDateInstance(DateFormat.LONG, locale);
+
+ DateFormat rfc3399 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+
+ DateFormat[] dfs = {dt1, dt2, dt3, rfc3399, d1, d2, d3}; //added RFC 3339 date format (XW-473)
+ return dfs;
+ }
+
+ private Object doConvertToNumber(Map<String, Object> context, Object value, Class toType) {
+ if (value instanceof String) {
+ if (toType == BigDecimal.class) {
+ return new BigDecimal((String) value);
+ } else if (toType == BigInteger.class) {
+ return new BigInteger((String) value);
+ } else if (toType.isPrimitive()) {
+ Object convertedValue = super.convertValue(context, value, toType);
+ String stringValue = (String) value;
+ if (!isInRange((Number)convertedValue, stringValue, toType))
+ throw new XWorkException("Overflow or underflow casting: \"" + stringValue + "\" into class " + convertedValue.getClass().getName());
+
+ return convertedValue;
+ } else {
+ String stringValue = (String) value;
+ if (!toType.isPrimitive() && (stringValue == null || stringValue.length() == 0)) {
+ return null;
+ }
+ NumberFormat numFormat = NumberFormat.getInstance(getLocale(context));
+ ParsePosition parsePos = new ParsePosition(0);
+ if (isIntegerType(toType)) {
+ numFormat.setParseIntegerOnly(true);
+ }
+ numFormat.setGroupingUsed(true);
+ Number number = numFormat.parse(stringValue, parsePos);
+
+ if (parsePos.getIndex() != stringValue.length()) {
+ throw new XWorkException("Unparseable number: \"" + stringValue + "\" at position "
+ + parsePos.getIndex());
+ } else {
+ if (!isInRange(number, stringValue, toType))
+ throw new XWorkException("Overflow or underflow casting: \"" + stringValue + "\" into class " + number.getClass().getName());
+
+ value = super.convertValue(context, number, toType);
+ }
+ }
+ } else if (value instanceof Object[]) {
+ Object[] objArray = (Object[]) value;
+
+ if (objArray.length == 1) {
+ return doConvertToNumber(context, objArray[0], toType);
+ }
+ }
+
+ // pass it through DefaultTypeConverter
+ return super.convertValue(context, value, toType);
+ }
+
+ protected boolean isInRange(Number value, String stringValue, Class toType) {
+ Number bigValue = null;
+ Number lowerBound = null;
+ Number upperBound = null;
+
+ try {
+ if (double.class == toType || Double.class == toType) {
+ bigValue = new BigDecimal(stringValue);
+ // Double.MIN_VALUE is the smallest positive non-zero number
+ lowerBound = BigDecimal.valueOf(Double.MAX_VALUE).negate();
+ upperBound = BigDecimal.valueOf(Double.MAX_VALUE);
+ } else if (float.class == toType || Float.class == toType) {
+ bigValue = new BigDecimal(stringValue);
+ // Float.MIN_VALUE is the smallest positive non-zero number
+ lowerBound = BigDecimal.valueOf(Float.MAX_VALUE).negate();
+ upperBound = BigDecimal.valueOf(Float.MAX_VALUE);
+ } else if (byte.class == toType || Byte.class == toType) {
+ bigValue = new BigInteger(stringValue);
+ lowerBound = BigInteger.valueOf(Byte.MIN_VALUE);
+ upperBound = BigInteger.valueOf(Byte.MAX_VALUE);
+ } else if (char.class == toType || Character.class == toType) {
+ bigValue = new BigInteger(stringValue);
+ lowerBound = BigInteger.valueOf(Character.MIN_VALUE);
+ upperBound = BigInteger.valueOf(Character.MAX_VALUE);
+ } else if (short.class == toType || Short.class == toType) {
+ bigValue = new BigInteger(stringValue);
+ lowerBound = BigInteger.valueOf(Short.MIN_VALUE);
+ upperBound = BigInteger.valueOf(Short.MAX_VALUE);
+ } else if (int.class == toType || Integer.class == toType) {
+ bigValue = new BigInteger(stringValue);
+ lowerBound = BigInteger.valueOf(Integer.MIN_VALUE);
+ upperBound = BigInteger.valueOf(Integer.MAX_VALUE);
+ } else if (long.class == toType || Long.class == toType) {
+ bigValue = new BigInteger(stringValue);
+ lowerBound = BigInteger.valueOf(Long.MIN_VALUE);
+ upperBound = BigInteger.valueOf(Long.MAX_VALUE);
+ }
+ } catch (NumberFormatException e) {
+ //shoult it fail here? BigInteger doesnt seem to be so nice parsing numbers as NumberFormat
+ return true;
+ }
+
+ return ((Comparable)bigValue).compareTo(lowerBound) >= 0 && ((Comparable)bigValue).compareTo(upperBound) <= 0;
+ }
+
+ protected boolean isIntegerType(Class type) {
+ if (double.class == type || float.class == type || Double.class == type || Float.class == type
+ || char.class == type || Character.class == type) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Converts the input as a number using java's number formatter to a string output.
+ */
+ private String doConvertFromNumberToString(Map<String, Object> context, Object value, Class toType) {
+ // XW-409: If the input is a Number we should format it to a string using the choosen locale and use java's numberformatter
+ if (Number.class.isAssignableFrom(toType)) {
+ NumberFormat numFormat = NumberFormat.getInstance(getLocale(context));
+ if (isIntegerType(toType)) {
+ numFormat.setParseIntegerOnly(true);
+ }
+ numFormat.setGroupingUsed(true);
+ numFormat.setMaximumFractionDigits(99); // to be sure we include all digits after decimal seperator, otherwise some of the fractions can be chopped
+
+ String number = numFormat.format(value);
+ if (number != null) {
+ return number;
+ }
+ }
+
+ return null; // no number
+ }
+
+
+ private String doConvertToString(Map<String, Object> context, Object value) {
+ String result = null;
+
+ if (value instanceof int[]) {
+ int[] x = (int[]) value;
+ List<Integer> intArray = new ArrayList<Integer>(x.length);
+
+ for (int aX : x) {
+ intArray.add(Integer.valueOf(aX));
+ }
+
+ result = StringUtils.join(intArray, ", ");
+ } else if (value instanceof long[]) {
+ long[] x = (long[]) value;
+ List<Long> longArray = new ArrayList<Long>(x.length);
+
+ for (long aX : x) {
+ longArray.add(Long.valueOf(aX));
+ }
+
+ result = StringUtils.join(longArray, ", ");
+ } else if (value instanceof double[]) {
+ double[] x = (double[]) value;
+ List<Double> doubleArray = new ArrayList<Double>(x.length);
+
+ for (double aX : x) {
+ doubleArray.add(new Double(aX));
+ }
+
+ result = StringUtils.join(doubleArray, ", ");
+ } else if (value instanceof boolean[]) {
+ boolean[] x = (boolean[]) value;
+ List<Boolean> booleanArray = new ArrayList<Boolean>(x.length);
+
+ for (boolean aX : x) {
+ booleanArray.add(new Boolean(aX));
+ }
+
+ result = StringUtils.join(booleanArray, ", ");
+ } else if (value instanceof Date) {
+ DateFormat df = null;
+ if (value instanceof java.sql.Time) {
+ df = DateFormat.getTimeInstance(DateFormat.MEDIUM, getLocale(context));
+ } else if (value instanceof java.sql.Timestamp) {
+ SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT,
+ DateFormat.MEDIUM,
+ getLocale(context));
+ df = new SimpleDateFormat(dfmt.toPattern() + MILLISECOND_FORMAT);
+ } else {
+ df = DateFormat.getDateInstance(DateFormat.SHORT, getLocale(context));
+ }
+ result = df.format(value);
+ } else if (value instanceof String[]) {
+ result = StringUtils.join((String[]) value, ", ");
+ }
+
+ return result;
+ }
+}