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:
Lukasz Lenart
2013-01-22 21:20:35 +00:00
parent ebc33bc718
commit 2d7564c05b
16 changed files with 741 additions and 349 deletions
@@ -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"/>
@@ -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);
@@ -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)
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
}
}
}
@@ -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());
}
}
}
}
@@ -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);
}
}
}
}
}
@@ -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");
}
}
}
@@ -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);
}
}
@@ -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;
}
}