mirror of
https://github.com/apache/struts.git
synced 2026-08-07 23:57:03 +00:00
WW-3871 refactors XWorkConverter to separate logic a bit
git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@1437180 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -246,4 +246,10 @@ public final class StrutsConstants {
|
||||
/** Enable handling exceptions by Dispatcher - true by default **/
|
||||
public static final String STRUTS_HANDLE_EXCEPTION = "struts.handle.exception";
|
||||
|
||||
public static final String STRUTS_CONVERTER_PROPERTIES_PROCESSOR = "struts.converter.properties.processor";
|
||||
public static final String STRUTS_CONVERTER_FILE_PROCESSOR = "struts.converter.file.processor";
|
||||
public static final String STRUTS_CONVERTER_ANNOTATION_PROCESSOR = "struts.converter.annotation.processor";
|
||||
public static final String STRUTS_CONVERTER_CREATOR = "struts.converter.creator";
|
||||
public static final String STRUTS_CONVERTER_HOLDER = "struts..converter.holder";
|
||||
|
||||
}
|
||||
|
||||
@@ -32,7 +32,12 @@ import com.opensymphony.xwork2.XWorkConstants;
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.ConfigurationProvider;
|
||||
import com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor;
|
||||
import com.opensymphony.xwork2.conversion.ConversionFileProcessor;
|
||||
import com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor;
|
||||
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterCreator;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterHolder;
|
||||
import com.opensymphony.xwork2.conversion.impl.ArrayConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.CollectionConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.DateConverter;
|
||||
@@ -241,6 +246,36 @@ import java.util.StringTokenizer;
|
||||
* <td>singleton</td>
|
||||
* <td>Converter used to convert any object to String and back</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor</td>
|
||||
* <td>struts.conversion.properties.processor</td>
|
||||
* <td>singleton</td>
|
||||
* <td>Process Properties to create converters</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor</td>
|
||||
* <td>struts.converter.file.processor</td>
|
||||
* <td>singleton</td>
|
||||
* <td>Process <class>-conversion.properties file create converters</class></td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor</td>
|
||||
* <td>struts.converter.annotation.processor</td>
|
||||
* <td>singleton</td>
|
||||
* <td>Process TypeConversion annotation to create converters</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>com.opensymphony.xwork2.conversion.TypeConverterCreator</td>
|
||||
* <td>struts.converter.creator</td>
|
||||
* <td>singleton</td>
|
||||
* <td>Creates user converters</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>com.opensymphony.xwork2.conversion.TypeConverterHolder</td>
|
||||
* <td>struts.converter.holder</td>
|
||||
* <td>singleton</td>
|
||||
* <td>Holds user converters' instances</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* <!-- END SNIPPET: extensionPoints -->
|
||||
@@ -287,8 +322,22 @@ public class BeanSelectionProvider implements ConfigurationProvider {
|
||||
public void register(ContainerBuilder builder, LocatableProperties props) {
|
||||
alias(ObjectFactory.class, StrutsConstants.STRUTS_OBJECTFACTORY, builder, props);
|
||||
alias(FileManagerFactory.class, StrutsConstants.STRUTS_FILE_MANAGER_FACTORY, builder, props, Scope.SINGLETON);
|
||||
|
||||
alias(XWorkConverter.class, StrutsConstants.STRUTS_XWORKCONVERTER, builder, props);
|
||||
alias(CollectionConverter.class, StrutsConstants.STRUTS_CONVERTER_COLLECTION, builder, props);
|
||||
alias(ArrayConverter.class, StrutsConstants.STRUTS_CONVERTER_ARRAY, builder, props);
|
||||
alias(DateConverter.class, StrutsConstants.STRUTS_CONVERTER_DATE, builder, props);
|
||||
alias(NumberConverter.class, StrutsConstants.STRUTS_CONVERTER_NUMBER, builder, props);
|
||||
alias(StringConverter.class, StrutsConstants.STRUTS_CONVERTER_STRING, builder, props);
|
||||
|
||||
alias(ConversionPropertiesProcessor.class, StrutsConstants.STRUTS_CONVERTER_PROPERTIES_PROCESSOR, builder, props);
|
||||
alias(ConversionFileProcessor.class, StrutsConstants.STRUTS_CONVERTER_FILE_PROCESSOR, builder, props);
|
||||
alias(ConversionAnnotationProcessor.class, StrutsConstants.STRUTS_CONVERTER_ANNOTATION_PROCESSOR, builder, props);
|
||||
alias(TypeConverterCreator.class, StrutsConstants.STRUTS_CONVERTER_CREATOR, builder, props);
|
||||
alias(TypeConverterHolder.class, StrutsConstants.STRUTS_CONVERTER_HOLDER, builder, props);
|
||||
|
||||
alias(TextProvider.class, StrutsConstants.STRUTS_XWORKTEXTPROVIDER, builder, props, Scope.DEFAULT);
|
||||
|
||||
alias(LocaleProvider.class, StrutsConstants.STRUTS_LOCALE_PROVIDER, builder, props);
|
||||
alias(ActionProxyFactory.class, StrutsConstants.STRUTS_ACTIONPROXYFACTORY, builder, props);
|
||||
alias(ObjectTypeDeterminer.class, StrutsConstants.STRUTS_OBJECTTYPEDETERMINER, builder, props);
|
||||
@@ -306,12 +355,6 @@ public class BeanSelectionProvider implements ConfigurationProvider {
|
||||
alias(UnknownHandlerManager.class, StrutsConstants.STRUTS_UNKNOWN_HANDLER_MANAGER, builder, props);
|
||||
alias(UrlHelper.class, StrutsConstants.STRUTS_URL_HELPER, builder, props);
|
||||
|
||||
alias(CollectionConverter.class, StrutsConstants.STRUTS_CONVERTER_COLLECTION, builder, props);
|
||||
alias(ArrayConverter.class, StrutsConstants.STRUTS_CONVERTER_ARRAY, builder, props);
|
||||
alias(DateConverter.class, StrutsConstants.STRUTS_CONVERTER_DATE, builder, props);
|
||||
alias(NumberConverter.class, StrutsConstants.STRUTS_CONVERTER_NUMBER, builder, props);
|
||||
alias(StringConverter.class, StrutsConstants.STRUTS_CONVERTER_STRING, builder, props);
|
||||
|
||||
if ("true".equalsIgnoreCase(props.getProperty(StrutsConstants.STRUTS_DEVMODE))) {
|
||||
props.setProperty(StrutsConstants.STRUTS_I18N_RELOAD, "true");
|
||||
props.setProperty(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, "true");
|
||||
|
||||
@@ -65,6 +65,12 @@
|
||||
<bean type="org.apache.struts2.components.template.TemplateEngine" name="jsp" class="org.apache.struts2.components.template.JspTemplateEngine" />
|
||||
|
||||
<bean type="com.opensymphony.xwork2.conversion.impl.XWorkConverter" name="struts" class="com.opensymphony.xwork2.conversion.impl.XWorkConverter" />
|
||||
<bean type="com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor" name="struts" class="com.opensymphony.xwork2.conversion.impl.DefaultConversionPropertiesProcessor" />
|
||||
<bean type="com.opensymphony.xwork2.conversion.ConversionFileProcessor" name="struts" class="com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor" />
|
||||
<bean type="com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor" name="struts" class="com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor" />
|
||||
<bean type="com.opensymphony.xwork2.conversion.TypeConverterCreator" name="struts" class="com.opensymphony.xwork2.conversion.impl.DefaultTypeConverterCreator" />
|
||||
<bean type="com.opensymphony.xwork2.conversion.TypeConverterHolder" name="struts" class="com.opensymphony.xwork2.conversion.impl.DefaultTypeConverterHolder" />
|
||||
|
||||
<bean class="com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter" />
|
||||
|
||||
<bean type="com.opensymphony.xwork2.conversion.impl.CollectionConverter" name="struts" class="com.opensymphony.xwork2.conversion.impl.CollectionConverter" scope="singleton"/>
|
||||
|
||||
+17
@@ -37,12 +37,22 @@ import com.opensymphony.xwork2.config.entities.ResultConfig;
|
||||
import com.opensymphony.xwork2.config.entities.ResultTypeConfig;
|
||||
import com.opensymphony.xwork2.config.entities.UnknownHandlerConfig;
|
||||
import com.opensymphony.xwork2.config.providers.InterceptorBuilder;
|
||||
import com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor;
|
||||
import com.opensymphony.xwork2.conversion.ConversionFileProcessor;
|
||||
import com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor;
|
||||
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterCreator;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterHolder;
|
||||
import com.opensymphony.xwork2.conversion.impl.ArrayConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.CollectionConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.DateConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultConversionPropertiesProcessor;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverterCreator;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverterHolder;
|
||||
import com.opensymphony.xwork2.conversion.impl.NumberConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.StringConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter;
|
||||
@@ -289,7 +299,14 @@ public class DefaultConfiguration implements Configuration {
|
||||
}
|
||||
builder.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON);
|
||||
builder.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON);
|
||||
|
||||
builder.factory(XWorkConverter.class, Scope.SINGLETON);
|
||||
builder.factory(ConversionPropertiesProcessor.class, DefaultConversionPropertiesProcessor.class, Scope.SINGLETON);
|
||||
builder.factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON);
|
||||
builder.factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON);
|
||||
builder.factory(TypeConverterCreator.class, DefaultTypeConverterCreator.class, Scope.SINGLETON);
|
||||
builder.factory(TypeConverterHolder.class, DefaultTypeConverterHolder.class, Scope.SINGLETON);
|
||||
|
||||
builder.factory(XWorkBasicConverter.class, Scope.SINGLETON);
|
||||
builder.factory(TypeConverter.class, XWorkConstants.COLLECTION_CONVERTER, CollectionConverter.class, Scope.SINGLETON);
|
||||
builder.factory(TypeConverter.class, XWorkConstants.ARRAY_CONVERTER, ArrayConverter.class, Scope.SINGLETON);
|
||||
|
||||
+20
-3
@@ -15,12 +15,22 @@ import com.opensymphony.xwork2.XWorkConstants;
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.ConfigurationProvider;
|
||||
import com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor;
|
||||
import com.opensymphony.xwork2.conversion.ConversionFileProcessor;
|
||||
import com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor;
|
||||
import com.opensymphony.xwork2.conversion.NullHandler;
|
||||
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterCreator;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterHolder;
|
||||
import com.opensymphony.xwork2.conversion.impl.ArrayConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.CollectionConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.DateConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultConversionPropertiesProcessor;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverterCreator;
|
||||
import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverterHolder;
|
||||
import com.opensymphony.xwork2.conversion.impl.InstantiatingNullHandler;
|
||||
import com.opensymphony.xwork2.conversion.impl.NumberConverter;
|
||||
import com.opensymphony.xwork2.conversion.impl.StringConverter;
|
||||
@@ -92,7 +102,15 @@ public class XWorkConfigurationProvider implements ConfigurationProvider {
|
||||
builder.factory(com.opensymphony.xwork2.ObjectFactory.class)
|
||||
.factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
|
||||
.factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
|
||||
|
||||
.factory(XWorkConverter.class, Scope.SINGLETON)
|
||||
.factory(XWorkBasicConverter.class, Scope.SINGLETON)
|
||||
.factory(ConversionPropertiesProcessor.class, DefaultConversionPropertiesProcessor.class, Scope.SINGLETON)
|
||||
.factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON)
|
||||
.factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON)
|
||||
.factory(TypeConverterCreator.class, DefaultTypeConverterCreator.class, Scope.SINGLETON)
|
||||
.factory(TypeConverterHolder.class, DefaultTypeConverterHolder.class, Scope.SINGLETON)
|
||||
|
||||
.factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
|
||||
.factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON)
|
||||
.factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
|
||||
@@ -107,18 +125,18 @@ public class XWorkConfigurationProvider implements ConfigurationProvider {
|
||||
.factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
|
||||
.factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
|
||||
|
||||
// silly workarounds for ognl since there is no way to flush its caches
|
||||
// silly workarounds for ognl since there is no way to flush its caches
|
||||
.factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, ArrayList.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, HashSet.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Set.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, HashMap.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, Map.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
|
||||
|
||||
.factory(PropertyAccessor.class, Collection.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(PropertyAccessor.class, ObjectProxy.class.getName(), ObjectProxyPropertyAccessor.class, Scope.SINGLETON)
|
||||
.factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
|
||||
.factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
|
||||
|
||||
.factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
|
||||
.factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
|
||||
.factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
|
||||
@@ -126,7 +144,6 @@ public class XWorkConfigurationProvider implements ConfigurationProvider {
|
||||
.factory(TextProvider.class, TextProviderSupport.class, Scope.SINGLETON)
|
||||
.factory(LocaleProvider.class, DefaultLocaleProvider.class, Scope.SINGLETON)
|
||||
.factory(OgnlUtil.class, Scope.SINGLETON)
|
||||
.factory(XWorkBasicConverter.class, Scope.SINGLETON)
|
||||
.factory(CollectionConverter.class, Scope.SINGLETON)
|
||||
.factory(ArrayConverter.class, Scope.SINGLETON)
|
||||
.factory(DateConverter.class, Scope.SINGLETON)
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.opensymphony.xwork2.conversion;
|
||||
|
||||
import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Used to process {@link com.opensymphony.xwork2.conversion.annotations.TypeConversion}
|
||||
* annotation to read defined Converters
|
||||
*/
|
||||
public interface ConversionAnnotationProcessor {
|
||||
|
||||
/**
|
||||
* Process annotation and build {@link TypeConverter} base on provided annotation
|
||||
* and assigning it under given key
|
||||
*
|
||||
* @param mapping keeps converters per given key
|
||||
* @param tc annotation which keeps information about converter
|
||||
* @param key key under which converter should be registered
|
||||
*/
|
||||
void process(Map<String, Object> mapping, TypeConversion tc, String key);
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.opensymphony.xwork2.conversion;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Used to process <clazz>-conversion.properties file to read defined Converters
|
||||
*/
|
||||
public interface ConversionFileProcessor {
|
||||
|
||||
/**
|
||||
* Process conversion file to create mapping for key (property, type) and corresponding converter
|
||||
*
|
||||
* @param mapping keeps converters per given key
|
||||
* @param clazz class which should be converted by the converter
|
||||
* @param converterFilename to read converters from
|
||||
*/
|
||||
void process(Map<String, Object> mapping, Class clazz, String converterFilename);
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.opensymphony.xwork2.conversion;
|
||||
|
||||
/**
|
||||
* Used to read converters from Properties file
|
||||
*/
|
||||
public interface ConversionPropertiesProcessor {
|
||||
|
||||
/**
|
||||
* Process given property to load converters as not required (Properties file doesn't have to exist)
|
||||
*
|
||||
* @param propsName Properties file name
|
||||
*/
|
||||
void process(String propsName);
|
||||
|
||||
/**
|
||||
* Process given property to load converters as required (Properties file must exist)
|
||||
*
|
||||
* @param propsName Properties file name
|
||||
*/
|
||||
void processRequired(String propsName);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.opensymphony.xwork2.conversion;
|
||||
|
||||
/**
|
||||
* Instantiate converter classes, if cannot create TypeConverter throws exception
|
||||
*/
|
||||
public interface TypeConverterCreator {
|
||||
|
||||
/**
|
||||
* Creates {@link TypeConverter} from given class
|
||||
*
|
||||
* @param className convert class
|
||||
* @return instance of {@link TypeConverter}
|
||||
* @throws Exception when cannot create/cast to {@link TypeConverter}
|
||||
*/
|
||||
TypeConverter createTypeConverter(String className) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.opensymphony.xwork2.conversion;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Holds all mappings related to {@link TypeConverter}s
|
||||
*/
|
||||
public interface TypeConverterHolder {
|
||||
|
||||
/**
|
||||
* Adds mapping for default type converters - application scoped
|
||||
*
|
||||
* @param className name of the class with associated converter
|
||||
* @param typeConverter {@link TypeConverter} instance for associated class
|
||||
*/
|
||||
void addDefaultMapping(String className, TypeConverter typeConverter);
|
||||
|
||||
/**
|
||||
* Checks if converter was already defined for given class
|
||||
*
|
||||
* @param className name of the class to check for
|
||||
* @return true if default mapping was already specified
|
||||
*/
|
||||
boolean containsDefaultMapping(String className);
|
||||
|
||||
/**
|
||||
* Returns instance of {@link TypeConverter} associated with given class
|
||||
*
|
||||
* @param className name of the class to return converter for
|
||||
* @return instance of {@link TypeConverter} to be used to convert class
|
||||
*/
|
||||
TypeConverter getDefaultMapping(String className);
|
||||
|
||||
/**
|
||||
* Target class conversion Mappings.
|
||||
*
|
||||
* @param clazz class to convert to/from
|
||||
* @return {@link TypeConverter} for given class
|
||||
*/
|
||||
Map<String, Object> getMapping(Class clazz);
|
||||
|
||||
/**
|
||||
* Assign mapping of converters for given class
|
||||
*
|
||||
* @param clazz class to convert to/from
|
||||
* @param mapping property converters
|
||||
*/
|
||||
void addMapping(Class clazz, Map<String, Object> mapping);
|
||||
|
||||
/**
|
||||
* Check if there is no mapping for given class to convert
|
||||
*
|
||||
* @param clazz class to convert to/from
|
||||
* @return true if mapping couldn't be found
|
||||
*/
|
||||
boolean containsNoMapping(Class clazz);
|
||||
|
||||
/**
|
||||
* Adds no mapping flag for give class
|
||||
*
|
||||
* @param clazz class to register missing converter
|
||||
*/
|
||||
void addNoMapping(Class clazz);
|
||||
|
||||
/**
|
||||
* Checks if no mapping was defined for given class name
|
||||
* FIXME lukaszlenart: maybe it should be merged with NoMapping
|
||||
*
|
||||
* @param className name of the class to check for
|
||||
* @return true if converter was defined for given class name
|
||||
*/
|
||||
boolean containsUnknownMapping(String className);
|
||||
|
||||
/**
|
||||
* Adds no converter flag for given class name
|
||||
* FIXME lukaszlenart: maybe it should be merged with NoMapping
|
||||
*
|
||||
* @param className name of the class to mark there is no converter for it
|
||||
*/
|
||||
void addUnknownMapping(String className);
|
||||
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterCreator;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterHolder;
|
||||
import com.opensymphony.xwork2.conversion.annotations.ConversionRule;
|
||||
import com.opensymphony.xwork2.conversion.annotations.ConversionType;
|
||||
import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ConversionAnnotationProcessor}
|
||||
*/
|
||||
public class DefaultConversionAnnotationProcessor implements ConversionAnnotationProcessor {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultConversionAnnotationProcessor.class);
|
||||
|
||||
private TypeConverterCreator converterCreator;
|
||||
private TypeConverterHolder converterHolder;
|
||||
|
||||
@Inject
|
||||
public void setTypeConverterCreator(TypeConverterCreator converterCreator) {
|
||||
this.converterCreator = converterCreator;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setTypeConverterHolder(TypeConverterHolder converterHolder) {
|
||||
this.converterHolder = converterHolder;
|
||||
}
|
||||
|
||||
public void process(Map<String, Object> mapping, TypeConversion tc, String key) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("TypeConversion [#0] with key: [#1]", tc.converter(), key);
|
||||
}
|
||||
if (key == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (tc.type() == ConversionType.APPLICATION) {
|
||||
converterHolder.addDefaultMapping(key, converterCreator.createTypeConverter(tc.converter()));
|
||||
} else {
|
||||
if (tc.rule() == ConversionRule.KEY_PROPERTY || tc.rule() == ConversionRule.CREATE_IF_NULL) {
|
||||
mapping.put(key, tc.value());
|
||||
}
|
||||
//for properties of classes
|
||||
else if (tc.rule() != ConversionRule.ELEMENT || tc.rule() == ConversionRule.KEY || tc.rule() == ConversionRule.COLLECTION) {
|
||||
mapping.put(key, converterCreator.createTypeConverter(tc.converter()));
|
||||
}
|
||||
//for keys of Maps
|
||||
else if (tc.rule() == ConversionRule.KEY) {
|
||||
Class converterClass = Thread.currentThread().getContextClassLoader().loadClass(tc.converter());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Converter class: [#0]", converterClass);
|
||||
}
|
||||
//check if the converter is a type converter if it is one
|
||||
//then just put it in the map as is. Otherwise
|
||||
//put a value in for the type converter of the class
|
||||
if (converterClass.isAssignableFrom(TypeConverter.class)) {
|
||||
mapping.put(key, converterCreator.createTypeConverter(tc.converter()));
|
||||
} else {
|
||||
mapping.put(key, converterClass);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Object placed in mapping for key [#0] is [#1]", key, mapping.get(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
//elements(values) of maps / lists
|
||||
else {
|
||||
mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass(tc.converter()));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Got exception for #0", e, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import com.opensymphony.xwork2.FileManager;
|
||||
import com.opensymphony.xwork2.FileManagerFactory;
|
||||
import com.opensymphony.xwork2.conversion.ConversionFileProcessor;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterCreator;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.ClassLoaderUtil;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ConversionFileProcessor}
|
||||
*/
|
||||
public class DefaultConversionFileProcessor implements ConversionFileProcessor {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultConversionFileProcessor.class);
|
||||
|
||||
private FileManager fileManager;
|
||||
private TypeConverterCreator converterCreator;
|
||||
|
||||
@Inject
|
||||
public void setFileManagerFactory(FileManagerFactory factory) {
|
||||
fileManager = factory.getFileManager();
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setTypeConverterCreator(TypeConverterCreator converterCreator) {
|
||||
this.converterCreator = converterCreator;
|
||||
}
|
||||
|
||||
public void process(Map<String, Object> mapping, Class clazz, String converterFilename) {
|
||||
try {
|
||||
InputStream is = fileManager.loadFile(ClassLoaderUtil.getResource(converterFilename, clazz));
|
||||
|
||||
if (is != null) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Processing conversion file [#0] for class [#1]", converterFilename, clazz);
|
||||
}
|
||||
|
||||
Properties prop = new Properties();
|
||||
prop.load(is);
|
||||
|
||||
for (Map.Entry<Object, Object> entry : prop.entrySet()) {
|
||||
String key = (String) entry.getKey();
|
||||
|
||||
if (mapping.containsKey(key)) {
|
||||
break;
|
||||
}
|
||||
// for keyProperty of Set
|
||||
if (key.startsWith(DefaultObjectTypeDeterminer.KEY_PROPERTY_PREFIX)
|
||||
|| key.startsWith(DefaultObjectTypeDeterminer.CREATE_IF_NULL_PREFIX)) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as String]");
|
||||
}
|
||||
mapping.put(key, entry.getValue());
|
||||
}
|
||||
//for properties of classes
|
||||
else if (!(key.startsWith(DefaultObjectTypeDeterminer.ELEMENT_PREFIX) ||
|
||||
key.startsWith(DefaultObjectTypeDeterminer.KEY_PREFIX) ||
|
||||
key.startsWith(DefaultObjectTypeDeterminer.DEPRECATED_ELEMENT_PREFIX))
|
||||
) {
|
||||
TypeConverter _typeConverter = converterCreator.createTypeConverter((String) entry.getValue());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as TypeConverter " + _typeConverter + "]");
|
||||
}
|
||||
mapping.put(key, _typeConverter);
|
||||
}
|
||||
//for keys of Maps
|
||||
else if (key.startsWith(DefaultObjectTypeDeterminer.KEY_PREFIX)) {
|
||||
|
||||
Class converterClass = Thread.currentThread().getContextClassLoader().loadClass((String) entry.getValue());
|
||||
|
||||
//check if the converter is a type converter if it is one
|
||||
//then just put it in the map as is. Otherwise
|
||||
//put a value in for the type converter of the class
|
||||
if (converterClass.isAssignableFrom(TypeConverter.class)) {
|
||||
TypeConverter _typeConverter = converterCreator.createTypeConverter((String) entry.getValue());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as TypeConverter " + _typeConverter + "]");
|
||||
}
|
||||
mapping.put(key, _typeConverter);
|
||||
} else {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as Class " + converterClass + "]");
|
||||
}
|
||||
mapping.put(key, converterClass);
|
||||
}
|
||||
}
|
||||
//elements(values) of maps / lists
|
||||
else {
|
||||
Class _c = Thread.currentThread().getContextClassLoader().loadClass((String) entry.getValue());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as Class " + _c + "]");
|
||||
}
|
||||
mapping.put(key, _c);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
if (LOG.isErrorEnabled()) {
|
||||
LOG.error("Problem loading properties for #0", ex, clazz.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import com.opensymphony.xwork2.XWorkException;
|
||||
import com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterCreator;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterHolder;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.ClassLoaderUtil;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* TODO lukaszlenart: add a comment
|
||||
*/
|
||||
public class DefaultConversionPropertiesProcessor implements ConversionPropertiesProcessor {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultConversionPropertiesProcessor.class);
|
||||
|
||||
private TypeConverterCreator converterCreator;
|
||||
private TypeConverterHolder converterHolder;
|
||||
|
||||
@Inject
|
||||
public void setTypeConverterCreator(TypeConverterCreator converterCreator) {
|
||||
this.converterCreator = converterCreator;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setTypeConverterHolder(TypeConverterHolder converterHolder) {
|
||||
this.converterHolder = converterHolder;
|
||||
}
|
||||
|
||||
public void process(String propsName) {
|
||||
loadConversionProperties(propsName, false);
|
||||
}
|
||||
|
||||
public void processRequired(String propsName) {
|
||||
loadConversionProperties(propsName, true);
|
||||
}
|
||||
|
||||
public void loadConversionProperties(String propsName, boolean require) {
|
||||
try {
|
||||
Iterator<URL> resources = ClassLoaderUtil.getResources(propsName, getClass(), true);
|
||||
while (resources.hasNext()) {
|
||||
URL url = resources.next();
|
||||
Properties props = new Properties();
|
||||
props.load(url.openStream());
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("processing conversion file [" + propsName + "]");
|
||||
}
|
||||
|
||||
for (Object o : props.entrySet()) {
|
||||
Map.Entry entry = (Map.Entry) o;
|
||||
String key = (String) entry.getKey();
|
||||
|
||||
try {
|
||||
TypeConverter _typeConverter = converterCreator.createTypeConverter((String) entry.getValue());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + " [treated as TypeConverter " + _typeConverter + "]");
|
||||
}
|
||||
converterHolder.addDefaultMapping(key, _typeConverter);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Conversion registration error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
if (require) {
|
||||
throw new XWorkException("Cannot load conversion properties file: "+propsName, ex);
|
||||
} else {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Cannot load conversion properties file: #0", ex, propsName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterCreator;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.ognl.XWorkTypeConverterWrapper;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link TypeConverterCreator}
|
||||
*/
|
||||
public class DefaultTypeConverterCreator implements TypeConverterCreator {
|
||||
|
||||
private ObjectFactory objectFactory;
|
||||
|
||||
@Inject
|
||||
public void setObjectFactory(ObjectFactory objectFactory) {
|
||||
this.objectFactory = objectFactory;
|
||||
}
|
||||
|
||||
public TypeConverter createTypeConverter(String className) throws Exception {
|
||||
// type converters are used across users
|
||||
Object obj = objectFactory.buildBean(className, null);
|
||||
if (obj instanceof TypeConverter) {
|
||||
return (TypeConverter) obj;
|
||||
|
||||
// For backwards compatibility
|
||||
} else if (obj instanceof ognl.TypeConverter) {
|
||||
return new XWorkTypeConverterWrapper((ognl.TypeConverter) obj);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Type converter class " + obj.getClass() + " doesn't implement com.opensymphony.xwork2.conversion.TypeConverter");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package com.opensymphony.xwork2.conversion.impl;
|
||||
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterHolder;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link TypeConverterHolder}
|
||||
*/
|
||||
public class DefaultTypeConverterHolder implements TypeConverterHolder {
|
||||
|
||||
/**
|
||||
* Record class and its type converter mapping.
|
||||
* <pre>
|
||||
* - String - classname as String
|
||||
* - TypeConverter - instance of TypeConverter
|
||||
* </pre>
|
||||
*/
|
||||
private HashMap<String, TypeConverter> defaultMappings = new HashMap<String, TypeConverter>(); // non-action (eg. returned value)
|
||||
|
||||
/**
|
||||
* Target class conversion Mappings.
|
||||
* <pre>
|
||||
* Map<Class, Map<String, Object>>
|
||||
* - Class -> convert to class
|
||||
* - Map<String, Object>
|
||||
* - String -> property name
|
||||
* eg. Element_property, property etc.
|
||||
* - Object -> String to represent properties
|
||||
* eg. value part of
|
||||
* KeyProperty_property=id
|
||||
* -> TypeConverter to represent an Ognl TypeConverter
|
||||
* eg. value part of
|
||||
* property=foo.bar.MyConverter
|
||||
* -> Class to represent a class
|
||||
* eg. value part of
|
||||
* Element_property=foo.bar.MyObject
|
||||
* </pre>
|
||||
*/
|
||||
private HashMap<Class, Map<String, Object>> mappings = new HashMap<Class, Map<String, Object>>(); // action
|
||||
|
||||
/**
|
||||
* Unavailable target class conversion mappings, serves as a simple cache.
|
||||
*/
|
||||
private HashSet<Class> noMapping = new HashSet<Class>(); // action
|
||||
|
||||
/**
|
||||
* Record classes that doesn't have conversion mapping defined.
|
||||
* <pre>
|
||||
* - String -> classname as String
|
||||
* </pre>
|
||||
*/
|
||||
protected HashSet<String> unknownMappings = new HashSet<String>(); // non-action (eg. returned value)
|
||||
|
||||
public void addDefaultMapping(String className, TypeConverter typeConverter) {
|
||||
defaultMappings.put(className, typeConverter);
|
||||
if (unknownMappings.contains(className)) {
|
||||
unknownMappings.remove(className);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean containsDefaultMapping(String className) {
|
||||
return defaultMappings.containsKey(className);
|
||||
}
|
||||
|
||||
public TypeConverter getDefaultMapping(String className) {
|
||||
return defaultMappings.get(className);
|
||||
}
|
||||
|
||||
public Map<String, Object> getMapping(Class clazz) {
|
||||
return mappings.get(clazz);
|
||||
}
|
||||
|
||||
public void addMapping(Class clazz, Map<String, Object> mapping) {
|
||||
mappings.put(clazz, mapping);
|
||||
}
|
||||
|
||||
public boolean containsNoMapping(Class clazz) {
|
||||
return noMapping.contains(clazz);
|
||||
}
|
||||
|
||||
public void addNoMapping(Class clazz) {
|
||||
noMapping.add(clazz);
|
||||
}
|
||||
|
||||
public boolean containsUnknownMapping(String className) {
|
||||
return unknownMappings.contains(className);
|
||||
}
|
||||
|
||||
public void addUnknownMapping(String className) {
|
||||
unknownMappings.add(className);
|
||||
}
|
||||
|
||||
}
|
||||
+67
-340
@@ -18,17 +18,16 @@ package com.opensymphony.xwork2.conversion.impl;
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.FileManager;
|
||||
import com.opensymphony.xwork2.FileManagerFactory;
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.XWorkConstants;
|
||||
import com.opensymphony.xwork2.XWorkException;
|
||||
import com.opensymphony.xwork2.XWorkMessages;
|
||||
import com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor;
|
||||
import com.opensymphony.xwork2.conversion.ConversionFileProcessor;
|
||||
import com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverter;
|
||||
import com.opensymphony.xwork2.conversion.TypeConverterHolder;
|
||||
import com.opensymphony.xwork2.conversion.annotations.Conversion;
|
||||
import com.opensymphony.xwork2.conversion.annotations.ConversionRule;
|
||||
import com.opensymphony.xwork2.conversion.annotations.ConversionType;
|
||||
import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.ognl.XWorkTypeConverterWrapper;
|
||||
import com.opensymphony.xwork2.util.AnnotationUtils;
|
||||
import com.opensymphony.xwork2.util.ClassLoaderUtil;
|
||||
import com.opensymphony.xwork2.util.CompoundRoot;
|
||||
@@ -39,8 +38,6 @@ import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -48,11 +45,8 @@ import java.net.URL;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -137,7 +131,8 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class XWorkConverter extends DefaultTypeConverter {
|
||||
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(XWorkConverter.class);
|
||||
private static final Logger LOG = LoggerFactory.getLogger(XWorkConverter.class);
|
||||
|
||||
public static final String REPORT_CONVERSION_ERRORS = "report.conversion.errors";
|
||||
public static final String CONVERSION_PROPERTY_FULLNAME = "conversion.property.fullName";
|
||||
public static final String CONVERSION_ERROR_PROPERTY_PREFIX = "invalid.fieldvalue.";
|
||||
@@ -148,68 +143,20 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
public static final String MESSAGE_INDEX_PATTERN = "\\[\\d+\\]\\.";
|
||||
public static final String MESSAGE_INDEX_BRACKET_PATTERN = "[\\[\\]\\.]";
|
||||
public static final String PERIOD = ".";
|
||||
public static final Pattern messageIndexPattern = Pattern.compile(MESSAGE_INDEX_PATTERN);
|
||||
|
||||
/**
|
||||
* Target class conversion Mappings.
|
||||
* <pre>
|
||||
* Map<Class, Map<String, Object>>
|
||||
* - Class -> convert to class
|
||||
* - Map<String, Object>
|
||||
* - String -> property name
|
||||
* eg. Element_property, property etc.
|
||||
* - Object -> String to represent properties
|
||||
* eg. value part of
|
||||
* KeyProperty_property=id
|
||||
* -> TypeConverter to represent an Ognl TypeConverter
|
||||
* eg. value part of
|
||||
* property=foo.bar.MyConverter
|
||||
* -> Class to represent a class
|
||||
* eg. value part of
|
||||
* Element_property=foo.bar.MyObject
|
||||
* </pre>
|
||||
*/
|
||||
protected HashMap<Class, Map<String, Object>> mappings = new HashMap<Class, Map<String, Object>>(); // action
|
||||
|
||||
/**
|
||||
* Unavailable target class conversion mappings, serves as a simple cache.
|
||||
*/
|
||||
protected HashSet<Class> noMapping = new HashSet<Class>(); // action
|
||||
|
||||
/**
|
||||
* Record class and its type converter mapping.
|
||||
* <pre>
|
||||
* - String - classname as String
|
||||
* - TypeConverter - instance of TypeConverter
|
||||
* </pre>
|
||||
*/
|
||||
protected HashMap<String, TypeConverter> defaultMappings = new HashMap<String, TypeConverter>(); // non-action (eg. returned value)
|
||||
|
||||
/**
|
||||
* Record classes that doesn't have conversion mapping defined.
|
||||
* <pre>
|
||||
* - String -> classname as String
|
||||
* </pre>
|
||||
*/
|
||||
protected HashSet<String> unknownMappings = new HashSet<String>(); // non-action (eg. returned value)
|
||||
public static final Pattern messageIndexPattern = Pattern.compile(MESSAGE_INDEX_PATTERN);
|
||||
|
||||
private TypeConverter defaultTypeConverter;
|
||||
private ObjectFactory objectFactory;
|
||||
private FileManager fileManager;
|
||||
private boolean reloadingConfigs;
|
||||
|
||||
private ConversionFileProcessor fileProcessor;
|
||||
private ConversionAnnotationProcessor annotationProcessor;
|
||||
|
||||
private TypeConverterHolder converterHolder;
|
||||
|
||||
protected XWorkConverter() {
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setObjectFactory(ObjectFactory factory) {
|
||||
this.objectFactory = factory;
|
||||
// note: this file is deprecated
|
||||
loadConversionProperties("xwork-default-conversion.properties");
|
||||
|
||||
loadConversionProperties("xwork-conversion.properties");
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setDefaultTypeConverter(XWorkBasicConverter conv) {
|
||||
this.defaultTypeConverter = conv;
|
||||
@@ -225,6 +172,28 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
this.reloadingConfigs = Boolean.parseBoolean(reloadingConfigs);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setConversionPropertiesProcessor(ConversionPropertiesProcessor propertiesProcessor) {
|
||||
// note: this file is deprecated
|
||||
propertiesProcessor.process("xwork-default-conversion.properties");
|
||||
propertiesProcessor.process("xwork-conversion.properties");
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setConversionFileProcessor(ConversionFileProcessor fileProcessor) {
|
||||
this.fileProcessor = fileProcessor;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setConversionAnnotationProcessor(ConversionAnnotationProcessor annotationProcessor) {
|
||||
this.annotationProcessor = annotationProcessor;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setTypeConverterHolder(TypeConverterHolder converterHolder) {
|
||||
this.converterHolder = converterHolder;
|
||||
}
|
||||
|
||||
public static String getConversionErrorMessage(String propertyName, ValueStack stack) {
|
||||
String defaultMessage = LocalizedTextUtil.findDefaultText(XWorkMessages.DEFAULT_INVALID_FIELDVALUE,
|
||||
ActionContext.getContext().getLocale(),
|
||||
@@ -234,7 +203,7 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
|
||||
List<String> indexValues = getIndexValues(propertyName);
|
||||
|
||||
propertyName = removeAllIndexesInProperyName(propertyName);
|
||||
propertyName = removeAllIndexesInProperytName(propertyName);
|
||||
|
||||
String getTextExpression = "getText('" + CONVERSION_ERROR_PROPERTY_PREFIX + propertyName + "','" + defaultMessage + "')";
|
||||
String message = (String) stack.findValue(getTextExpression);
|
||||
@@ -248,7 +217,7 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
return message;
|
||||
}
|
||||
|
||||
private static String removeAllIndexesInProperyName(String propertyName) {
|
||||
private static String removeAllIndexesInProperytName(String propertyName) {
|
||||
return propertyName.replaceAll(MESSAGE_INDEX_PATTERN, PERIOD);
|
||||
}
|
||||
|
||||
@@ -386,11 +355,11 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
* @return a TypeConverter to handle the specified class or null if none can be found
|
||||
*/
|
||||
public TypeConverter lookup(String className) {
|
||||
if (unknownMappings.contains(className) && !defaultMappings.containsKey(className)) {
|
||||
if (converterHolder.containsUnknownMapping(className) && !converterHolder.containsDefaultMapping(className)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TypeConverter result = defaultMappings.get(className);
|
||||
TypeConverter result = converterHolder.getDefaultMapping(className);
|
||||
|
||||
//Looks for super classes
|
||||
if (result == null) {
|
||||
@@ -430,13 +399,12 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
|
||||
protected Object getConverter(Class clazz, String property) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Property: " + property);
|
||||
LOG.debug("Class: " + clazz.getName());
|
||||
LOG.debug("Retrieving convert for class [#0] and property [#1]", clazz, property);
|
||||
}
|
||||
synchronized (clazz) {
|
||||
if ((property != null) && !noMapping.contains(clazz)) {
|
||||
if ((property != null) && !converterHolder.containsNoMapping(clazz)) {
|
||||
try {
|
||||
Map<String, Object> mapping = mappings.get(clazz);
|
||||
Map<String, Object> mapping = converterHolder.getMapping(clazz);
|
||||
|
||||
if (mapping == null) {
|
||||
mapping = buildConverterMapping(clazz);
|
||||
@@ -446,7 +414,7 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
|
||||
Object converter = mapping.get(property);
|
||||
if (LOG.isDebugEnabled() && converter == null) {
|
||||
LOG.debug("converter is null for property " + property + ". Mapping size: " + mapping.size());
|
||||
LOG.debug("Converter is null for property [#0]. Mapping size [#1]:", property, mapping.size());
|
||||
for (String next : mapping.keySet()) {
|
||||
LOG.debug(next + ":" + mapping.get(next));
|
||||
}
|
||||
@@ -454,13 +422,12 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
return converter;
|
||||
} catch (Throwable t) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Got exception trying to resolve convert for class [#0] and property [#1]", t, clazz.getName(), property);
|
||||
LOG.debug("Got exception trying to resolve convert for class [#0] and property [#1]", t, clazz, property);
|
||||
}
|
||||
noMapping.add(clazz);
|
||||
converterHolder.addNoMapping(clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -485,14 +452,11 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
}
|
||||
|
||||
public synchronized void registerConverter(String className, TypeConverter converter) {
|
||||
defaultMappings.put(className, converter);
|
||||
if (unknownMappings.contains(className)) {
|
||||
unknownMappings.remove(className);
|
||||
}
|
||||
converterHolder.addDefaultMapping(className, converter);
|
||||
}
|
||||
|
||||
public synchronized void registerConverterNotFound(String className) {
|
||||
unknownMappings.add(className);
|
||||
converterHolder.addUnknownMapping(className);
|
||||
}
|
||||
|
||||
private Object[] getClassProperty(Map<String, Object> context) {
|
||||
@@ -509,79 +473,9 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
* @param clazz class to look for converter mappings for
|
||||
*/
|
||||
protected void addConverterMapping(Map<String, Object> mapping, Class clazz) {
|
||||
try {
|
||||
String converterFilename = buildConverterFilename(clazz);
|
||||
InputStream is = fileManager.loadFile(ClassLoaderUtil.getResource(converterFilename, clazz));
|
||||
|
||||
if (is != null) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("processing conversion file [" + converterFilename + "] [class=" + clazz + "]");
|
||||
}
|
||||
|
||||
Properties prop = new Properties();
|
||||
prop.load(is);
|
||||
|
||||
for (Map.Entry<Object, Object> entry : prop.entrySet()) {
|
||||
String key = (String) entry.getKey();
|
||||
|
||||
if (mapping.containsKey(key)) {
|
||||
break;
|
||||
}
|
||||
// for keyProperty of Set
|
||||
if (key.startsWith(DefaultObjectTypeDeterminer.KEY_PROPERTY_PREFIX)
|
||||
|| key.startsWith(DefaultObjectTypeDeterminer.CREATE_IF_NULL_PREFIX)) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as String]");
|
||||
}
|
||||
mapping.put(key, entry.getValue());
|
||||
}
|
||||
//for properties of classes
|
||||
else if (!(key.startsWith(DefaultObjectTypeDeterminer.ELEMENT_PREFIX) ||
|
||||
key.startsWith(DefaultObjectTypeDeterminer.KEY_PREFIX) ||
|
||||
key.startsWith(DefaultObjectTypeDeterminer.DEPRECATED_ELEMENT_PREFIX))
|
||||
) {
|
||||
TypeConverter _typeConverter = createTypeConverter((String) entry.getValue());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as TypeConverter " + _typeConverter + "]");
|
||||
}
|
||||
mapping.put(key, _typeConverter);
|
||||
}
|
||||
//for keys of Maps
|
||||
else if (key.startsWith(DefaultObjectTypeDeterminer.KEY_PREFIX)) {
|
||||
|
||||
Class converterClass = Thread.currentThread().getContextClassLoader().loadClass((String) entry.getValue());
|
||||
|
||||
//check if the converter is a type converter if it is one
|
||||
//then just put it in the map as is. Otherwise
|
||||
//put a value in for the type converter of the class
|
||||
if (converterClass.isAssignableFrom(TypeConverter.class)) {
|
||||
TypeConverter _typeConverter = createTypeConverter((String) entry.getValue());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as TypeConverter " + _typeConverter + "]");
|
||||
}
|
||||
mapping.put(key, _typeConverter);
|
||||
} else {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as Class " + converterClass + "]");
|
||||
}
|
||||
mapping.put(key, converterClass);
|
||||
}
|
||||
}
|
||||
//elements(values) of maps / lists
|
||||
else {
|
||||
Class _c = Thread.currentThread().getContextClassLoader().loadClass((String) entry.getValue());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + "[treated as Class " + _c + "]");
|
||||
}
|
||||
mapping.put(key, _c);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
if (LOG.isErrorEnabled()) {
|
||||
LOG.error("Problem loading properties for #0", ex, clazz.getName());
|
||||
}
|
||||
}
|
||||
// Process <clazz>-conversion.properties file
|
||||
String converterFilename = buildConverterFilename(clazz);
|
||||
fileProcessor.process(mapping, clazz, converterFilename);
|
||||
|
||||
// Process annotations
|
||||
Annotation[] annotations = clazz.getAnnotations();
|
||||
@@ -589,156 +483,46 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
for (Annotation annotation : annotations) {
|
||||
if (annotation instanceof Conversion) {
|
||||
Conversion conversion = (Conversion) annotation;
|
||||
|
||||
for (TypeConversion tc : conversion.conversions()) {
|
||||
|
||||
String key = tc.key();
|
||||
|
||||
if (mapping.containsKey(key)) {
|
||||
if (mapping.containsKey(tc.key())) {
|
||||
break;
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
if (StringUtils.isEmpty(key)) {
|
||||
if (StringUtils.isEmpty(tc.key())) {
|
||||
LOG.debug("WARNING! key of @TypeConversion [#0] applied to [#1] is empty!", tc.converter(), clazz.getName());
|
||||
} else {
|
||||
LOG.debug("TypeConversion [#0] with key: [#1]", tc.converter(), key);
|
||||
}
|
||||
}
|
||||
|
||||
if (key != null) {
|
||||
try {
|
||||
if (tc.type() == ConversionType.APPLICATION) {
|
||||
defaultMappings.put(key, createTypeConverter(tc.converter()));
|
||||
} else {
|
||||
if (tc.rule().toString().equals(ConversionRule.KEY_PROPERTY) || tc.rule().toString().equals(ConversionRule.CREATE_IF_NULL)) {
|
||||
mapping.put(key, tc.value());
|
||||
}
|
||||
//for properties of classes
|
||||
else if (!(tc.rule().toString().equals(ConversionRule.ELEMENT.toString())) ||
|
||||
tc.rule().toString().equals(ConversionRule.KEY.toString()) ||
|
||||
tc.rule().toString().equals(ConversionRule.COLLECTION.toString())
|
||||
) {
|
||||
mapping.put(key, createTypeConverter(tc.converter()));
|
||||
|
||||
|
||||
}
|
||||
//for keys of Maps
|
||||
else if (tc.rule().toString().equals(ConversionRule.KEY.toString())) {
|
||||
Class converterClass = Thread.currentThread().getContextClassLoader().loadClass(tc.converter());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Converter class: " + converterClass);
|
||||
}
|
||||
//check if the converter is a type converter if it is one
|
||||
//then just put it in the map as is. Otherwise
|
||||
//put a value in for the type converter of the class
|
||||
if (converterClass.isAssignableFrom(TypeConverter.class)) {
|
||||
mapping.put(key, createTypeConverter(tc.converter()));
|
||||
} else {
|
||||
mapping.put(key, converterClass);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Object placed in mapping for key "
|
||||
+ key
|
||||
+ " is "
|
||||
+ mapping.get(key));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
//elements(values) of maps / lists
|
||||
else {
|
||||
mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass(tc.converter()));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Got exception for #0", e, key);
|
||||
}
|
||||
LOG.debug("TypeConversion [#0] with key: [#1]", tc.converter(), tc.key());
|
||||
}
|
||||
}
|
||||
annotationProcessor.process(mapping, tc, tc.key());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Method[] methods = clazz.getMethods();
|
||||
|
||||
for (Method method : methods) {
|
||||
|
||||
// Process annotated methods
|
||||
for (Method method : clazz.getMethods()) {
|
||||
annotations = method.getAnnotations();
|
||||
|
||||
for (Annotation annotation : annotations) {
|
||||
if (annotation instanceof TypeConversion) {
|
||||
TypeConversion tc = (TypeConversion) annotation;
|
||||
|
||||
String key = tc.key();
|
||||
if (mapping.containsKey(key)) {
|
||||
if (mapping.containsKey(tc.key())) {
|
||||
break;
|
||||
}
|
||||
String key = tc.key();
|
||||
// Default to the property name
|
||||
if (key != null && key.length() == 0) {
|
||||
if (StringUtils.isEmpty(key)) {
|
||||
key = AnnotationUtils.resolvePropertyName(method);
|
||||
LOG.debug("key from method name... " + key + " - " + method.getName());
|
||||
}
|
||||
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(key + ":" + key);
|
||||
}
|
||||
|
||||
if (key != null) {
|
||||
try {
|
||||
if (tc.type() == ConversionType.APPLICATION) {
|
||||
defaultMappings.put(key, createTypeConverter(tc.converter()));
|
||||
} else {
|
||||
if (tc.rule().toString().equals(ConversionRule.KEY_PROPERTY)) {
|
||||
mapping.put(key, tc.value());
|
||||
}
|
||||
//for properties of classes
|
||||
else if (!(tc.rule().toString().equals(ConversionRule.ELEMENT.toString())) ||
|
||||
tc.rule().toString().equals(ConversionRule.KEY.toString()) ||
|
||||
tc.rule().toString().equals(ConversionRule.COLLECTION.toString())
|
||||
) {
|
||||
mapping.put(key, createTypeConverter(tc.converter()));
|
||||
}
|
||||
//for keys of Maps
|
||||
else if (tc.rule().toString().equals(ConversionRule.KEY.toString())) {
|
||||
Class converterClass = Thread.currentThread().getContextClassLoader().loadClass(tc.converter());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Converter class: " + converterClass);
|
||||
}
|
||||
//check if the converter is a type converter if it is one
|
||||
//then just put it in the map as is. Otherwise
|
||||
//put a value in for the type converter of the class
|
||||
if (converterClass.isAssignableFrom(TypeConverter.class)) {
|
||||
mapping.put(key, createTypeConverter(tc.converter()));
|
||||
} else {
|
||||
mapping.put(key, converterClass);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Object placed in mapping for key "
|
||||
+ key
|
||||
+ " is "
|
||||
+ mapping.get(key));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
//elements(values) of maps / lists
|
||||
else {
|
||||
mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass(tc.converter()));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Got exception for #0", e, key);
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Retrieved key [#0] from method name [#1]", key, method.getName());
|
||||
}
|
||||
}
|
||||
annotationProcessor.process(mapping, tc, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Looks for converter mappings for the specified class, traversing up its class hierarchy and interfaces and adding
|
||||
* any additional mappings it may find. Mappings lower in the hierarchy have priority over those higher in the
|
||||
@@ -768,9 +552,9 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
}
|
||||
|
||||
if (mapping.size() > 0) {
|
||||
mappings.put(clazz, mapping);
|
||||
converterHolder.addMapping(clazz, mapping);
|
||||
} else {
|
||||
noMapping.add(clazz);
|
||||
converterHolder.addNoMapping(clazz);
|
||||
}
|
||||
|
||||
return mapping;
|
||||
@@ -789,62 +573,6 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
TypeConverter createTypeConverter(String className) throws Exception {
|
||||
// type converters are used across users
|
||||
Object obj = objectFactory.buildBean(className, null);
|
||||
if (obj instanceof TypeConverter) {
|
||||
return (TypeConverter) obj;
|
||||
|
||||
// For backwards compatibility
|
||||
} else if (obj instanceof ognl.TypeConverter) {
|
||||
return new XWorkTypeConverterWrapper((ognl.TypeConverter) obj);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Type converter class " + obj.getClass() + " doesn't implement com.opensymphony.xwork2.conversion.TypeConverter");
|
||||
}
|
||||
}
|
||||
|
||||
public void loadConversionProperties(String propsName) {
|
||||
loadConversionProperties(propsName, false);
|
||||
}
|
||||
|
||||
public void loadConversionProperties(String propsName, boolean require) {
|
||||
try {
|
||||
Iterator<URL> resources = ClassLoaderUtil.getResources(propsName, getClass(), true);
|
||||
while (resources.hasNext()) {
|
||||
URL url = resources.next();
|
||||
Properties props = new Properties();
|
||||
props.load(url.openStream());
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("processing conversion file [" + propsName + "]");
|
||||
}
|
||||
|
||||
for (Object o : props.entrySet()) {
|
||||
Map.Entry entry = (Map.Entry) o;
|
||||
String key = (String) entry.getKey();
|
||||
|
||||
try {
|
||||
TypeConverter _typeConverter = createTypeConverter((String) entry.getValue());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("\t" + key + ":" + entry.getValue() + " [treated as TypeConverter " + _typeConverter + "]");
|
||||
}
|
||||
defaultMappings.put(key, _typeConverter);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Conversion registration error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
if (require) {
|
||||
throw new XWorkException("Cannot load conversion properties file: "+propsName, ex);
|
||||
} else {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Cannot load conversion properties file: #0", ex, propsName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recurses through a class' interfaces and class hierarchy looking for a TypeConverter in the default mapping that
|
||||
* can handle the specified class.
|
||||
@@ -856,15 +584,15 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
TypeConverter result = null;
|
||||
|
||||
if (clazz != null) {
|
||||
result = defaultMappings.get(clazz.getName());
|
||||
result = converterHolder.getDefaultMapping(clazz.getName());
|
||||
|
||||
if (result == null) {
|
||||
// Looks for direct interfaces (depth = 1 )
|
||||
Class[] interfaces = clazz.getInterfaces();
|
||||
|
||||
for (Class anInterface : interfaces) {
|
||||
if (defaultMappings.containsKey(anInterface.getName())) {
|
||||
result = (TypeConverter) defaultMappings.get(anInterface.getName());
|
||||
if (converterHolder.containsDefaultMapping(anInterface.getName())) {
|
||||
result = converterHolder.getDefaultMapping(anInterface.getName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -880,5 +608,4 @@ public class XWorkConverter extends DefaultTypeConverter {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user