ui.format system refining from RC1 feedback: Support for one format annotation applying to multiple field types, Printer/Parser building blocks for more flexibility, full Joda time formatting support, FormattingService as a service entry-point for clients to use
This commit is contained in:
parent
471fbf7ebf
commit
913bc03a3b
|
|
@ -17,6 +17,7 @@ package org.springframework.mapping.support;
|
|||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
|
@ -31,7 +32,13 @@ final class BeanMappableType implements MappableType<Object> {
|
|||
return true;
|
||||
}
|
||||
|
||||
public Set<String> getFields(Object object) {
|
||||
public EvaluationContext getEvaluationContext(Object object, ConversionService conversionService) {
|
||||
StandardEvaluationContext context = new StandardEvaluationContext(object);
|
||||
context.setTypeConverter(new StandardTypeConverter(conversionService));
|
||||
return context;
|
||||
}
|
||||
|
||||
public Set<String> getFieldNames(Object object) {
|
||||
Set<String> fields = new LinkedHashSet<String>();
|
||||
PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(object.getClass());
|
||||
for (PropertyDescriptor descriptor : descriptors) {
|
||||
|
|
@ -44,10 +51,9 @@ final class BeanMappableType implements MappableType<Object> {
|
|||
return fields;
|
||||
}
|
||||
|
||||
public EvaluationContext getEvaluationContext(Object object, ConversionService conversionService) {
|
||||
StandardEvaluationContext context = new StandardEvaluationContext(object);
|
||||
context.setTypeConverter(new StandardTypeConverter(conversionService));
|
||||
return context;
|
||||
public Map<String, Object> getNestedFields(String fieldName, Object object) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.mapping.support;
|
||||
|
||||
import org.springframework.core.style.StylerUtils;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.mapping.Mapper;
|
||||
|
||||
/**
|
||||
* A mapping between several source fields and one or more target fields.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
final class FlexibleFieldMapping implements SpelMapping {
|
||||
|
||||
private String[] fields;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private final Mapper flexibleFieldMapper;
|
||||
|
||||
private Expression condition;
|
||||
|
||||
public FlexibleFieldMapping(String[] fields, Mapper<?, ?> flexibleFieldMapper, Expression condition) {
|
||||
this.fields = fields;
|
||||
this.flexibleFieldMapper = flexibleFieldMapper;
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
public String[] getSourceFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
public boolean mapsField(String field) {
|
||||
for (String f : this.fields) {
|
||||
if (f.equals(field)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void map(SpelMappingContext context) {
|
||||
if (!context.conditionHolds(this.condition)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.flexibleFieldMapper.map(context.getSource(), context.getTarget());
|
||||
} catch (Exception e) {
|
||||
context.addMappingFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.flexibleFieldMapper.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof FlexibleFieldMapping)) {
|
||||
return false;
|
||||
}
|
||||
FlexibleFieldMapping m = (FlexibleFieldMapping) o;
|
||||
return this.flexibleFieldMapper.equals(m.flexibleFieldMapper);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[FlexibleFieldMapping<" + StylerUtils.style(this.fields) + " -> " + this.flexibleFieldMapper + ">]";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.springframework.mapping.support;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
|
@ -31,16 +32,6 @@ final class MapMappableType implements MappableType<Map<? extends Object, ? exte
|
|||
return object instanceof Map<?, ?>;
|
||||
}
|
||||
|
||||
public Set<String> getFields(Map<? extends Object, ? extends Object> object) {
|
||||
LinkedHashSet<String> fields = new LinkedHashSet<String>(object.size(), 1);
|
||||
for (Object key : object.keySet()) {
|
||||
if (key != null && key instanceof String) {
|
||||
fields.add((String) key);
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
public EvaluationContext getEvaluationContext(Map<? extends Object, ? extends Object> object,
|
||||
ConversionService conversionService) {
|
||||
StandardEvaluationContext context = new StandardEvaluationContext(object);
|
||||
|
|
@ -49,4 +40,28 @@ final class MapMappableType implements MappableType<Map<? extends Object, ? exte
|
|||
return context;
|
||||
}
|
||||
|
||||
public Set<String> getFieldNames(Map<? extends Object, ? extends Object> object) {
|
||||
Set<String> fieldNames = new LinkedHashSet<String>(object.size(), 1);
|
||||
for (Object key : object.keySet()) {
|
||||
if (key != null && key instanceof String) {
|
||||
fieldNames.add((String) key);
|
||||
}
|
||||
}
|
||||
return fieldNames;
|
||||
}
|
||||
|
||||
public Map<String, Object> getNestedFields(String fieldName, Map<? extends Object, ? extends Object> object) {
|
||||
Map<String, Object> fields = new LinkedHashMap<String, Object>(object.size(), 1);
|
||||
for (Map.Entry<? extends Object, ? extends Object> entry : object.entrySet()) {
|
||||
Object key = entry.getKey();
|
||||
if (key != null && key instanceof String) {
|
||||
String name = (String) key;
|
||||
if (name.startsWith(fieldName + ".")) {
|
||||
fields.put(name.substring(fieldName.length() + 1), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.springframework.mapping.support;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
|
|
@ -28,9 +29,10 @@ import org.springframework.expression.EvaluationContext;
|
|||
interface MappableType<T> {
|
||||
|
||||
/**
|
||||
* The fields of the object that are eligible for mapping, including any nested fields.
|
||||
* Is this object to map an instanceof this mappable type?
|
||||
* @param object the object to test
|
||||
*/
|
||||
Set<String> getFields(T object);
|
||||
boolean isInstance(Object object);
|
||||
|
||||
/**
|
||||
* A evaluation context for accessing the object.
|
||||
|
|
@ -38,9 +40,13 @@ interface MappableType<T> {
|
|||
EvaluationContext getEvaluationContext(T object, ConversionService conversionService);
|
||||
|
||||
/**
|
||||
* Is this object to map an instanceof this mappable type?
|
||||
* @param object the object to test
|
||||
* The names of the fields on the object that are eligible for mapping, including any nested fields.
|
||||
*/
|
||||
boolean isInstance(Object object);
|
||||
Set<String> getFieldNames(T object);
|
||||
|
||||
/**
|
||||
* A map of the nested fields on the object that are nested relative to <code>fieldName</code>.
|
||||
*/
|
||||
Map<String, Object> getNestedFields(String fieldName, T object);
|
||||
|
||||
}
|
||||
|
|
@ -15,6 +15,9 @@
|
|||
*/
|
||||
package org.springframework.mapping.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.mapping.Mapper;
|
||||
|
||||
|
|
@ -107,6 +110,17 @@ public interface MapperBuilder<S, T> {
|
|||
*/
|
||||
MapperBuilder<S, T> addMapping(String[] fields, Mapper<S, T> mapper);
|
||||
|
||||
/**
|
||||
* Register a mapping that delegates to an assembler to convert multiple source field values to a single target field value.
|
||||
* The source field names mapped begin with <code>field.</code>.
|
||||
* For example, adding an assembler mapping for a field named <code>dateTime</code> would pass all nested <code>dateTime.*</code> fields to the assembler.
|
||||
* Such fields might be <code>year</code>, <code>month</code>, <code>day</code>, etc.
|
||||
* @param fields the name of the source field whose value will be assembled from multiple nested fields that begin with the same name
|
||||
* @param converter the assembler that will perform value assembly from the field values
|
||||
* @return this, for configuring additional field mapping options fluently
|
||||
*/
|
||||
MapperBuilder<S, T> addAssemblerMapping(String field, Converter<? extends Map<?, ?>, ?> converter);
|
||||
|
||||
/**
|
||||
* Register a conditional mapping between a source field and a target field.
|
||||
* The source and target field names will be the same value.
|
||||
|
|
@ -170,7 +184,7 @@ public interface MapperBuilder<S, T> {
|
|||
* @param condition the boolean expression that determines if this mapping executes
|
||||
* @return this, for configuring additional field mapping options fluently
|
||||
*/
|
||||
MapperBuilder<S, T> addMapping(String[] fields, Mapper<S, T> mapper, String condition);
|
||||
MapperBuilder<S, T> addConditionalMapping(String[] fields, Mapper<S, T> mapper, String condition);
|
||||
|
||||
/**
|
||||
* Register a Mapper that will be used to map between nested source and target fields of a specific sourceType/targetType pair.
|
||||
|
|
|
|||
|
|
@ -61,11 +61,7 @@ final class MappingConverter implements GenericConverter {
|
|||
}
|
||||
|
||||
private boolean isCopyByReference(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
if (BeanUtils.isSimpleValueType(targetType.getType())) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Object createTargetAndMap(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
|
|
|
|||
|
|
@ -15,9 +15,11 @@
|
|||
*/
|
||||
package org.springframework.mapping.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.style.StylerUtils;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.mapping.Mapper;
|
||||
|
||||
/**
|
||||
* A mapping between several source fields and a target field.
|
||||
|
|
@ -25,30 +27,33 @@ import org.springframework.mapping.Mapper;
|
|||
*/
|
||||
final class MultiFieldToFieldMapping implements SpelMapping {
|
||||
|
||||
private String[] fields;
|
||||
private final String sourceField;
|
||||
|
||||
private final Expression targetField;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private final Mapper multiFieldMapper;
|
||||
private final Converter targetFieldValueAssembler;
|
||||
|
||||
private Expression condition;
|
||||
private final Expression condition;
|
||||
|
||||
public MultiFieldToFieldMapping(String[] fields, Mapper<?, ?> multiFieldMapper, Expression condition) {
|
||||
this.fields = fields;
|
||||
this.multiFieldMapper = multiFieldMapper;
|
||||
public MultiFieldToFieldMapping(String sourceField, Expression targetField, Converter<? extends Map<?,?>, ?> assembler,
|
||||
Expression condition) {
|
||||
this.sourceField = sourceField;
|
||||
this.targetField = targetField;
|
||||
this.targetFieldValueAssembler = assembler;
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
public String[] getSourceFields() {
|
||||
return fields;
|
||||
public String getSourceField() {
|
||||
return this.sourceField + ".*";
|
||||
}
|
||||
|
||||
public String getTargetField() {
|
||||
return this.targetField.getExpressionString();
|
||||
}
|
||||
|
||||
public boolean mapsField(String field) {
|
||||
for (String f : this.fields) {
|
||||
if (f.equals(field)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return field.startsWith(this.sourceField + ".");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -57,14 +62,16 @@ final class MultiFieldToFieldMapping implements SpelMapping {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
this.multiFieldMapper.map(context.getSource(), context.getTarget());
|
||||
Map<String, Object> nestedFields = context.getSourceNestedFields(this.sourceField);
|
||||
Object value = this.targetFieldValueAssembler.convert(nestedFields);
|
||||
context.setTargetFieldValue(this.targetField, value);
|
||||
} catch (Exception e) {
|
||||
context.addMappingFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.multiFieldMapper.hashCode();
|
||||
return getSourceField().hashCode() + getTargetField().hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
|
|
@ -72,11 +79,11 @@ final class MultiFieldToFieldMapping implements SpelMapping {
|
|||
return false;
|
||||
}
|
||||
MultiFieldToFieldMapping m = (MultiFieldToFieldMapping) o;
|
||||
return this.multiFieldMapper.equals(m.multiFieldMapper);
|
||||
return getSourceField().equals(m.getSourceField()) && getTargetField().equals(m.getTargetField());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[MultiFieldToFieldMapping<" + StylerUtils.style(this.fields) + " -> " + this.multiFieldMapper + ">]";
|
||||
return "[MultiFieldToFieldMapping<" + StylerUtils.style(getSourceField()) + " -> " + getTargetField() + ">]";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ package org.springframework.mapping.support;
|
|||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
|
@ -24,7 +25,6 @@ import org.apache.commons.logging.LogFactory;
|
|||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ParseException;
|
||||
|
|
@ -75,21 +75,24 @@ final class SpelMapper implements Mapper<Object, Object> {
|
|||
// TODO
|
||||
}
|
||||
|
||||
public void addMapping(String sourceFieldExpression, String targetFieldExpression, Converter<?, ?> converter,
|
||||
public void addFieldToFieldMapping(String sourceField, String targetField, Converter<?, ?> converter,
|
||||
String condition) {
|
||||
Expression sourceField = parseSourceField(sourceFieldExpression);
|
||||
Expression targetField = parseTargetField(targetFieldExpression);
|
||||
FieldToFieldMapping mapping = new FieldToFieldMapping(sourceField, targetField, converter,
|
||||
parseCondition(condition));
|
||||
this.mappings.add(mapping);
|
||||
this.mappings.add(new FieldToFieldMapping(parseSourceField(sourceField), parseTargetField(targetField),
|
||||
converter, parseCondition(condition)));
|
||||
}
|
||||
|
||||
public void addMapping(String field, Mapper<?, ?> mapper, String condition) {
|
||||
public void addFieldToMultiFieldMapping(String field, Mapper<?, ?> mapper, String condition) {
|
||||
this.mappings.add(new FieldToMultiFieldMapping(parseSourceField(field), mapper, parseCondition(condition)));
|
||||
}
|
||||
|
||||
public void addMapping(String[] fields, Mapper<?, ?> mapper, String condition) {
|
||||
this.mappings.add(new MultiFieldToFieldMapping(fields, mapper, parseCondition(condition)));
|
||||
public void addMultiFieldToFieldMapping(String[] fields, Mapper<?, ?> mapper, String condition) {
|
||||
this.mappings.add(new FlexibleFieldMapping(fields, mapper, parseCondition(condition)));
|
||||
}
|
||||
|
||||
public void addMultiFieldToFieldMapping(String sourceField, String targetField,
|
||||
Converter<? extends Map<?, ?>, ?> assembler, String condition) {
|
||||
this.mappings.add(new MultiFieldToFieldMapping(sourceField, parseTargetField(targetField), assembler,
|
||||
parseCondition(condition)));
|
||||
}
|
||||
|
||||
public void addNestedMapper(Mapper<?, ?> nestedMapper, MappingTargetFactory targetFactory) {
|
||||
|
|
@ -112,16 +115,17 @@ final class SpelMapper implements Mapper<Object, Object> {
|
|||
Assert.notNull(target, "The target to map to cannot be null");
|
||||
try {
|
||||
MappingContextHolder.push(source);
|
||||
EvaluationContext sourceContext = getEvaluationContext(source);
|
||||
EvaluationContext targetContext = getEvaluationContext(target);
|
||||
SpelMappingContext context = new SpelMappingContext(sourceContext, targetContext);
|
||||
MappableType<?> sourceType = this.mappableTypeFactory.getMappableType(source);
|
||||
MappableType<?> targetType = this.mappableTypeFactory.getMappableType(target);
|
||||
SpelMappingContext context = new SpelMappingContext(source, sourceType, target, targetType,
|
||||
this.conversionService);
|
||||
for (SpelMapping mapping : this.mappings) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(MappingContextHolder.getLevel() + mapping);
|
||||
}
|
||||
mapping.map(context);
|
||||
}
|
||||
Set<FieldToFieldMapping> autoMappings = getAutoMappings(sourceContext, targetContext);
|
||||
Set<FieldToFieldMapping> autoMappings = getAutoMappings(context);
|
||||
for (SpelMapping mapping : autoMappings) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(MappingContextHolder.getLevel() + mapping + " (auto)");
|
||||
|
|
@ -171,33 +175,29 @@ final class SpelMapper implements Mapper<Object, Object> {
|
|||
return GenericTypeResolver.resolveTypeArguments(mapper.getClass(), Mapper.class);
|
||||
}
|
||||
|
||||
private EvaluationContext getEvaluationContext(Object object) {
|
||||
return mappableTypeFactory.getMappableType(object).getEvaluationContext(object, this.conversionService);
|
||||
}
|
||||
|
||||
private Set<FieldToFieldMapping> getAutoMappings(EvaluationContext sourceContext, EvaluationContext targetContext) {
|
||||
private Set<FieldToFieldMapping> getAutoMappings(SpelMappingContext context) {
|
||||
if (this.autoMappingEnabled) {
|
||||
Set<FieldToFieldMapping> autoMappings = new LinkedHashSet<FieldToFieldMapping>();
|
||||
Set<String> sourceFields = getMappableFields(sourceContext.getRootObject().getValue());
|
||||
Set<String> sourceFields = context.getSourceFieldNames();
|
||||
for (String field : sourceFields) {
|
||||
if (!explicitlyMapped(field)) {
|
||||
Expression sourceExpression;
|
||||
Expression targetExpression;
|
||||
Expression sourceField;
|
||||
try {
|
||||
sourceExpression = sourceExpressionParser.parseExpression(field);
|
||||
sourceField = sourceExpressionParser.parseExpression(field);
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalArgumentException("The mapping source '" + field
|
||||
throw new IllegalArgumentException("The source field '" + field
|
||||
+ "' is not a parseable value expression", e);
|
||||
}
|
||||
Expression targetField;
|
||||
try {
|
||||
targetField = targetExpressionParser.parseExpression(field);
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalArgumentException("The target field '" + field
|
||||
+ "' is not a parseable value expression", e);
|
||||
}
|
||||
try {
|
||||
targetExpression = targetExpressionParser.parseExpression(field);
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalArgumentException("The mapping target '" + field
|
||||
+ "' is not a parseable value expression", e);
|
||||
}
|
||||
try {
|
||||
if (targetExpression.isWritable(targetContext)) {
|
||||
autoMappings.add(new FieldToFieldMapping(sourceExpression, targetExpression, null, null));
|
||||
if (context.isTargetFieldWriteable(targetField)) {
|
||||
autoMappings.add(new FieldToFieldMapping(sourceField, targetField, null, null));
|
||||
}
|
||||
} catch (EvaluationException e) {
|
||||
|
||||
|
|
@ -210,10 +210,6 @@ final class SpelMapper implements Mapper<Object, Object> {
|
|||
}
|
||||
}
|
||||
|
||||
private Set<String> getMappableFields(Object object) {
|
||||
return mappableTypeFactory.getMappableType(object).getFields(object);
|
||||
}
|
||||
|
||||
private boolean explicitlyMapped(String field) {
|
||||
for (SpelMapping mapping : this.mappings) {
|
||||
if (mapping.mapsField(field)) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package org.springframework.mapping.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.mapping.Mapper;
|
||||
|
||||
|
|
@ -40,63 +42,68 @@ final class SpelMapperBuilder<S, T> implements MapperBuilder<S, T> {
|
|||
}
|
||||
|
||||
public MapperBuilder<S, T> addMapping(String field) {
|
||||
this.mapper.addMapping(field, field, null, null);
|
||||
this.mapper.addFieldToFieldMapping(field, field, null, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapperBuilder<S, T> addMapping(String field, Converter<?, ?> converter) {
|
||||
this.mapper.addMapping(field, field, converter, null);
|
||||
this.mapper.addFieldToFieldMapping(field, field, converter, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapperBuilder<S, T> addMapping(String field, Mapper<?, T> mapper) {
|
||||
this.mapper.addMapping(field, mapper, null);
|
||||
this.mapper.addFieldToMultiFieldMapping(field, mapper, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapperBuilder<S, T> addMapping(String sourceField, String targetField) {
|
||||
this.mapper.addMapping(sourceField, targetField, null, null);
|
||||
this.mapper.addFieldToFieldMapping(sourceField, targetField, null, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapperBuilder<S, T> addMapping(String sourceField, String targetField, Converter<?, ?> converter) {
|
||||
this.mapper.addMapping(sourceField, targetField, converter, null);
|
||||
this.mapper.addFieldToFieldMapping(sourceField, targetField, converter, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapperBuilder<S, T> addMapping(String[] fields, Mapper<S, T> mapper) {
|
||||
this.mapper.addMapping(fields, mapper, null);
|
||||
this.mapper.addMultiFieldToFieldMapping(fields, mapper, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapperBuilder<S, T> addAssemblerMapping(String field, Converter<? extends Map<?, ?>, ?> assembler) {
|
||||
this.mapper.addMultiFieldToFieldMapping(field, field, assembler, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapperBuilder<S, T> addConditionalMapping(String field, String condition) {
|
||||
this.mapper.addMapping(field, field, null, condition);
|
||||
this.mapper.addFieldToFieldMapping(field, field, null, condition);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapperBuilder<S, T> addConditionalMapping(String field, Converter<?, ?> converter, String condition) {
|
||||
this.mapper.addMapping(field, field, converter, condition);
|
||||
this.mapper.addFieldToFieldMapping(field, field, converter, condition);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapperBuilder<S, T> addConditionalMapping(String field, Mapper<?, T> mapper, String condition) {
|
||||
this.mapper.addMapping(field, mapper, condition);
|
||||
this.mapper.addFieldToMultiFieldMapping(field, mapper, condition);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapperBuilder<S, T> addConditionalMapping(String sourceField, String targetField, String condition) {
|
||||
this.mapper.addMapping(sourceField, targetField, null, condition);
|
||||
this.mapper.addFieldToFieldMapping(sourceField, targetField, null, condition);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapperBuilder<S, T> addConditionalMapping(String sourceField, String targetField, Converter<?, ?> converter,
|
||||
String condition) {
|
||||
this.mapper.addMapping(sourceField, targetField, converter, condition);
|
||||
this.mapper.addFieldToFieldMapping(sourceField, targetField, converter, condition);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapperBuilder<S, T> addMapping(String[] fields, Mapper<S, T> mapper, String condition) {
|
||||
this.mapper.addMapping(fields, mapper, condition);
|
||||
public MapperBuilder<S, T> addConditionalMapping(String[] fields, Mapper<S, T> mapper, String condition) {
|
||||
this.mapper.addMultiFieldToFieldMapping(fields, mapper, condition);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@ package org.springframework.mapping.support;
|
|||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.mapping.MappingException;
|
||||
|
|
@ -25,15 +28,19 @@ import org.springframework.mapping.MappingFailure;
|
|||
|
||||
final class SpelMappingContext {
|
||||
|
||||
private final MappableType sourceType;
|
||||
|
||||
private final EvaluationContext sourceEvaluationContext;
|
||||
|
||||
private final EvaluationContext targetEvaluationContext;
|
||||
|
||||
private final List<MappingFailure> failures = new LinkedList<MappingFailure>();
|
||||
|
||||
public SpelMappingContext(EvaluationContext sourceEvaluationContext, EvaluationContext targetEvaluationContext) {
|
||||
this.sourceEvaluationContext = sourceEvaluationContext;
|
||||
this.targetEvaluationContext = targetEvaluationContext;
|
||||
public SpelMappingContext(Object source, MappableType sourceType, Object target, MappableType targetType,
|
||||
ConversionService conversionService) {
|
||||
this.sourceType = sourceType;
|
||||
this.sourceEvaluationContext = sourceType.getEvaluationContext(source, conversionService);
|
||||
this.targetEvaluationContext = targetType.getEvaluationContext(target, conversionService);
|
||||
}
|
||||
|
||||
public Object getSource() {
|
||||
|
|
@ -55,6 +62,14 @@ final class SpelMappingContext {
|
|||
return sourceField.getValue(this.sourceEvaluationContext);
|
||||
}
|
||||
|
||||
public Set<String> getSourceFieldNames() {
|
||||
return this.sourceType.getFieldNames(getSource());
|
||||
}
|
||||
|
||||
public Map<String, Object> getSourceNestedFields(String sourceFieldName) {
|
||||
return this.sourceType.getNestedFields(sourceFieldName, getSource());
|
||||
}
|
||||
|
||||
public void setTargetFieldValue(Expression targetField, Object value) {
|
||||
targetField.setValue(this.targetEvaluationContext, value);
|
||||
}
|
||||
|
|
@ -69,4 +84,8 @@ final class SpelMappingContext {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isTargetFieldWriteable(Expression targetField) {
|
||||
return targetField.isWritable(this.targetEvaluationContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,31 +13,42 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.format;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A factory that creates {@link Formatter formatters} to format property values on properties
|
||||
* annotated with a particular format {@link Annotation}.
|
||||
* A factory that creates formatters to format values of fields annotated with a particular format {@link Annotation}.
|
||||
*
|
||||
* <p>For example, a <code>CurrencyAnnotationFormatterFactory</code> might create a <code>Formatter</code>
|
||||
* that formats a <code>BigDecimal</code> value set on a property annotated with <code>@CurrencyFormat</code>.
|
||||
* <p>For example, a <code>DateTimeFormatAnnotationFormatterFactory</code> might create a formatter
|
||||
* that formats a <code>Date</code> objects set on properties annotated with <code>@DateFormat</code>.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
* @param <A> the type of Annotation this factory uses to create Formatter instances
|
||||
* @param <T> the type of object that the factory's Formatters are dealing with
|
||||
* @param <A> the type of Annotation that should trigger property formatting
|
||||
*/
|
||||
public interface AnnotationFormatterFactory<A extends Annotation, T> {
|
||||
public interface AnnotationFormatterFactory<A extends Annotation> {
|
||||
|
||||
/**
|
||||
* Get the Formatter that will format the value of the property annotated with the provided annotation.
|
||||
* The annotation instance can contain properties that may be used to configure the Formatter that is returned.
|
||||
* @param annotation the annotation instance
|
||||
* @return the Formatter to use to format values of properties annotated with the annotation.
|
||||
* The types of fields that may be annotated with the <A> annotation.
|
||||
*/
|
||||
Formatter<T> getFormatter(A annotation);
|
||||
Set<Class<?>> getFieldTypes();
|
||||
|
||||
/**
|
||||
* Get the Printer to print the value of a property of <code>fieldType</code> annotated with <code>annotation</code>.
|
||||
* @param annotation the annotation instance
|
||||
* @param fieldType the type of property being annotated
|
||||
* @return the printer
|
||||
*/
|
||||
Printer<?> getPrinter(A annotation, Class<?> fieldType);
|
||||
|
||||
/**
|
||||
* Get the Parser to parse the printed value of a property of <code>fieldType</code> annotated with <code>annotation</code>.
|
||||
* @param annotation the annotation instance
|
||||
* @param fieldType the type of field being annotated
|
||||
* @return the parser
|
||||
*/
|
||||
Parser<?> getParser(A annotation, Class<?> fieldType);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,35 +16,14 @@
|
|||
|
||||
package org.springframework.ui.format;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Formats objects of type T for display.
|
||||
* Formats objects of type T.
|
||||
* A Formatter is both a Printer <i>and</i> a Parser for an object type.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
* @param <T> the type of object this formatter can format
|
||||
* @param <T> the type of object this Formatter formats
|
||||
*/
|
||||
public interface Formatter<T> {
|
||||
|
||||
/**
|
||||
* Format the object of type T for display.
|
||||
* @param object the object to format
|
||||
* @param locale the user's locale
|
||||
* @return the formatted display string
|
||||
*/
|
||||
String format(T object, Locale locale);
|
||||
|
||||
/**
|
||||
* Parse an object from its formatted representation.
|
||||
* @param formatted a formatted representation
|
||||
* @param locale the user's locale
|
||||
* @return the parsed object
|
||||
* @throws ParseException when a parse exception occurs
|
||||
* @throws RuntimeException when thrown by coercion methods that are
|
||||
*
|
||||
*/
|
||||
T parse(String formatted, Locale locale) throws ParseException;
|
||||
public interface Formatter<T> extends Printer<T>, Parser<T> {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,15 +13,12 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.format;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||
|
||||
/**
|
||||
* A shared registry of Formatters.
|
||||
* A registry of field formatting logic.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
|
|
@ -29,46 +26,43 @@ import org.springframework.core.convert.TypeDescriptor;
|
|||
public interface FormatterRegistry {
|
||||
|
||||
/**
|
||||
* Adds a Formatter to this registry indexed by type.
|
||||
* <p>Use this add method when type differs from <T>.
|
||||
* Calling <code>getFormatter(type)</code> returns a decorator that wraps
|
||||
* the <code>targetFormatter</code> instance.
|
||||
* <p>On format, the decorator first coerces the instance of type to <T>,
|
||||
* then delegates to <code>targetFormatter</code> to format the value.
|
||||
* <p>On parse, the decorator first delegates to the formatter to parse a <T>,
|
||||
* then coerces the parsed value to type.
|
||||
* @param type the object type
|
||||
* @param targetFormatter the target formatter
|
||||
* Adds a Formatter to format fields of a specific type.
|
||||
* The Formatter will delegate to the specified <code>printer</code> for printing and <code>parser</code> for parsing.
|
||||
* <p>
|
||||
* On print, if the Printer's type T is declared and <code>fieldType</code> is not assignable to T,
|
||||
* a coersion to T will be attempted before delegating to <code>printer</code> to print a field value.
|
||||
* On parse, if the object returned by the Parser is not assignable to the runtime field type,
|
||||
* a coersion to the field type will be attempted before returning the parsed field value.
|
||||
* @param fieldType the field type to format
|
||||
* @param formatter the formatter to add
|
||||
*/
|
||||
void addFormatterByType(Class<?> type, Formatter<?> targetFormatter);
|
||||
void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser);
|
||||
|
||||
/**
|
||||
* Adds a Formatter to this registry indexed by <T>.
|
||||
* <p>Calling <code>getFormatter(<T>.class)</code> returns <code>formatter</code>.
|
||||
* @param formatter the formatter
|
||||
* Adds a Formatter to format fields of a specific type.
|
||||
* <p>
|
||||
* On print, if the Formatter's type T is declared and <code>fieldType</code> is not assignable to T,
|
||||
* a coersion to T will be attempted before delegating to <code>formatter</code> to print a field value.
|
||||
* On parse, if the parsed object returned by <code>formatter</code> is not assignable to the runtime field type,
|
||||
* a coersion to the field type will be attempted before returning the parsed field value.
|
||||
* @param fieldType the field type to format
|
||||
* @param formatter the formatter to add
|
||||
*/
|
||||
void addFormatterByType(Formatter<?> formatter);
|
||||
void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter);
|
||||
|
||||
/**
|
||||
* Adds a Formatter to this registry indexed by the given annotation type.
|
||||
* <p>Calling <code>getFormatter(...)</code> on a field or accessor method
|
||||
* with the given annotation returns <code>formatter</code>.
|
||||
* @param formatter the formatter
|
||||
* Adds a Formatter to format fields annotated with a specific format annotation.
|
||||
* @param annotationFormatterFactory the annotation formatter factory to add
|
||||
*/
|
||||
void addFormatterByAnnotation(Class<? extends Annotation> annotationType, Formatter<?> formatter);
|
||||
void addFormatterForFieldAnnotation(AnnotationFormatterFactory<?> annotationFormatterFactory);
|
||||
|
||||
/**
|
||||
* Adds a AnnotationFormatterFactory that returns the Formatter for properties annotated with a specific annotation.
|
||||
* <p>Calling <code>getFormatter(...)</code> on a field or accessor method
|
||||
* with the given annotation returns <code>formatter</code>.
|
||||
* @param factory the annotation formatter factory
|
||||
* Returns the registry of Converters that coerse field values to types required by Formatters.
|
||||
* Allows clients to register their own custom converters.
|
||||
* For example, a date/time formatting configuration might expect a java.util.Date field value to be converted to a Long for formatting.
|
||||
* Registering a simpler DateToLongConverter allievates the need to register multiple formatters for closely related types.
|
||||
* @return the converter registry, allowing new Converters to be registered
|
||||
*/
|
||||
void addFormatterByAnnotation(AnnotationFormatterFactory<?, ?> factory);
|
||||
|
||||
/**
|
||||
* Get the Formatter for the type descriptor.
|
||||
* @return the Formatter, or <code>null</code> if no suitable one is registered
|
||||
*/
|
||||
Formatter<Object> getFormatter(TypeDescriptor type);
|
||||
ConverterRegistry getConverterRegistry();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ui.format;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
|
||||
/**
|
||||
* A service interface for formatting localized field values.
|
||||
* This is the entry point into the <code>ui.format</code> system.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface FormattingService {
|
||||
|
||||
/**
|
||||
* Print the field value for display in the locale.
|
||||
* @param fieldValue the field value
|
||||
* @param fieldType the field type
|
||||
* @param locale the user's locale
|
||||
* @return the printed string
|
||||
*/
|
||||
String print(Object fieldValue, TypeDescriptor fieldType, Locale locale);
|
||||
|
||||
/**
|
||||
* Parse the the value submitted by the user.
|
||||
* @param submittedValue the submitted field value
|
||||
* @param fieldType the field type
|
||||
* @param locale the user's locale
|
||||
* @return the parsed field value
|
||||
*/
|
||||
Object parse(String submittedValue, TypeDescriptor fieldType, Locale locale) throws ParseException;
|
||||
|
||||
}
|
||||
|
|
@ -13,23 +13,29 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
package org.springframework.ui.format;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import java.text.ParseException;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Converts from a java.util.Date to a java.util.Calendar.
|
||||
* Parses objects of type T from their printed representations.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
* @param <T> the type of object this Parser parses
|
||||
*/
|
||||
final class DateToCalendarConverter implements Converter<Date, Calendar> {
|
||||
public interface Parser<T> {
|
||||
|
||||
public Calendar convert(Date source) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(source);
|
||||
return cal;
|
||||
}
|
||||
/**
|
||||
* Parse an object from its printed representation.
|
||||
* @param printed a printed representation
|
||||
* @param locale the current user locale
|
||||
* @return the parsed object
|
||||
* @throws ParseException when a parse exception occurs in a java.text parsing library
|
||||
* @throws RuntimeException when a parse exception occurs
|
||||
*/
|
||||
T parse(String printed, Locale locale) throws ParseException;
|
||||
|
||||
}
|
||||
|
|
@ -13,21 +13,26 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
package org.springframework.ui.format;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Converts from a java.util.Calendar to a java.util.Date.
|
||||
* Prints objects of type T for display.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
* @param <T> the type of object this Printer prints
|
||||
*/
|
||||
final class CalendarToDateConverter implements Converter<Calendar, Date> {
|
||||
public interface Printer<T> {
|
||||
|
||||
public Date convert(Calendar source) {
|
||||
return source.getTime();
|
||||
}
|
||||
/**
|
||||
* Print the object of type T for display.
|
||||
* @param object the object to print
|
||||
* @param locale the current user locale
|
||||
* @return the printed string
|
||||
*/
|
||||
String print(T object, Locale locale);
|
||||
|
||||
}
|
||||
|
|
@ -97,17 +97,11 @@ public class DateFormatter implements Formatter<Date> {
|
|||
}
|
||||
|
||||
|
||||
public String format(Date date, Locale locale) {
|
||||
if (date == null) {
|
||||
return "";
|
||||
}
|
||||
public String print(Date date, Locale locale) {
|
||||
return getDateFormat(locale).format(date);
|
||||
}
|
||||
|
||||
public Date parse(String formatted, Locale locale) throws ParseException {
|
||||
if (formatted.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
return getDateFormat(locale).parse(formatted);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ui.format.jodatime;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.ReadableInstant;
|
||||
import org.joda.time.ReadablePartial;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.springframework.ui.format.AnnotationFormatterFactory;
|
||||
import org.springframework.ui.format.Parser;
|
||||
import org.springframework.ui.format.Printer;
|
||||
|
||||
/**
|
||||
* Base class for annotation-based Joda DateTime formatters.
|
||||
* Can format any Joda {@link ReadableInstant} or {@link ReadablePartial} property, as well as standard {@link Date}, {@link Calendar}, and Long properties.
|
||||
* @author Keith Donald
|
||||
* @param <A> the format annotation parameter type to be declared by subclasses
|
||||
*/
|
||||
abstract class AbstractDateTimeAnnotationFormatterFactory<A extends Annotation> implements AnnotationFormatterFactory<A> {
|
||||
|
||||
private final Set<Class<?>> propertyTypes;
|
||||
|
||||
public AbstractDateTimeAnnotationFormatterFactory() {
|
||||
this.propertyTypes = Collections.unmodifiableSet(createPropertyTypes());
|
||||
}
|
||||
|
||||
public Set<Class<?>> getFieldTypes() {
|
||||
return propertyTypes;
|
||||
}
|
||||
|
||||
public Printer<?> getPrinter(A annotation, Class<?> propertyType) {
|
||||
DateTimeFormatter formatter = configureDateTimeFormatterFrom(annotation);
|
||||
if (ReadableInstant.class.isAssignableFrom(propertyType)) {
|
||||
return new ReadableInstantPrinter(formatter);
|
||||
} else if (ReadablePartial.class.equals(propertyType)) {
|
||||
return new ReadablePartialPrinter(formatter);
|
||||
} else if (Calendar.class.isAssignableFrom(propertyType)) {
|
||||
// assumes Calendar->ReadableInstant converter is registered
|
||||
return new ReadableInstantPrinter(formatter);
|
||||
} else {
|
||||
// assumes Date->Long converter is registered
|
||||
return new MillisecondInstantPrinter(formatter);
|
||||
}
|
||||
}
|
||||
|
||||
public Parser<DateTime> getParser(A annotation, Class<?> propertyType) {
|
||||
return new DateTimeParser(configureDateTimeFormatterFrom(annotation));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook method subclasses should override to configure the Joda {@link DateTimeFormatter} from the <code>annotation</code> values.
|
||||
* @param annotation the annotation containing formatter configuration rules
|
||||
* @return the configured date time formatter
|
||||
*/
|
||||
protected abstract DateTimeFormatter configureDateTimeFormatterFrom(A annotation);
|
||||
|
||||
// internal helpers
|
||||
|
||||
private Set<Class<?>> createPropertyTypes() {
|
||||
Set<Class<?>> propertyTypes = new HashSet<Class<?>>();
|
||||
propertyTypes.add(ReadableInstant.class);
|
||||
propertyTypes.add(ReadablePartial.class);
|
||||
propertyTypes.add(Date.class);
|
||||
propertyTypes.add(Calendar.class);
|
||||
propertyTypes.add(Long.class);
|
||||
return propertyTypes;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ui.format.jodatime;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates a property should be formatted as a date time.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
@Target( { ElementType.METHOD, ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DateTimeFormat {
|
||||
|
||||
/**
|
||||
* The style to use for formatting the date portion of the property.
|
||||
* Defaults to {@link FormatStyle#NONE}.
|
||||
*/
|
||||
FormatStyle dateStyle() default FormatStyle.NONE;
|
||||
|
||||
/**
|
||||
* The style to use for formatting the time portion of the property.
|
||||
* Defaults to {@link FormatStyle#NONE}.
|
||||
*/
|
||||
FormatStyle timeStyle() default FormatStyle.NONE;
|
||||
|
||||
/**
|
||||
* A pattern String that defines a custom format for the property.
|
||||
* Use this method or the <code>*Style</code> methods, not both.
|
||||
*/
|
||||
String pattern() default "";
|
||||
|
||||
/**
|
||||
* Supported DateTimeFormat styles.
|
||||
*/
|
||||
public enum FormatStyle {
|
||||
SHORT {
|
||||
public String toString() {
|
||||
return "S";
|
||||
}
|
||||
},
|
||||
MEDIUM {
|
||||
public String toString() {
|
||||
return "M";
|
||||
}
|
||||
},
|
||||
LONG {
|
||||
public String toString() {
|
||||
return "L";
|
||||
}
|
||||
},
|
||||
FULL {
|
||||
public String toString() {
|
||||
return "F";
|
||||
}
|
||||
},
|
||||
NONE {
|
||||
public String toString() {
|
||||
return "-";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ui.format.jodatime;
|
||||
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.springframework.ui.format.jodatime.DateTimeFormat.FormatStyle;
|
||||
|
||||
/**
|
||||
* Formats properties annotated with the {@link DateTimeFormat} annotation.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
* @see DateTimeFormat
|
||||
*/
|
||||
public final class DateTimeFormatAnnotationFormatterFactory extends AbstractDateTimeAnnotationFormatterFactory<DateTimeFormat> {
|
||||
|
||||
protected DateTimeFormatter configureDateTimeFormatterFrom(DateTimeFormat annotation) {
|
||||
String pattern = annotation.pattern();
|
||||
if (!pattern.isEmpty()) {
|
||||
return forPattern(pattern);
|
||||
} else {
|
||||
FormatStyle dateStyle = annotation.dateStyle();
|
||||
FormatStyle timeStyle = annotation.timeStyle();
|
||||
return forDateTimeStyle(dateStyle, timeStyle);
|
||||
}
|
||||
}
|
||||
|
||||
private DateTimeFormatter forDateTimeStyle(FormatStyle dateStyle, FormatStyle timeStyle) {
|
||||
return org.joda.time.format.DateTimeFormat.forStyle(dateStyle.toString() + timeStyle.toString());
|
||||
}
|
||||
|
||||
private DateTimeFormatter forPattern(String pattern) {
|
||||
return org.joda.time.format.DateTimeFormat.forPattern(pattern);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ui.format.jodatime;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.springframework.ui.format.Formatter;
|
||||
|
||||
/**
|
||||
* {@link Formatter} implementation to handle JodaTime {@link DateTime} instances.
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class DateTimeFormatter implements Formatter<DateTime> {
|
||||
|
||||
private org.joda.time.format.DateTimeFormatter formatter;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DateTimeFormatter} for the given JodaTime formatting pattern.
|
||||
* @param pattern
|
||||
* @see DateTimeFormat#forPattern(String)
|
||||
*/
|
||||
public DateTimeFormatter(String pattern) {
|
||||
this.formatter = DateTimeFormat.forPattern(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link DateTimeFormatter} for the given JodaTime formatter.
|
||||
* @param formatter the Date Formatter instance
|
||||
*/
|
||||
public DateTimeFormatter(org.joda.time.format.DateTimeFormatter formatter) {
|
||||
this.formatter = formatter;
|
||||
}
|
||||
|
||||
public String format(DateTime object, Locale locale) {
|
||||
return null == object ? "" : object.toString(formatter);
|
||||
}
|
||||
|
||||
public DateTime parse(String formatted, Locale locale) throws ParseException {
|
||||
return formatter.withLocale(locale).parseDateTime(formatted);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,35 +13,32 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.format.jodatime;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.junit.Test;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.springframework.ui.format.Parser;
|
||||
|
||||
/**
|
||||
* Parses Joda Time {@link DateTime} instances using a {@link DateTimeFormatter}.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class DateTimeFormatterTests {
|
||||
public final class DateTimeParser implements Parser<DateTime> {
|
||||
|
||||
private DateTimeFormatter formatter = new DateTimeFormatter("yyyy-MM-dd");
|
||||
private final DateTimeFormatter formatter;
|
||||
|
||||
@Test
|
||||
public void formatValue() {
|
||||
DateTime dateTime = DateTimeFormat.forPattern("yyyy-MM-dd").parseDateTime("2009-06-01");
|
||||
assertEquals("2009-06-01", formatter.format(dateTime, Locale.US));
|
||||
/**
|
||||
* Creates a new DateTimeParser.
|
||||
* @param formatter the Joda DateTimeFormatter instance
|
||||
*/
|
||||
public DateTimeParser(DateTimeFormatter formatter) {
|
||||
this.formatter = formatter;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseValue() throws ParseException {
|
||||
DateTime dateTime = DateTimeFormat.forPattern("yyyy-MM-dd").parseDateTime("2009-06-01");
|
||||
assertEquals(dateTime, formatter.parse("2009-06-01", Locale.US));
|
||||
public DateTime parse(String printed, Locale locale) throws ParseException {
|
||||
return JodaTimeContextHolder.getFormatter(this.formatter, locale).parseDateTime(printed);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -13,29 +13,25 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ui.format.jodatime;
|
||||
|
||||
package org.springframework.ui.format;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* A type that can be formatted as a String for display in a user interface.
|
||||
*
|
||||
* Indicates a property should be formatted as a ISO date time.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Target( { ElementType.METHOD, ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Formatted {
|
||||
public @interface ISODateTimeFormat {
|
||||
|
||||
/**
|
||||
* The Formatter that handles the formatting for the annotated element.
|
||||
*/
|
||||
Class<? extends Formatter> value();
|
||||
FormatStyle value();
|
||||
|
||||
public enum FormatStyle {
|
||||
DATE, TIME, DATE_TIME
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ui.format.jodatime;
|
||||
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.springframework.ui.format.jodatime.ISODateTimeFormat.FormatStyle;
|
||||
|
||||
/**
|
||||
* Formats properties annotated with the {@link ISODateTimeFormat} annotation.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
* @see ISODateTimeFormat
|
||||
*/
|
||||
public final class ISODateTimeFormatAnnotationFormatterFactory extends AbstractDateTimeAnnotationFormatterFactory<ISODateTimeFormat> {
|
||||
|
||||
protected DateTimeFormatter configureDateTimeFormatterFrom(ISODateTimeFormat annotation) {
|
||||
FormatStyle style = annotation.value();
|
||||
if (style == FormatStyle.DATE) {
|
||||
return org.joda.time.format.ISODateTimeFormat.date();
|
||||
} else if (style == FormatStyle.TIME) {
|
||||
return org.joda.time.format.ISODateTimeFormat.time();
|
||||
} else {
|
||||
return org.joda.time.format.ISODateTimeFormat.dateTime();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ui.format.jodatime;
|
||||
|
||||
import org.joda.time.Chronology;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* A context that holds user-specific Joda Time settings such as the user's Chronology (calendar system) and time zone.
|
||||
* A <code>null</code> property value indicate the user has not specified a setting.
|
||||
* @author Keith Donald
|
||||
* @see JodaTimeContextHolder
|
||||
*/
|
||||
public class JodaTimeContext {
|
||||
|
||||
private Chronology chronology;
|
||||
|
||||
private DateTimeZone timeZone;
|
||||
|
||||
/**
|
||||
* The user's chronology (calendar system).
|
||||
* Null if not specified.
|
||||
*/
|
||||
public Chronology getChronology() {
|
||||
return chronology;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user's chronology.
|
||||
*/
|
||||
public void setChronology(Chronology chronology) {
|
||||
this.chronology = chronology;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user's timezone.
|
||||
* Null if not specified.
|
||||
*/
|
||||
public DateTimeZone getTimeZone() {
|
||||
return timeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user's timezone.
|
||||
*/
|
||||
public void setTimeZone(DateTimeZone timeZone) {
|
||||
this.timeZone = timeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Formatter with the this context's settings applied to the base <code>formatter</code>.
|
||||
* @param formatter the base formatter that establishes default formatting rules, generally context independent
|
||||
* @return the context DateTimeFormatter
|
||||
*/
|
||||
public DateTimeFormatter getFormatter(DateTimeFormatter formatter) {
|
||||
if (this.chronology != null) {
|
||||
formatter = formatter.withChronology(this.chronology);
|
||||
}
|
||||
if (this.timeZone != null) {
|
||||
formatter = formatter.withZone(this.timeZone);
|
||||
}
|
||||
return formatter;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ui.format.jodatime;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.springframework.core.NamedInheritableThreadLocal;
|
||||
|
||||
/**
|
||||
* A holder for a thread-local user {@link JodaTimeContext}.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public final class JodaTimeContextHolder {
|
||||
|
||||
private static final ThreadLocal<JodaTimeContext> jodaTimeContextHolder = new NamedInheritableThreadLocal<JodaTimeContext>(
|
||||
"Joda Time Context");
|
||||
|
||||
/**
|
||||
* Return the JodaTimeContext associated with the current thread, if any.
|
||||
* @return the current JodaTimeContext, or <code>null</code> if none
|
||||
*/
|
||||
public static JodaTimeContext getJodaTimeContext() {
|
||||
return jodaTimeContextHolder.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate the given JodaTimeContext with the current thread.
|
||||
* @param context the current JodaTimeContext, or <code>null</code> to clear
|
||||
* the thread-bound context
|
||||
*/
|
||||
public static void setLocaleContext(JodaTimeContext context) {
|
||||
jodaTimeContextHolder.set(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Formatter with the user-specific settings applied to thefrom the base <code>formatter</code>.
|
||||
* @param formatter the base formatter that establishes default formatting rules, generally user independent
|
||||
* @param locale the current user locale (may be null if not known)
|
||||
* @return the user's DateTimeFormatter
|
||||
*/
|
||||
public static DateTimeFormatter getFormatter(DateTimeFormatter formatter, Locale locale) {
|
||||
if (locale != null) {
|
||||
formatter = formatter.withLocale(locale);
|
||||
}
|
||||
JodaTimeContext context = getJodaTimeContext();
|
||||
return context != null ? context.getFormatter(formatter) : formatter;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ui.format.jodatime;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
import org.joda.time.DateMidnight;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.LocalDateTime;
|
||||
import org.joda.time.LocalTime;
|
||||
import org.joda.time.ReadableInstant;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||
|
||||
/**
|
||||
* Installs lower-level type converters required to integrate Joda Time support into Spring's formatting and property binding systems.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
final class JodaTimeConverters {
|
||||
|
||||
private JodaTimeConverters() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the converters into the converter registry.
|
||||
* @param registry the converter registry
|
||||
*/
|
||||
public static void registerConverters(ConverterRegistry registry) {
|
||||
registry.addConverter(new DateTimeToLocalDateConverter());
|
||||
registry.addConverter(new DateTimeToLocalTimeConverter());
|
||||
registry.addConverter(new DateTimeToLocalDateTimeConverter());
|
||||
registry.addConverter(new DateTimeToDateMidnightConverter());
|
||||
registry.addConverter(new DateTimeToDateConverter());
|
||||
registry.addConverter(new DateTimeToCalendarConverter());
|
||||
registry.addConverter(new DateToLongConverter());
|
||||
registry.addConverter(new CalendarToDateTimeConverter());
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
// used when binding a parsed DateTime to a LocalDate property
|
||||
private static class DateTimeToLocalDateConverter implements Converter<DateTime, LocalDate> {
|
||||
public LocalDate convert(DateTime source) {
|
||||
return source.toLocalDate();
|
||||
}
|
||||
}
|
||||
|
||||
// used when binding a parsed DateTime to a LocalTime property
|
||||
private static class DateTimeToLocalTimeConverter implements Converter<DateTime, LocalTime> {
|
||||
public LocalTime convert(DateTime source) {
|
||||
return source.toLocalTime();
|
||||
}
|
||||
}
|
||||
|
||||
// used when binding a parsed DateTime to a LocalDateTime property
|
||||
private static class DateTimeToLocalDateTimeConverter implements Converter<DateTime, LocalDateTime> {
|
||||
public LocalDateTime convert(DateTime source) {
|
||||
return source.toLocalDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
// used when binding a parsed DateTime to a DateMidnight property
|
||||
private static class DateTimeToDateMidnightConverter implements Converter<DateTime, DateMidnight> {
|
||||
public DateMidnight convert(DateTime source) {
|
||||
return source.toDateMidnight();
|
||||
}
|
||||
}
|
||||
|
||||
// used when binding a parsed DateTime to a java.util.Date property
|
||||
private static class DateTimeToDateConverter implements Converter<DateTime, Date> {
|
||||
public Date convert(DateTime source) {
|
||||
return source.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
// used when binding a parsed DateTime to a java.util.Calendar property
|
||||
private static class DateTimeToCalendarConverter implements Converter<DateTime, Calendar> {
|
||||
public Calendar convert(DateTime source) {
|
||||
return source.toGregorianCalendar();
|
||||
}
|
||||
}
|
||||
|
||||
// used when formatting a java.util.Date property with a MillisecondInstantPrinter
|
||||
private static class DateToLongConverter implements Converter<Date, Long> {
|
||||
public Long convert(Date source) {
|
||||
return source.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
// used when formatting a java.util.Calendar property with a ReadableInstantPrinter
|
||||
private static class CalendarToDateTimeConverter implements Converter<Calendar, ReadableInstant> {
|
||||
public ReadableInstant convert(Calendar source) {
|
||||
return new DateTime(source);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ui.format.jodatime;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.LocalDateTime;
|
||||
import org.joda.time.LocalTime;
|
||||
import org.joda.time.ReadableInstant;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
import org.springframework.ui.format.FormatterRegistry;
|
||||
import org.springframework.ui.format.Parser;
|
||||
import org.springframework.ui.format.Printer;
|
||||
|
||||
/**
|
||||
* Configures Joda Time's Formatting system for use with Spring.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class JodaTimeFormattingConfigurer {
|
||||
|
||||
private FormatterRegistry formatterRegistry;
|
||||
|
||||
private String dateStyle;
|
||||
|
||||
private String timeStyle;
|
||||
|
||||
private String dateTimeStyle;
|
||||
|
||||
private boolean useISOFormat;
|
||||
|
||||
/**
|
||||
* Creates a new JodaTimeFormattingConfigurer that installs into the provided FormatterRegistry.
|
||||
* Call {@link #registerJodaTimeFormatting()} to install.
|
||||
* @param formatterRegistry the registry to register Joda Time formatters with
|
||||
*/
|
||||
public JodaTimeFormattingConfigurer(FormatterRegistry formatterRegistry) {
|
||||
this.formatterRegistry = formatterRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default format style of Joda {@link LocalDate} objects.
|
||||
* Default is {@link DateTimeFormat#shortDate()}.
|
||||
* @param dateStyle the date format style
|
||||
*/
|
||||
public void setDateStyle(String dateStyle) {
|
||||
this.dateStyle = dateStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default format style of Joda {@link LocalTime} objects.
|
||||
* Default is {@link DateTimeFormat#shortTime()}.
|
||||
* @param timeStyle the time format style
|
||||
*/
|
||||
public void setTimeStyle(String timeStyle) {
|
||||
this.timeStyle = timeStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default format style of Joda {@link LocalDateTime} and {@link DateTime} objects, as well as JDK {@link Date} and {@link Calendar} objects.
|
||||
* Default is {@link DateTimeFormat#shortDateTime()}.
|
||||
* @param dateTimeStyle the date time format style
|
||||
*/
|
||||
public void setDateTimeStyle(String dateTimeStyle) {
|
||||
this.dateTimeStyle = dateTimeStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether standard ISO formatting should be applied to all Date/Time types.
|
||||
* Default is false (no).
|
||||
* If set to true, the dateStyle, timeStyle, and dateTimeStyle properties are ignored.
|
||||
* @param useISOFormat true to enable ISO formatting
|
||||
*/
|
||||
public void setUseISOFormat(boolean useISOFormat) {
|
||||
this.useISOFormat = useISOFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install Joda Time formatters given the current configuration of this {@link JodaTimeFormattingConfigurer}.
|
||||
*/
|
||||
public void registerJodaTimeFormatting() {
|
||||
JodaTimeConverters.registerConverters(this.formatterRegistry.getConverterRegistry());
|
||||
|
||||
DateTimeFormatter jodaDateFormatter = getJodaDateFormatter();
|
||||
this.formatterRegistry.addFormatterForFieldType(LocalDate.class, new ReadablePartialPrinter(jodaDateFormatter), new DateTimeParser(jodaDateFormatter));
|
||||
|
||||
DateTimeFormatter jodaTimeFormatter = getJodaTimeFormatter();
|
||||
this.formatterRegistry.addFormatterForFieldType(LocalTime.class, new ReadablePartialPrinter(jodaTimeFormatter), new DateTimeParser(jodaTimeFormatter));
|
||||
|
||||
DateTimeFormatter jodaDateTimeFormatter = getJodaDateTimeFormatter();
|
||||
Parser<DateTime> dateTimeParser = new DateTimeParser(jodaDateTimeFormatter);
|
||||
this.formatterRegistry.addFormatterForFieldType(LocalDateTime.class, new ReadablePartialPrinter(jodaDateTimeFormatter), dateTimeParser);
|
||||
|
||||
Printer<ReadableInstant> readableInstantPrinter = new ReadableInstantPrinter(jodaDateTimeFormatter);
|
||||
this.formatterRegistry.addFormatterForFieldType(ReadableInstant.class, readableInstantPrinter, dateTimeParser);
|
||||
this.formatterRegistry.addFormatterForFieldType(Calendar.class, readableInstantPrinter, dateTimeParser);
|
||||
this.formatterRegistry.addFormatterForFieldType(Calendar.class, new MillisecondInstantPrinter(jodaDateTimeFormatter), dateTimeParser);
|
||||
|
||||
this.formatterRegistry.addFormatterForFieldAnnotation(new DateTimeFormatAnnotationFormatterFactory());
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private DateTimeFormatter getJodaDateFormatter() {
|
||||
if (this.useISOFormat) {
|
||||
return ISODateTimeFormat.date();
|
||||
}
|
||||
if (this.dateStyle != null) {
|
||||
return DateTimeFormat.forStyle(this.dateStyle + "-");
|
||||
} else {
|
||||
return DateTimeFormat.shortDate();
|
||||
}
|
||||
}
|
||||
|
||||
private DateTimeFormatter getJodaTimeFormatter() {
|
||||
if (this.useISOFormat) {
|
||||
return ISODateTimeFormat.time();
|
||||
}
|
||||
if (this.timeStyle != null) {
|
||||
return DateTimeFormat.forStyle("-" + this.timeStyle);
|
||||
} else {
|
||||
return DateTimeFormat.shortTime();
|
||||
}
|
||||
}
|
||||
|
||||
private DateTimeFormatter getJodaDateTimeFormatter() {
|
||||
if (this.useISOFormat) {
|
||||
return ISODateTimeFormat.dateTime();
|
||||
}
|
||||
if (this.dateTimeStyle != null) {
|
||||
return DateTimeFormat.forStyle(this.dateTimeStyle);
|
||||
} else {
|
||||
return DateTimeFormat.shortDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ui.format.jodatime;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.springframework.ui.format.Printer;
|
||||
|
||||
/**
|
||||
* Prints Long instances using a {@link DateTimeFormatter}.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public final class MillisecondInstantPrinter implements Printer<Long> {
|
||||
|
||||
private final DateTimeFormatter formatter;
|
||||
|
||||
/**
|
||||
* Creates a new ReadableInstantPrinter.
|
||||
* @param formatter the Joda DateTimeFormatter instance
|
||||
*/
|
||||
public MillisecondInstantPrinter(DateTimeFormatter formatter) {
|
||||
this.formatter = formatter;
|
||||
}
|
||||
|
||||
public String print(Long instant, Locale locale) {
|
||||
return JodaTimeContextHolder.getFormatter(this.formatter, locale).print(instant);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ui.format.jodatime;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.joda.time.ReadableInstant;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.springframework.ui.format.Printer;
|
||||
|
||||
/**
|
||||
* Prints Joda Time {@link ReadableInstant} instances using a {@link DateTimeFormatter}.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public final class ReadableInstantPrinter implements Printer<ReadableInstant> {
|
||||
|
||||
private final DateTimeFormatter formatter;
|
||||
|
||||
/**
|
||||
* Creates a new ReadableInstantPrinter.
|
||||
* @param formatter the Joda DateTimeFormatter instance
|
||||
*/
|
||||
public ReadableInstantPrinter(DateTimeFormatter formatter) {
|
||||
this.formatter = formatter;
|
||||
}
|
||||
|
||||
public String print(ReadableInstant instant, Locale locale) {
|
||||
return JodaTimeContextHolder.getFormatter(this.formatter, locale).print(instant);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ui.format.jodatime;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.joda.time.ReadablePartial;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.springframework.ui.format.Printer;
|
||||
|
||||
/**
|
||||
* Prints Joda Time {@link ReadablePartial} instances using a {@link DateTimeFormatter}.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public final class ReadablePartialPrinter implements Printer<ReadablePartial> {
|
||||
|
||||
private final DateTimeFormatter formatter;
|
||||
|
||||
/**
|
||||
* Creates a new ReadableInstantPrinter.
|
||||
* @param formatter the Joda DateTimeFormatter instance
|
||||
*/
|
||||
public ReadablePartialPrinter(DateTimeFormatter formatter) {
|
||||
this.formatter = formatter;
|
||||
}
|
||||
|
||||
public String print(ReadablePartial partial, Locale locale) {
|
||||
return JodaTimeContextHolder.getFormatter(this.formatter, locale).print(partial);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Integration with the Joda Time for formatting Joda types as well as standard JDK Date types.
|
||||
*/
|
||||
package org.springframework.ui.format.jodatime;
|
||||
|
|
@ -46,18 +46,11 @@ public abstract class AbstractNumberFormatter implements Formatter<Number> {
|
|||
}
|
||||
|
||||
|
||||
public String format(Number integer, Locale locale) {
|
||||
if (integer == null) {
|
||||
return "";
|
||||
}
|
||||
NumberFormat format = getNumberFormat(locale);
|
||||
return format.format(integer);
|
||||
public String print(Number integer, Locale locale) {
|
||||
return getNumberFormat(locale).format(integer);
|
||||
}
|
||||
|
||||
public Number parse(String formatted, Locale locale) throws ParseException {
|
||||
if (formatted.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
NumberFormat format = getNumberFormat(locale);
|
||||
ParsePosition position = new ParsePosition(0);
|
||||
Number number = format.parse(formatted, position);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
* A SPI for defining Formatters to format field model values for display in a UI.
|
||||
* An API for defining Formatters to format field model values for display in a UI.
|
||||
*/
|
||||
package org.springframework.ui.format;
|
||||
|
|
|
|||
|
|
@ -19,79 +19,40 @@ package org.springframework.ui.format.support;
|
|||
import java.text.ParseException;
|
||||
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.core.convert.support.GenericConverter;
|
||||
import org.springframework.ui.format.Formatter;
|
||||
import org.springframework.ui.format.FormatterRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.ui.format.FormattingService;
|
||||
|
||||
/**
|
||||
* Adapter that exposes a {@link ConversionService} reference for a given
|
||||
* {@link org.springframework.ui.format.FormatterRegistry}, retrieving the current
|
||||
* Locale from {@link org.springframework.context.i18n.LocaleContextHolder}.
|
||||
* Adapter that exposes a {@link ConversionService} reference for a given {@link FormattingService},
|
||||
* retrieving the current Locale from {@link LocaleContextHolder}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class FormattingConversionServiceAdapter extends GenericConversionService {
|
||||
|
||||
private final FormatterRegistry formatterRegistry;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new FormattingConversionServiceAdapter for the given FormatterRegistry.
|
||||
* @param formatterRegistry the FormatterRegistry to wrap
|
||||
*/
|
||||
public FormattingConversionServiceAdapter(FormatterRegistry formatterRegistry) {
|
||||
Assert.notNull(formatterRegistry, "FormatterRegistry must not be null");
|
||||
this.formatterRegistry = formatterRegistry;
|
||||
if (formatterRegistry instanceof GenericFormatterRegistry) {
|
||||
setParent(((GenericFormatterRegistry) formatterRegistry).getConversionService());
|
||||
}
|
||||
else {
|
||||
setParent(new DefaultConversionService());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
if (String.class.equals(sourceType.getType())) {
|
||||
Formatter formatter = this.formatterRegistry.getFormatter(targetType);
|
||||
if (formatter != null) {
|
||||
return new FormattingConverter(formatter);
|
||||
}
|
||||
}
|
||||
return super.getConverter(sourceType, targetType);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adapter that exposes the Converter interface on top of a given Formatter.
|
||||
*/
|
||||
private static class FormattingConverter implements GenericConverter {
|
||||
|
||||
private final Formatter formatter;
|
||||
|
||||
public FormattingConverter(Formatter formatter) {
|
||||
this.formatter = formatter;
|
||||
}
|
||||
private final FormattingService formattingService;
|
||||
|
||||
public FormattingConversionServiceAdapter(FormattingService formattingService) {
|
||||
this.formattingService = formattingService;
|
||||
addGenericConverter(String.class, Object.class, new GenericConverter() {
|
||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return this.formatter.parse((String) source, LocaleContextHolder.getLocale());
|
||||
}
|
||||
catch (ParseException ex) {
|
||||
throw new IllegalArgumentException("Could not convert formatted value '" + source + "'", ex);
|
||||
return FormattingConversionServiceAdapter.this.formattingService.parse((String) source, targetType, LocaleContextHolder.getLocale());
|
||||
} catch (ParseException e) {
|
||||
throw new ConversionFailedException(sourceType, targetType, source, e);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
addGenericConverter(Object.class, String.class, new GenericConverter() {
|
||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return FormattingConversionServiceAdapter.this.formattingService.print(source, targetType, LocaleContextHolder.getLocale());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ import java.beans.PropertyEditorSupport;
|
|||
import java.text.ParseException;
|
||||
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.ui.format.Formatter;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.ui.format.FormattingService;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
|
@ -33,23 +34,26 @@ import org.springframework.util.Assert;
|
|||
*/
|
||||
public class FormattingPropertyEditorAdapter extends PropertyEditorSupport {
|
||||
|
||||
private final Formatter<Object> formatter;
|
||||
private final FormattingService formattingService;
|
||||
|
||||
private final TypeDescriptor fieldType;
|
||||
|
||||
/**
|
||||
* Create a new FormattingPropertyEditorAdapter for the given Formatter.
|
||||
* @param formatter the Formatter to wrap
|
||||
*/
|
||||
public FormattingPropertyEditorAdapter(Formatter<Object> formatter) {
|
||||
Assert.notNull(formatter, "Formatter must not be null");
|
||||
this.formatter = formatter;
|
||||
public FormattingPropertyEditorAdapter(FormattingService formattingService, Class<?> fieldType) {
|
||||
Assert.notNull(formattingService, "FormattingService must not be null");
|
||||
Assert.notNull(formattingService, "FieldType must not be null");
|
||||
this.formattingService = formattingService;
|
||||
this.fieldType = TypeDescriptor.valueOf(fieldType);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setAsText(String text) throws IllegalArgumentException {
|
||||
try {
|
||||
setValue(this.formatter.parse(text, LocaleContextHolder.getLocale()));
|
||||
setValue(this.formattingService.parse(text, this.fieldType, LocaleContextHolder.getLocale()));
|
||||
}
|
||||
catch (ParseException ex) {
|
||||
throw new IllegalArgumentException("Failed to parse formatted value", ex);
|
||||
|
|
@ -58,7 +62,7 @@ public class FormattingPropertyEditorAdapter extends PropertyEditorSupport {
|
|||
|
||||
@Override
|
||||
public String getAsText() {
|
||||
return this.formatter.format(getValue(), LocaleContextHolder.getLocale());
|
||||
return this.formattingService.print(getValue(), this.fieldType, LocaleContextHolder.getLocale());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,406 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.format.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.text.ParseException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.ui.format.AnnotationFormatterFactory;
|
||||
import org.springframework.ui.format.Formatted;
|
||||
import org.springframework.ui.format.Formatter;
|
||||
import org.springframework.ui.format.FormatterRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A generic implementation of {@link org.springframework.ui.format.FormatterRegistry}
|
||||
* suitable for use in most environments.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
* @see #setFormatters(Set)
|
||||
* @see #setFormatterMap(Map)
|
||||
* @see #setAnnotationFormatterMap(Map)
|
||||
* @see #setAnnotationFormatterFactories(Set)
|
||||
* @see #setConversionService(ConversionService)
|
||||
* @see #addFormatterByType(Formatter)
|
||||
* @see #addFormatterByType(Class, Formatter)
|
||||
* @see #addFormatterByAnnotation(Class, Formatter)
|
||||
* @see #addFormatterByAnnotation(AnnotationFormatterFactory)
|
||||
*/
|
||||
public class GenericFormatterRegistry implements FormatterRegistry, ApplicationContextAware, Cloneable {
|
||||
|
||||
private final Map<Class<?>, FormatterHolder> typeFormatters = new ConcurrentHashMap<Class<?>, FormatterHolder>();
|
||||
|
||||
private final Map<Class<?>, AnnotationFormatterFactoryHolder> annotationFormatters = new ConcurrentHashMap<Class<?>, AnnotationFormatterFactoryHolder>();
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private boolean shared = true;
|
||||
|
||||
/**
|
||||
* Registers the formatters in the set provided.
|
||||
* JavaBean-friendly alternative to calling {@link #addFormatterByType(Formatter)}.
|
||||
* @see #add(Formatter)
|
||||
*/
|
||||
public void setFormatters(Set<Formatter<?>> formatters) {
|
||||
for (Formatter<?> formatter : formatters) {
|
||||
addFormatterByType(formatter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the formatters in the map provided by type.
|
||||
* JavaBean-friendly alternative to calling {@link #addFormatterByType(Class, Formatter)}.
|
||||
* @see #add(Class, Formatter)
|
||||
*/
|
||||
public void setFormatterMap(Map<Class<?>, Formatter<?>> formatters) {
|
||||
for (Map.Entry<Class<?>, Formatter<?>> entry : formatters.entrySet()) {
|
||||
addFormatterByType(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the formatters in the map provided by annotation type.
|
||||
* JavaBean-friendly alternative to calling {@link #addFormatterByAnnotation(Class, Formatter)}.
|
||||
* @see #add(Class, Formatter)
|
||||
*/
|
||||
public void setAnnotationFormatterMap(Map<Class<? extends Annotation>, Formatter<?>> formatters) {
|
||||
for (Map.Entry<Class<? extends Annotation>, Formatter<?>> entry : formatters.entrySet()) {
|
||||
addFormatterByAnnotation(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the annotation formatter factories in the set provided.
|
||||
* JavaBean-friendly alternative to calling {@link #addFormatterByAnnotation(AnnotationFormatterFactory)}.
|
||||
* @see #add(AnnotationFormatterFactory)
|
||||
*/
|
||||
public void setAnnotationFormatterFactories(Set<AnnotationFormatterFactory<?, ?>> factories) {
|
||||
for (AnnotationFormatterFactory<?, ?> factory : factories) {
|
||||
addFormatterByAnnotation(factory);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the type conversion service that will be used to coerce objects to the
|
||||
* types required for formatting. Defaults to a {@link DefaultConversionService}.
|
||||
* @see #addFormatterByType(Class, Formatter)
|
||||
*/
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
Assert.notNull(conversionService, "ConversionService must not be null");
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type conversion service which this FormatterRegistry delegates to.
|
||||
*/
|
||||
public ConversionService getConversionService() {
|
||||
return this.conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the context's default ConversionService if none specified locally.
|
||||
*/
|
||||
public void setApplicationContext(ApplicationContext context) {
|
||||
if (this.conversionService == null
|
||||
&& context.containsBean(ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME)) {
|
||||
this.conversionService = context.getBean(ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME,
|
||||
ConversionService.class);
|
||||
}
|
||||
this.applicationContext = context;
|
||||
}
|
||||
|
||||
// cloning support
|
||||
|
||||
/**
|
||||
* Specify whether this FormatterRegistry is shared, in which case newly
|
||||
* registered Formatters will be visible to other callers as well.
|
||||
* <p>A new GenericFormatterRegistry is considered as shared by default,
|
||||
* whereas a cloned GenericFormatterRegistry will be non-shared by default.
|
||||
* @see #clone()
|
||||
*/
|
||||
public void setShared(boolean shared) {
|
||||
this.shared = shared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this FormatterRegistry is shared, in which case newly
|
||||
* registered Formatters will be visible to other callers as well.
|
||||
*/
|
||||
public boolean isShared() {
|
||||
return this.shared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an independent clone of this FormatterRegistry.
|
||||
* @see #setShared
|
||||
*/
|
||||
@Override
|
||||
public GenericFormatterRegistry clone() {
|
||||
GenericFormatterRegistry clone = new GenericFormatterRegistry();
|
||||
clone.typeFormatters.putAll(this.typeFormatters);
|
||||
clone.annotationFormatters.putAll(this.annotationFormatters);
|
||||
clone.conversionService = this.conversionService;
|
||||
clone.applicationContext = applicationContext;
|
||||
clone.shared = false;
|
||||
return clone;
|
||||
}
|
||||
|
||||
// implementing FormatterRegistry
|
||||
|
||||
public void addFormatterByType(Formatter<?> formatter) {
|
||||
Class<?> formatterObjectType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
|
||||
if (formatterObjectType == null) {
|
||||
throw new IllegalArgumentException("Unable to register Formatter " + formatter
|
||||
+ "; cannot determine parameterized object type <T>");
|
||||
}
|
||||
this.typeFormatters.put(formatterObjectType, new FormatterHolder(formatterObjectType, formatter));
|
||||
}
|
||||
|
||||
public void addFormatterByType(Class<?> type, Formatter<?> formatter) {
|
||||
Class<?> formatterObjectType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
|
||||
if (formatterObjectType != null && !type.isAssignableFrom(formatterObjectType)) {
|
||||
if (this.conversionService == null) {
|
||||
throw new IllegalStateException("Unable to index Formatter " + formatter + " under type ["
|
||||
+ type.getName() + "]; not able to convert from a [" + formatterObjectType.getName()
|
||||
+ "] parsed by the Formatter to [" + type.getName()
|
||||
+ "] because this.conversionService is null");
|
||||
}
|
||||
if (!this.conversionService.canConvert(formatterObjectType, type)) {
|
||||
throw new IllegalArgumentException("Unable to index Formatter " + formatter + " under type ["
|
||||
+ type.getName() + "]; not able to convert from a [" + formatterObjectType.getName()
|
||||
+ "] parsed by the Formatter to [" + type.getName() + "]");
|
||||
}
|
||||
if (!this.conversionService.canConvert(type, formatterObjectType)) {
|
||||
throw new IllegalArgumentException("Unable to index Formatter " + formatter + " under type ["
|
||||
+ type.getName() + "]; not able to convert to [" + formatterObjectType.getName()
|
||||
+ "] to format a [" + type.getName() + "]");
|
||||
}
|
||||
}
|
||||
this.typeFormatters.put(type, new FormatterHolder(formatterObjectType, formatter));
|
||||
}
|
||||
|
||||
public void addFormatterByAnnotation(Class<? extends Annotation> annotationType, Formatter<?> formatter) {
|
||||
Class<?> formatterObjectType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
|
||||
SimpleAnnotationFormatterFactory factory = new SimpleAnnotationFormatterFactory(formatter);
|
||||
this.annotationFormatters.put(annotationType,
|
||||
new AnnotationFormatterFactoryHolder(formatterObjectType, factory));
|
||||
}
|
||||
|
||||
public void addFormatterByAnnotation(AnnotationFormatterFactory<?, ?> factory) {
|
||||
Class<?>[] typeArgs = GenericTypeResolver.resolveTypeArguments(factory.getClass(),
|
||||
AnnotationFormatterFactory.class);
|
||||
if (typeArgs == null || typeArgs.length != 2) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to extract parameterized type arguments from AnnotationFormatterFactory ["
|
||||
+ factory.getClass().getName()
|
||||
+ "]; does the factory parameterize the <A> and <T> generic types?");
|
||||
}
|
||||
this.annotationFormatters.put(typeArgs[0], new AnnotationFormatterFactoryHolder(typeArgs[1], factory));
|
||||
}
|
||||
|
||||
public Formatter<Object> getFormatter(TypeDescriptor type) {
|
||||
Assert.notNull(type, "TypeDescriptor is required");
|
||||
FormatterHolder holder = findFormatterHolderForAnnotatedProperty(type.getAnnotations());
|
||||
Class<?> objectType = type.getObjectType();
|
||||
if (holder == null) {
|
||||
holder = findFormatterHolderForType(objectType);
|
||||
}
|
||||
if (holder == null) {
|
||||
holder = getDefaultFormatterHolder(objectType);
|
||||
}
|
||||
if (holder == null) {
|
||||
return null;
|
||||
}
|
||||
Class<?> formatterObjectType = holder.getFormatterObjectType();
|
||||
if (formatterObjectType != null && !objectType.isAssignableFrom(formatterObjectType)) {
|
||||
if (this.conversionService != null) {
|
||||
return new ConvertingFormatter(type, holder);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return holder.getFormatter();
|
||||
}
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private FormatterHolder findFormatterHolderForAnnotatedProperty(Annotation[] annotations) {
|
||||
for (Annotation annotation : annotations) {
|
||||
FormatterHolder holder = findFormatterHolderForAnnotation(annotation);
|
||||
if (holder != null) {
|
||||
return holder;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private FormatterHolder findFormatterHolderForAnnotation(Annotation annotation) {
|
||||
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||
AnnotationFormatterFactoryHolder factory = this.annotationFormatters.get(annotationType);
|
||||
if (factory != null) {
|
||||
return factory.getFormatterHolder(annotation);
|
||||
} else {
|
||||
Formatted formatted = annotationType.getAnnotation(Formatted.class);
|
||||
if (formatted != null) {
|
||||
// property annotation has @Formatted meta-annotation
|
||||
Formatter<?> formatter = createFormatter(formatted.value());
|
||||
addFormatterByAnnotation(annotationType, formatter);
|
||||
return findFormatterHolderForAnnotation(annotation);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private FormatterHolder findFormatterHolderForType(Class<?> type) {
|
||||
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
|
||||
classQueue.addFirst(type);
|
||||
while (!classQueue.isEmpty()) {
|
||||
Class<?> currentClass = classQueue.removeLast();
|
||||
FormatterHolder holder = this.typeFormatters.get(currentClass);
|
||||
if (holder != null) {
|
||||
return holder;
|
||||
}
|
||||
if (currentClass.getSuperclass() != null) {
|
||||
classQueue.addFirst(currentClass.getSuperclass());
|
||||
}
|
||||
Class<?>[] interfaces = currentClass.getInterfaces();
|
||||
for (Class<?> ifc : interfaces) {
|
||||
classQueue.addFirst(ifc);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private FormatterHolder getDefaultFormatterHolder(Class<?> type) {
|
||||
Formatted formatted = AnnotationUtils.findAnnotation(type, Formatted.class);
|
||||
if (formatted != null) {
|
||||
Formatter<?> formatter = createFormatter(formatted.value());
|
||||
addFormatterByType(type, formatter);
|
||||
return findFormatterHolderForType(type);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Formatter<?> createFormatter(Class<? extends Formatter> formatterClass) {
|
||||
return (this.applicationContext != null ? this.applicationContext.getAutowireCapableBeanFactory().createBean(
|
||||
formatterClass) : BeanUtils.instantiate(formatterClass));
|
||||
}
|
||||
|
||||
private abstract static class AbstractFormatterHolder {
|
||||
|
||||
private Class<?> formatterObjectType;
|
||||
|
||||
public AbstractFormatterHolder(Class<?> formatterObjectType) {
|
||||
this.formatterObjectType = formatterObjectType;
|
||||
}
|
||||
|
||||
public Class<?> getFormatterObjectType() {
|
||||
return formatterObjectType;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class FormatterHolder extends AbstractFormatterHolder {
|
||||
|
||||
private Formatter formatter;
|
||||
|
||||
public FormatterHolder(Class<?> formatterObjectType, Formatter<?> formatter) {
|
||||
super(formatterObjectType);
|
||||
this.formatter = formatter;
|
||||
}
|
||||
|
||||
public Formatter getFormatter() {
|
||||
return this.formatter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class AnnotationFormatterFactoryHolder extends AbstractFormatterHolder {
|
||||
|
||||
private AnnotationFormatterFactory factory;
|
||||
|
||||
public AnnotationFormatterFactoryHolder(Class<?> formatterObjectType, AnnotationFormatterFactory<?, ?> factory) {
|
||||
super(formatterObjectType);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public FormatterHolder getFormatterHolder(Annotation annotation) {
|
||||
return new FormatterHolder(getFormatterObjectType(), this.factory.getFormatter(annotation));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class SimpleAnnotationFormatterFactory implements AnnotationFormatterFactory {
|
||||
|
||||
private final Formatter instance;
|
||||
|
||||
public SimpleAnnotationFormatterFactory(Formatter<?> instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
public Formatter getFormatter(Annotation annotation) {
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
|
||||
private class ConvertingFormatter implements Formatter {
|
||||
|
||||
private final TypeDescriptor type;
|
||||
|
||||
private final FormatterHolder formatterHolder;
|
||||
|
||||
public ConvertingFormatter(TypeDescriptor type, FormatterHolder formatterHolder) {
|
||||
this.type = type;
|
||||
this.formatterHolder = formatterHolder;
|
||||
}
|
||||
|
||||
public String format(Object object, Locale locale) {
|
||||
object = GenericFormatterRegistry.this.conversionService.convert(object, this.formatterHolder
|
||||
.getFormatterObjectType());
|
||||
return this.formatterHolder.getFormatter().format(object, locale);
|
||||
}
|
||||
|
||||
public Object parse(String formatted, Locale locale) throws ParseException {
|
||||
Object parsed = this.formatterHolder.getFormatter().parse(formatted, locale);
|
||||
parsed = GenericFormatterRegistry.this.conversionService.convert(parsed, TypeDescriptor
|
||||
.valueOf(this.formatterHolder.getFormatterObjectType()), this.type);
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.format.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.text.ParseException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.ui.format.AnnotationFormatterFactory;
|
||||
import org.springframework.ui.format.Formatter;
|
||||
import org.springframework.ui.format.FormatterRegistry;
|
||||
import org.springframework.ui.format.FormattingService;
|
||||
import org.springframework.ui.format.Parser;
|
||||
import org.springframework.ui.format.Printer;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A generic implementation of {@link FormattingService} suitable for use in most environments.
|
||||
* Is a {@link FormatterRegistry} to allow for registration of field formatting logic.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class GenericFormattingService implements FormattingService, FormatterRegistry {
|
||||
|
||||
private final Map<Class<?>, GenericFormatter> typeFormatters = new ConcurrentHashMap<Class<?>, GenericFormatter>();
|
||||
|
||||
private final Map<Class<? extends Annotation>, GenericAnnotationFormatterFactory> annotationFormatters = new ConcurrentHashMap<Class<? extends Annotation>, GenericAnnotationFormatterFactory>();
|
||||
|
||||
private GenericConversionService conversionService = new GenericConversionService();
|
||||
|
||||
/**
|
||||
* Configure a parent of the type conversion service that will be used to coerce objects to types required for formatting.
|
||||
*/
|
||||
public void setParentConversionService(ConversionService parentConversionService) {
|
||||
this.conversionService.setParent(parentConversionService);
|
||||
}
|
||||
|
||||
// implementing FormattingService
|
||||
|
||||
public String print(Object fieldValue, TypeDescriptor fieldType, Locale locale) {
|
||||
return getFormatter(fieldType).print(fieldValue, fieldType, locale);
|
||||
}
|
||||
|
||||
public Object parse(String submittedValue, TypeDescriptor fieldType, Locale locale) throws ParseException {
|
||||
return getFormatter(fieldType).parse(submittedValue, fieldType, locale);
|
||||
}
|
||||
|
||||
// implementing FormatterRegistry
|
||||
|
||||
public void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser) {
|
||||
Class<?> printerObjectType = resolvePrinterObjectType(printer);
|
||||
Class<?> parserObjectType = resolveParserObjectType(parser);
|
||||
this.typeFormatters.put(fieldType, new GenericFormatter(printerObjectType, printer, parserObjectType, parser));
|
||||
}
|
||||
|
||||
public void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter) {
|
||||
Class<?> formatterObjectType = resolveFormatterObjectType(formatter);
|
||||
this.typeFormatters.put(fieldType, new GenericFormatter(formatterObjectType, formatter, formatterObjectType, formatter));
|
||||
}
|
||||
|
||||
public void addFormatterForFieldAnnotation(AnnotationFormatterFactory<?> annotationFormatterFactory) {
|
||||
Class<? extends Annotation> annotationType = resolveAnnotationType(annotationFormatterFactory);
|
||||
if (annotationType == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to extract parameterized Annotation type argument from AnnotationFormatterFactory ["
|
||||
+ annotationFormatterFactory.getClass().getName()
|
||||
+ "]; does the factory parameterize the <A extends Annotation> generic type?");
|
||||
}
|
||||
this.annotationFormatters.put(annotationType, new GenericAnnotationFormatterFactory(annotationFormatterFactory));
|
||||
}
|
||||
|
||||
public ConverterRegistry getConverterRegistry() {
|
||||
return this.conversionService;
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private Class<?> resolveParserObjectType(Parser<?> parser) {
|
||||
return GenericTypeResolver.resolveTypeArgument(parser.getClass(), Parser.class);
|
||||
}
|
||||
|
||||
private Class<?> resolvePrinterObjectType(Printer<?> printer) {
|
||||
return GenericTypeResolver.resolveTypeArgument(printer.getClass(), Printer.class);
|
||||
}
|
||||
|
||||
private Class<?> resolveFormatterObjectType(Formatter<?> formatter) {
|
||||
return GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<? extends Annotation> resolveAnnotationType(AnnotationFormatterFactory<?> annotationFormatterFactory) {
|
||||
return (Class<? extends Annotation>) GenericTypeResolver.resolveTypeArgument(annotationFormatterFactory.getClass(), AnnotationFormatterFactory.class);
|
||||
}
|
||||
|
||||
private GenericFormatter getFormatter(TypeDescriptor fieldType) {
|
||||
Assert.notNull(fieldType, "Field TypeDescriptor is required");
|
||||
GenericFormatter formatter = findFormatterForAnnotatedField(fieldType);
|
||||
Class<?> fieldObjectType = fieldType.getObjectType();
|
||||
if (formatter == null) {
|
||||
formatter = findFormatterForFieldType(fieldObjectType);
|
||||
}
|
||||
return formatter;
|
||||
}
|
||||
|
||||
private GenericFormatter findFormatterForAnnotatedField(TypeDescriptor fieldType) {
|
||||
for (Annotation annotation : fieldType.getAnnotations()) {
|
||||
GenericFormatter formatter = findFormatterForAnnotation(annotation, fieldType.getObjectType());
|
||||
if (formatter != null) {
|
||||
return formatter;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private GenericFormatter findFormatterForAnnotation(Annotation annotation, Class<?> fieldType) {
|
||||
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||
GenericAnnotationFormatterFactory factory = this.annotationFormatters.get(annotationType);
|
||||
if (factory != null) {
|
||||
return factory.getFormatter(annotation, fieldType);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private GenericFormatter findFormatterForFieldType(Class<?> fieldType) {
|
||||
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
|
||||
classQueue.addFirst(fieldType);
|
||||
while (!classQueue.isEmpty()) {
|
||||
Class<?> currentClass = classQueue.removeLast();
|
||||
GenericFormatter formatter = this.typeFormatters.get(currentClass);
|
||||
if (formatter != null) {
|
||||
return formatter;
|
||||
}
|
||||
if (currentClass.getSuperclass() != null) {
|
||||
classQueue.addFirst(currentClass.getSuperclass());
|
||||
}
|
||||
Class<?>[] interfaces = currentClass.getInterfaces();
|
||||
for (Class<?> ifc : interfaces) {
|
||||
classQueue.addFirst(ifc);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private class GenericFormatter {
|
||||
|
||||
private TypeDescriptor printerObjectType;
|
||||
|
||||
private Printer printer;
|
||||
|
||||
private Parser parser;
|
||||
|
||||
public GenericFormatter(Class<?> printerObjectType, Printer<?> printer, Class<?> parserObjectType, Parser<?> parser) {
|
||||
this.printerObjectType = TypeDescriptor.valueOf(printerObjectType);
|
||||
this.printer = printer;
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
public String print(Object fieldValue, TypeDescriptor fieldType, Locale locale) {
|
||||
if (!fieldType.isAssignableTo(this.printerObjectType)) {
|
||||
fieldValue = GenericFormattingService.this.conversionService.convert(fieldValue, fieldType, this.printerObjectType);
|
||||
}
|
||||
return fieldType != null ? this.printer.print(fieldValue, locale) : "";
|
||||
}
|
||||
|
||||
public Object parse(String submittedValue, TypeDescriptor fieldType, Locale locale) throws ParseException {
|
||||
if (submittedValue.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Object parsedValue = this.parser.parse(submittedValue, locale);
|
||||
TypeDescriptor parsedObjectType = TypeDescriptor.valueOf(parsedValue.getClass());
|
||||
if (!parsedObjectType.isAssignableTo(fieldType)) {
|
||||
parsedValue = GenericFormattingService.this.conversionService.convert(parsedValue, parsedObjectType, fieldType);
|
||||
}
|
||||
return parsedValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private class GenericAnnotationFormatterFactory {
|
||||
|
||||
private AnnotationFormatterFactory annotationFormatterFactory;
|
||||
|
||||
public GenericAnnotationFormatterFactory(AnnotationFormatterFactory<?> annotationFormatterFactory) {
|
||||
this.annotationFormatterFactory = annotationFormatterFactory;
|
||||
}
|
||||
|
||||
public GenericFormatter getFormatter(Annotation annotation, Class<?> fieldType) {
|
||||
Printer<?> printer = this.annotationFormatterFactory.getPrinter(annotation, fieldType);
|
||||
Parser<?> parser = this.annotationFormatterFactory.getParser(annotation, fieldType);
|
||||
return new GenericFormatter(getPrinterObjectType(printer, fieldType), printer, getParserObjectType(parser, fieldType), parser);
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private Class<?> getPrinterObjectType(Printer<?> printer, Class<?> fieldType) {
|
||||
// TODO cache
|
||||
return resolvePrinterObjectType(printer);
|
||||
}
|
||||
|
||||
private Class<?> getParserObjectType(Parser<?> parser, Class<?> fieldType) {
|
||||
// TODO cache
|
||||
return resolveParserObjectType(parser);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ui.format.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.ui.format.Formatter;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* A generic Formatter that reflectively invokes methods on a class to carry out format and parse operations.
|
||||
* The format method should be a public member method that returns a String and either accepts no arguments or a single Locale argument.
|
||||
* The parse method should be a declared public static method that returns the formattedObjectType and either accepts a single String argument or String and Locale arguments.
|
||||
* Throws an {@link IllegalArgumentException} if either the format method or parse method could not be resolved.
|
||||
* Useful for invoking existing Formatter logic on a formattable type without needing a dedicated Formatter class.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class MethodInvokingFormatter implements Formatter {
|
||||
|
||||
private Class<?> formattedObjectType;
|
||||
|
||||
private Method formatMethod;
|
||||
|
||||
private boolean formatMethodLocaleArgumentPresent;
|
||||
|
||||
private Method parseMethod;
|
||||
|
||||
private boolean parseMethodLocaleArgumentPresent;
|
||||
|
||||
/**
|
||||
* Creates a new reflective method invoking formatter.
|
||||
* @param formattedObjectType the object type that contains format and parse methods
|
||||
* @param formatMethodName the format method name e.g. "toString"
|
||||
* @param parseMethodName the parse method name e.g. "valueOf"
|
||||
*/
|
||||
public MethodInvokingFormatter(Class<?> formattedObjectType, String formatMethodName, String parseMethodName) {
|
||||
this.formattedObjectType = formattedObjectType;
|
||||
resolveFormatMethod(formatMethodName);
|
||||
resolveParseMethod(parseMethodName);
|
||||
}
|
||||
|
||||
public String format(Object object, Locale locale) {
|
||||
if (this.formatMethodLocaleArgumentPresent) {
|
||||
return (String) ReflectionUtils.invokeMethod(this.formatMethod, object, locale);
|
||||
} else {
|
||||
return (String) ReflectionUtils.invokeMethod(this.formatMethod, object);
|
||||
}
|
||||
}
|
||||
|
||||
public Object parse(String formatted, Locale locale) {
|
||||
if (this.parseMethodLocaleArgumentPresent) {
|
||||
return ReflectionUtils.invokeMethod(this.parseMethod, null, formatted, locale);
|
||||
} else {
|
||||
return ReflectionUtils.invokeMethod(this.parseMethod, null, formatted);
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveFormatMethod(String methodName) {
|
||||
Method[] methods = this.formattedObjectType.getMethods();
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals(methodName) && method.getReturnType().equals(String.class)) {
|
||||
if (method.getParameterTypes().length == 0) {
|
||||
this.formatMethod = method;
|
||||
} else if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(Locale.class)) {
|
||||
this.formatMethod = method;
|
||||
this.formatMethodLocaleArgumentPresent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.formatMethod == null) {
|
||||
throw new IllegalArgumentException("Unable to resolve format method '" + methodName + "' on class ["
|
||||
+ this.formattedObjectType.getName()
|
||||
+ "] method should have signature [public String <methodName>()] "
|
||||
+ "or [public String <methodName>(Locale)]");
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveParseMethod(String methodName) {
|
||||
Method[] methods = this.formattedObjectType.getDeclaredMethods();
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals(methodName) && method.getReturnType().equals(this.formattedObjectType)
|
||||
&& Modifier.isPublic(method.getModifiers()) && Modifier.isStatic(method.getModifiers())) {
|
||||
if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(String.class)) {
|
||||
this.parseMethod = method;
|
||||
} else if (method.getParameterTypes().length == 2 && method.getParameterTypes()[0].equals(String.class)
|
||||
&& method.getParameterTypes()[1].equals(Locale.class)) {
|
||||
this.parseMethod = method;
|
||||
this.parseMethodLocaleArgumentPresent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.parseMethod == null) {
|
||||
throw new IllegalArgumentException("Unable to resolve parse method '" + methodName + "' on class ["
|
||||
+ this.formattedObjectType.getName()
|
||||
+ "]; method should have signature [public static T <methodName>(String)] "
|
||||
+ "or [public static T <methodName>(String, Locale)]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,8 +24,7 @@ import org.springframework.beans.PropertyAccessorUtils;
|
|||
import org.springframework.beans.PropertyEditorRegistry;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.ui.format.Formatter;
|
||||
import org.springframework.ui.format.FormatterRegistry;
|
||||
import org.springframework.ui.format.FormattingService;
|
||||
import org.springframework.ui.format.support.FormattingConversionServiceAdapter;
|
||||
import org.springframework.ui.format.support.FormattingPropertyEditorAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
|
|
@ -44,7 +43,7 @@ import org.springframework.util.Assert;
|
|||
*/
|
||||
public abstract class AbstractPropertyBindingResult extends AbstractBindingResult {
|
||||
|
||||
private FormatterRegistry formatterRegistry;
|
||||
private FormattingService formattingService;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -57,10 +56,10 @@ public abstract class AbstractPropertyBindingResult extends AbstractBindingResul
|
|||
}
|
||||
|
||||
|
||||
public void initFormatterLookup(FormatterRegistry formatterRegistry) {
|
||||
Assert.notNull(formatterRegistry, "FormatterRegistry must not be null");
|
||||
this.formatterRegistry = formatterRegistry;
|
||||
getPropertyAccessor().setConversionService(new FormattingConversionServiceAdapter(formatterRegistry));
|
||||
public void initFormatting(FormattingService formattingService) {
|
||||
Assert.notNull(formattingService, "FormattingService must not be null");
|
||||
this.formattingService = formattingService;
|
||||
getPropertyAccessor().setConversionService(new FormattingConversionServiceAdapter(formattingService));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -117,15 +116,14 @@ public abstract class AbstractPropertyBindingResult extends AbstractBindingResul
|
|||
return textValue;
|
||||
}
|
||||
}
|
||||
if (this.formattingService != null) {
|
||||
// Try custom formatter...
|
||||
TypeDescriptor td = getPropertyAccessor().getPropertyTypeDescriptor(fixedField);
|
||||
Formatter<Object> formatter = (this.formatterRegistry != null ? this.formatterRegistry.getFormatter(td) : null);
|
||||
if (formatter != null) {
|
||||
return formatter.format(value, LocaleContextHolder.getLocale());
|
||||
}
|
||||
// Nothing found: return value as-is.
|
||||
return this.formattingService.print(value, td, LocaleContextHolder.getLocale());
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the custom PropertyEditor for the given field, if any.
|
||||
|
|
@ -147,16 +145,15 @@ public abstract class AbstractPropertyBindingResult extends AbstractBindingResul
|
|||
*/
|
||||
@Override
|
||||
public PropertyEditor findEditor(String field, Class valueType) {
|
||||
if (valueType == null) {
|
||||
valueType = getFieldType(field);
|
||||
}
|
||||
PropertyEditor editor = super.findEditor(field, valueType);
|
||||
if (editor == null) {
|
||||
TypeDescriptor td = (field != null ?
|
||||
getPropertyAccessor().getPropertyTypeDescriptor(fixedField(field)) :
|
||||
TypeDescriptor.valueOf(valueType));
|
||||
Formatter<Object> formatter =
|
||||
(this.formatterRegistry != null ? this.formatterRegistry.getFormatter(td) : null);
|
||||
if (formatter != null) {
|
||||
editor = new FormattingPropertyEditorAdapter(formatter);
|
||||
}
|
||||
editor = new FormattingPropertyEditorAdapter(this.formattingService, valueType);
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import java.util.Map;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.ConfigurablePropertyAccessor;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyAccessException;
|
||||
|
|
@ -35,10 +34,8 @@ import org.springframework.beans.SimpleTypeConverter;
|
|||
import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.ui.format.FormatterRegistry;
|
||||
import org.springframework.ui.format.FormattingService;
|
||||
import org.springframework.ui.format.support.FormattingConversionServiceAdapter;
|
||||
import org.springframework.ui.format.support.GenericFormatterRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
|
|
@ -138,7 +135,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
|||
|
||||
private Validator validator;
|
||||
|
||||
private FormatterRegistry formatterRegistry;
|
||||
private FormattingService formattingService;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -186,8 +183,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
|||
Assert.isNull(this.bindingResult,
|
||||
"DataBinder is already initialized - call initBeanPropertyAccess before any other configuration methods");
|
||||
this.bindingResult = new BeanPropertyBindingResult(getTarget(), getObjectName());
|
||||
if (this.formatterRegistry != null) {
|
||||
this.bindingResult.initFormatterLookup(this.formatterRegistry);
|
||||
if (this.formattingService != null) {
|
||||
this.bindingResult.initFormatting(this.formattingService);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -200,8 +197,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
|||
Assert.isNull(this.bindingResult,
|
||||
"DataBinder is already initialized - call initDirectFieldAccess before any other configuration methods");
|
||||
this.bindingResult = new DirectFieldBindingResult(getTarget(), getObjectName());
|
||||
if (this.formatterRegistry != null) {
|
||||
this.bindingResult.initFormatterLookup(this.formatterRegistry);
|
||||
if (this.formattingService != null) {
|
||||
this.bindingResult.initFormatting(this.formattingService);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -229,8 +226,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
|||
protected SimpleTypeConverter getSimpleTypeConverter() {
|
||||
if (this.typeConverter == null) {
|
||||
this.typeConverter = new SimpleTypeConverter();
|
||||
if (this.formatterRegistry != null) {
|
||||
this.typeConverter.setConversionService(new FormattingConversionServiceAdapter(this.formatterRegistry));
|
||||
if (this.formattingService != null) {
|
||||
this.typeConverter.setConversionService(new FormattingConversionServiceAdapter(this.formattingService));
|
||||
}
|
||||
}
|
||||
return this.typeConverter;
|
||||
|
|
@ -464,33 +461,12 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the FormatterRegistry to use for obtaining Formatters in preference
|
||||
* to JavaBeans PropertyEditors.
|
||||
* Set the FormattingService to use for field value formatting in preference to JavaBeans PropertyEditors.
|
||||
*/
|
||||
public void setFormatterRegistry(FormatterRegistry formatterRegistry) {
|
||||
this.formatterRegistry = formatterRegistry;
|
||||
public void setFormattingService(FormattingService formattingService) {
|
||||
this.formattingService = formattingService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the FormatterRegistry to use for obtaining Formatters in preference
|
||||
* to JavaBeans PropertyEditors.
|
||||
* @return the FormatterRegistry (never <code>null</code>), which may also be
|
||||
* used to register further Formatters for this DataBinder
|
||||
*/
|
||||
public FormatterRegistry getFormatterRegistry() {
|
||||
if (this.formatterRegistry == null) {
|
||||
GenericFormatterRegistry registry = new GenericFormatterRegistry();
|
||||
registry.setConversionService(new DefaultConversionService());
|
||||
this.formatterRegistry = registry;
|
||||
}
|
||||
else if (this.formatterRegistry instanceof GenericFormatterRegistry &&
|
||||
((GenericFormatterRegistry) this.formatterRegistry).isShared()) {
|
||||
this.formatterRegistry = ((GenericFormatterRegistry) this.formatterRegistry).clone();
|
||||
}
|
||||
return this.formatterRegistry;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of PropertyEditorRegistry/TypeConverter interface
|
||||
//---------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.MutableDateTime;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
|
@ -317,6 +318,35 @@ public class MappingTests {
|
|||
.getActivationDateTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiFieldToFieldMappingWithAssembler() {
|
||||
Mapper<Map, Account> mapper = MapperFactory.mapperBuilder(Map.class, Account.class)
|
||||
.setAutoMappingEnabled(false)
|
||||
// field to multiple fields
|
||||
.addAssemblerMapping("activationDateTime", new Converter<Map<String, String>, DateTime>() {
|
||||
public DateTime convert(Map<String, String> source) {
|
||||
MutableDateTime dateTime = new MutableDateTime();
|
||||
dateTime.setYear(Integer.parseInt(source.get("year")));
|
||||
dateTime.setMonthOfYear(Integer.parseInt(source.get("month")));
|
||||
dateTime.setDayOfMonth(Integer.parseInt(source.get("day")));
|
||||
dateTime.setHourOfDay(Integer.parseInt(source.get("hour")));
|
||||
dateTime.setMinuteOfHour(Integer.parseInt(source.get("minute")));
|
||||
dateTime.setSecondOfMinute(0);
|
||||
dateTime.setMillisOfSecond(0);
|
||||
return dateTime.toDateTime();
|
||||
}
|
||||
}).getMapper();
|
||||
Map<String, Object> source = new HashMap<String, Object>();
|
||||
source.put("activationDateTime.year", "2009");
|
||||
source.put("activationDateTime.month", "10");
|
||||
source.put("activationDateTime.day", "12");
|
||||
source.put("activationDateTime.hour", "12");
|
||||
source.put("activationDateTime.minute", "0");
|
||||
Account account = mapper.map(source, new Account());
|
||||
assertEquals(ISODateTimeFormat.dateTime().parseDateTime("2009-10-12T12:00:00.000-04:00"), account
|
||||
.getActivationDateTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void conditionalMapping() {
|
||||
Map<String, String> domestic = new HashMap<String, String>();
|
||||
|
|
@ -328,9 +358,8 @@ public class MappingTests {
|
|||
domestic.put("cityCode", "whatever");
|
||||
|
||||
Mapper<Map, PhoneNumber> mapper = MapperFactory.mapperBuilder(Map.class, PhoneNumber.class)
|
||||
.addConditionalMapping("countryCode", "international == 'true'")
|
||||
.addConditionalMapping("cityCode", "international == 'true'")
|
||||
.getMapper();
|
||||
.addConditionalMapping("countryCode", "international == 'true'").addConditionalMapping("cityCode",
|
||||
"international == 'true'").getMapper();
|
||||
|
||||
PhoneNumber number = mapper.map(domestic, new PhoneNumber());
|
||||
assertEquals("205", number.getAreaCode());
|
||||
|
|
@ -479,7 +508,7 @@ public class MappingTests {
|
|||
|
||||
MapperFactory.defaultMapper().map(source, target);
|
||||
assertEquals(1, target.getNumber());
|
||||
assertTrue(item != target.getLineItem());
|
||||
assertTrue(item == target.getLineItem());
|
||||
assertEquals(new BigDecimal("30.00"), target.getLineItem().getAmount());
|
||||
assertEquals(source, target.getLineItem().getOrder());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public class DateFormatterTests {
|
|||
cal.set(Calendar.YEAR, 2009);
|
||||
cal.set(Calendar.MONTH, Calendar.JUNE);
|
||||
cal.set(Calendar.DAY_OF_MONTH, 1);
|
||||
assertEquals("2009-06-01", formatter.format(cal.getTime(), Locale.US));
|
||||
assertEquals("2009-06-01", formatter.print(cal.getTime(), Locale.US));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -16,12 +16,13 @@
|
|||
|
||||
package org.springframework.ui.format.number;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.ParseException;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
|
|
@ -33,7 +34,7 @@ public class CurrencyFormatterTests {
|
|||
|
||||
@Test
|
||||
public void formatValue() {
|
||||
assertEquals("$23.00", formatter.format(new BigDecimal("23"), Locale.US));
|
||||
assertEquals("$23.00", formatter.print(new BigDecimal("23"), Locale.US));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -41,11 +42,6 @@ public class CurrencyFormatterTests {
|
|||
assertEquals(new BigDecimal("23.56"), formatter.parse("$23.56", Locale.US));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseEmptyValue() throws ParseException {
|
||||
assertEquals(null, formatter.parse("", Locale.US));
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void parseBogusValue() throws ParseException {
|
||||
formatter.parse("bogus", Locale.US);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ public class DecimalFormatterTests {
|
|||
|
||||
@Test
|
||||
public void formatValue() {
|
||||
assertEquals("23.56", formatter.format(new BigDecimal("23.56"), Locale.US));
|
||||
assertEquals("23.56", formatter.print(new BigDecimal("23.56"), Locale.US));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -42,11 +42,6 @@ public class DecimalFormatterTests {
|
|||
assertEquals(new BigDecimal("23.56"), formatter.parse("23.56", Locale.US));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseEmptyValue() throws ParseException {
|
||||
assertEquals(null, formatter.parse("", Locale.US));
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void parseBogusValue() throws ParseException {
|
||||
formatter.parse("bogus", Locale.US);
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public class IntegerFormatterTests {
|
|||
|
||||
@Test
|
||||
public void formatValue() {
|
||||
assertEquals("23", formatter.format(23L, Locale.US));
|
||||
assertEquals("23", formatter.print(23L, Locale.US));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -41,11 +41,6 @@ public class IntegerFormatterTests {
|
|||
assertEquals((Long) 2356L, formatter.parse("2356", Locale.US));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseEmptyValue() throws ParseException {
|
||||
assertEquals(null, formatter.parse("", Locale.US));
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void parseBogusValue() throws ParseException {
|
||||
formatter.parse("bogus", Locale.US);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ public class PercentFormatterTests {
|
|||
|
||||
@Test
|
||||
public void formatValue() {
|
||||
assertEquals("23%", formatter.format(new BigDecimal(".23"), Locale.US));
|
||||
assertEquals("23%", formatter.print(new BigDecimal(".23"), Locale.US));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -43,11 +43,6 @@ public class PercentFormatterTests {
|
|||
Locale.US));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseEmptyValue() throws ParseException {
|
||||
assertEquals(null, formatter.parse("", Locale.US));
|
||||
}
|
||||
|
||||
@Test(expected = ParseException.class)
|
||||
public void parseBogusValue() throws ParseException {
|
||||
formatter.parse("bogus", Locale.US);
|
||||
|
|
|
|||
|
|
@ -1,228 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.format.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.text.ParseException;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.ui.format.AnnotationFormatterFactory;
|
||||
import org.springframework.ui.format.Formatted;
|
||||
import org.springframework.ui.format.Formatter;
|
||||
import org.springframework.ui.format.number.CurrencyFormatter;
|
||||
import org.springframework.ui.format.number.IntegerFormatter;
|
||||
import org.springframework.ui.format.support.GenericFormatterRegistry;
|
||||
|
||||
/**
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class GenericFormatterRegistryTests {
|
||||
|
||||
private GenericFormatterRegistry registry;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
registry = new GenericFormatterRegistry();
|
||||
registry.setConversionService(new DefaultConversionService());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd() throws ParseException {
|
||||
registry.addFormatterByType(new IntegerFormatter());
|
||||
Formatter formatter = registry.getFormatter(TypeDescriptor.valueOf(Integer.class));
|
||||
String formatted = formatter.format(new Integer(3), Locale.US);
|
||||
assertEquals("3", formatted);
|
||||
Integer i = (Integer) formatter.parse("3", Locale.US);
|
||||
assertEquals(new Integer(3), i);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddLookupByPrimitive() throws ParseException {
|
||||
registry.addFormatterByType(new IntegerFormatter());
|
||||
Formatter formatter = registry.getFormatter(TypeDescriptor.valueOf(int.class));
|
||||
String formatted = formatter.format(3, Locale.US);
|
||||
assertEquals("3", formatted);
|
||||
int integer = (Integer) formatter.parse("3", Locale.US);
|
||||
assertEquals(3, integer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddByObjectType() {
|
||||
registry.addFormatterByType(BigInteger.class, new IntegerFormatter());
|
||||
Formatter formatter = registry.getFormatter(TypeDescriptor.valueOf(BigInteger.class));
|
||||
String formatted = formatter.format(new BigInteger("3"), Locale.US);
|
||||
assertEquals("3", formatted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddByAnnotation() throws Exception {
|
||||
registry.addFormatterByAnnotation(Currency.class, new CurrencyFormatter());
|
||||
Formatter formatter = registry.getFormatter(new TypeDescriptor(getClass().getField("currencyField")));
|
||||
String formatted = formatter.format(new BigDecimal("5.00"), Locale.US);
|
||||
assertEquals("$5.00", formatted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddAnnotationFormatterFactory() throws Exception {
|
||||
registry.addFormatterByAnnotation(new CurrencyAnnotationFormatterFactory());
|
||||
Formatter formatter = registry.getFormatter(new TypeDescriptor(getClass().getField("currencyField")));
|
||||
String formatted = formatter.format(new BigDecimal("5.00"), Locale.US);
|
||||
assertEquals("$5.00", formatted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDefaultFormatterFromMetaAnnotation() throws Exception {
|
||||
Formatter formatter = registry.getFormatter(new TypeDescriptor(getClass().getField("smartCurrencyField")));
|
||||
String formatted = formatter.format(new BigDecimal("5.00"), Locale.US);
|
||||
assertEquals("$5.00", formatted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDefaultFormatterForType() {
|
||||
Formatter formatter = registry.getFormatter(TypeDescriptor.valueOf(Address.class));
|
||||
Address address = new Address();
|
||||
address.street = "12345 Bel Aire Estates";
|
||||
address.city = "Palm Bay";
|
||||
address.state = "FL";
|
||||
address.zip = "12345";
|
||||
String formatted = formatter.format(address, Locale.US);
|
||||
assertEquals("12345 Bel Aire Estates:Palm Bay:FL:12345", formatted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDefaultFormatterNull() throws ParseException {
|
||||
assertNull(registry.getFormatter(TypeDescriptor.valueOf(Integer.class)));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testGetFormatterCannotConvert() {
|
||||
registry.addFormatterByType(Integer.class, new AddressFormatter());
|
||||
}
|
||||
|
||||
@Currency
|
||||
public BigDecimal currencyField;
|
||||
|
||||
@SmartCurrency
|
||||
public BigDecimal smartCurrencyField;
|
||||
|
||||
@Target( { ElementType.METHOD, ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Currency {
|
||||
}
|
||||
|
||||
@Target( { ElementType.METHOD, ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Formatted(CurrencyFormatter.class)
|
||||
public @interface SmartCurrency {
|
||||
}
|
||||
|
||||
public static class CurrencyAnnotationFormatterFactory implements AnnotationFormatterFactory<Currency, Number> {
|
||||
|
||||
private final CurrencyFormatter currencyFormatter = new CurrencyFormatter();
|
||||
|
||||
public Formatter<Number> getFormatter(Currency annotation) {
|
||||
return this.currencyFormatter;
|
||||
}
|
||||
}
|
||||
|
||||
@Formatted(AddressFormatter.class)
|
||||
public static class Address {
|
||||
|
||||
private String street;
|
||||
private String city;
|
||||
private String state;
|
||||
private String zip;
|
||||
private String country;
|
||||
|
||||
public String getStreet() {
|
||||
return street;
|
||||
}
|
||||
|
||||
public void setStreet(String street) {
|
||||
this.street = street;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public void setCity(String city) {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getZip() {
|
||||
return zip;
|
||||
}
|
||||
|
||||
public void setZip(String zip) {
|
||||
this.zip = zip;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("street", street).append("city", city).append("state", state)
|
||||
.append("zip", zip).toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AddressFormatter implements Formatter<Address> {
|
||||
|
||||
public String format(Address address, Locale locale) {
|
||||
return address.getStreet() + ":" + address.getCity() + ":" + address.getState() + ":" + address.getZip();
|
||||
}
|
||||
|
||||
public Address parse(String formatted, Locale locale) throws ParseException {
|
||||
Address address = new Address();
|
||||
String[] fields = formatted.split(":");
|
||||
address.setStreet(fields[0]);
|
||||
address.setCity(fields[1]);
|
||||
address.setState(fields[2]);
|
||||
address.setZip(fields[3]);
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.format.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.ui.format.jodatime.DateTimeFormatAnnotationFormatterFactory;
|
||||
import org.springframework.ui.format.jodatime.DateTimeParser;
|
||||
import org.springframework.ui.format.jodatime.ReadablePartialPrinter;
|
||||
import org.springframework.ui.format.jodatime.DateTimeFormat.FormatStyle;
|
||||
import org.springframework.ui.format.number.IntegerFormatter;
|
||||
|
||||
/**
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class GenericFormattingServiceTests {
|
||||
|
||||
private GenericFormattingService formattingService;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
formattingService = new GenericFormattingService();
|
||||
formattingService.setParentConversionService(new DefaultConversionService());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatFieldForTypeWithFormatter() throws ParseException {
|
||||
formattingService.addFormatterForFieldType(Number.class, new IntegerFormatter());
|
||||
String formatted = formattingService.print(new Integer(3), TypeDescriptor.valueOf(Integer.class), Locale.US);
|
||||
assertEquals("3", formatted);
|
||||
Integer i = (Integer) formattingService.parse("3", TypeDescriptor.valueOf(Integer.class), Locale.US);
|
||||
assertEquals(new Integer(3), i);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatFieldForTypeWithPrinterParserWithCoersion() throws ParseException {
|
||||
formattingService.getConverterRegistry().addConverter(new Converter<DateTime, LocalDate>() {
|
||||
public LocalDate convert(DateTime source) {
|
||||
return source.toLocalDate();
|
||||
}
|
||||
});
|
||||
formattingService.addFormatterForFieldType(LocalDate.class, new ReadablePartialPrinter(DateTimeFormat
|
||||
.shortDate()), new DateTimeParser(DateTimeFormat.shortDate()));
|
||||
String formatted = formattingService.print(new LocalDate(2009, 10, 31), TypeDescriptor.valueOf(LocalDate.class), Locale.US);
|
||||
assertEquals("10/31/09", formatted);
|
||||
LocalDate date = (LocalDate) formattingService.parse("10/31/09", TypeDescriptor.valueOf(LocalDate.class), Locale.US);
|
||||
assertEquals(new LocalDate(2009, 10, 31), date);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatFieldForAnnotation() throws Exception {
|
||||
formattingService.getConverterRegistry().addConverter(new Converter<Date, Long>() {
|
||||
public Long convert(Date source) {
|
||||
return source.getTime();
|
||||
}
|
||||
});
|
||||
formattingService.getConverterRegistry().addConverter(new Converter<DateTime, Date>() {
|
||||
public Date convert(DateTime source) {
|
||||
return source.toDate();
|
||||
}
|
||||
});
|
||||
formattingService.addFormatterForFieldAnnotation(new DateTimeFormatAnnotationFormatterFactory());
|
||||
String formatted = formattingService.print(new LocalDate(2009, 10, 31).toDateTimeAtCurrentTime().toDate(), new TypeDescriptor(Model.class.getField("date")), Locale.US);
|
||||
assertEquals("10/31/09", formatted);
|
||||
LocalDate date = new LocalDate(formattingService.parse("10/31/09", new TypeDescriptor(Model.class.getField("date")), Locale.US));
|
||||
assertEquals(new LocalDate(2009, 10, 31), date);
|
||||
}
|
||||
|
||||
private static class Model {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@org.springframework.ui.format.jodatime.DateTimeFormat(dateStyle=FormatStyle.SHORT)
|
||||
public Date date;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
package org.springframework.ui.format.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.ui.format.support.MethodInvokingFormatter;
|
||||
|
||||
public class MethodInvokingFormatterTests {
|
||||
|
||||
private MethodInvokingFormatter formatter = new MethodInvokingFormatter(AccountNumber.class, "getFormatted",
|
||||
"valueOf");
|
||||
|
||||
private MethodInvokingFormatter formatter2 = new MethodInvokingFormatter(I8nAccountNumber.class, "getFormatted",
|
||||
"valueOf");
|
||||
|
||||
@Test
|
||||
public void testFormat() {
|
||||
assertEquals("123456789", formatter.format(new AccountNumber(123456789L), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse() {
|
||||
assertEquals(new Long(123456789), ((AccountNumber) formatter.parse("123456789", null)).number);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatI18n() {
|
||||
assertEquals("123456789", formatter2.format(new I8nAccountNumber(123456789L), Locale.GERMAN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseI18n() {
|
||||
assertEquals(new Long(123456789), ((I8nAccountNumber) formatter2.parse("123456789", Locale.GERMAN)).number);
|
||||
}
|
||||
|
||||
public static class AccountNumber {
|
||||
|
||||
private Long number;
|
||||
|
||||
public AccountNumber(Long number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public String getFormatted() {
|
||||
return number.toString();
|
||||
}
|
||||
|
||||
public static AccountNumber valueOf(String str) {
|
||||
return new AccountNumber(Long.valueOf(str));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class I8nAccountNumber {
|
||||
|
||||
private Long number;
|
||||
|
||||
public I8nAccountNumber(Long number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public String getFormatted(Locale locale) {
|
||||
assertEquals(Locale.GERMAN, locale);
|
||||
return number.toString();
|
||||
}
|
||||
|
||||
public static I8nAccountNumber valueOf(String str, Locale locale) {
|
||||
assertEquals(Locale.GERMAN, locale);
|
||||
return new I8nAccountNumber(Long.valueOf(str));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -16,21 +16,20 @@
|
|||
|
||||
package org.springframework.validation;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.beans.PropertyEditor;
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
|
|
@ -45,12 +44,12 @@ import org.springframework.beans.SerializablePerson;
|
|||
import org.springframework.beans.TestBean;
|
||||
import org.springframework.beans.propertyeditors.CustomCollectionEditor;
|
||||
import org.springframework.beans.propertyeditors.CustomNumberEditor;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.context.support.ResourceBundleMessageSource;
|
||||
import org.springframework.context.support.StaticMessageSource;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.ui.format.number.DecimalFormatter;
|
||||
import org.springframework.ui.format.Formatted;
|
||||
import org.springframework.ui.format.support.GenericFormatterRegistry;
|
||||
import org.springframework.ui.format.support.GenericFormattingService;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
|
@ -304,7 +303,10 @@ public class DataBinderTests extends TestCase {
|
|||
public void testBindingWithFormatter() {
|
||||
TestBean tb = new TestBean();
|
||||
DataBinder binder = new DataBinder(tb);
|
||||
binder.getFormatterRegistry().addFormatterByType(Float.class, new DecimalFormatter());
|
||||
GenericFormattingService formattingService = new GenericFormattingService();
|
||||
formattingService.setParentConversionService(new DefaultConversionService());
|
||||
formattingService.addFormatterForFieldType(Float.class, new DecimalFormatter());
|
||||
binder.setFormattingService(formattingService);
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
pvs.addPropertyValue("myFloat", "1,2");
|
||||
|
||||
|
|
@ -329,46 +331,6 @@ public class DataBinderTests extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testBindingWithDefaultFormatterFromField() {
|
||||
doTestBindingWithDefaultFormatter(new FormattedFieldTestBean());
|
||||
}
|
||||
|
||||
public void testBindingWithDefaultFormatterFromGetter() {
|
||||
doTestBindingWithDefaultFormatter(new FormattedGetterTestBean());
|
||||
}
|
||||
|
||||
public void testBindingWithDefaultFormatterFromSetter() {
|
||||
doTestBindingWithDefaultFormatter(new FormattedSetterTestBean());
|
||||
}
|
||||
|
||||
private void doTestBindingWithDefaultFormatter(Object tb) {
|
||||
DataBinder binder = new DataBinder(tb);
|
||||
// force formatter registry to be created
|
||||
binder.getFormatterRegistry();
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
pvs.addPropertyValue("number", "1,2");
|
||||
|
||||
LocaleContextHolder.setLocale(Locale.GERMAN);
|
||||
try {
|
||||
binder.bind(pvs);
|
||||
assertEquals(new Float("1.2"), binder.getBindingResult().getRawFieldValue("number"));
|
||||
assertEquals("1,2", binder.getBindingResult().getFieldValue("number"));
|
||||
|
||||
PropertyEditor editor = binder.getBindingResult().findEditor("number", Float.class);
|
||||
assertNotNull(editor);
|
||||
editor.setValue(new Float("1.4"));
|
||||
assertEquals("1,4", editor.getAsText());
|
||||
|
||||
editor = binder.getBindingResult().findEditor("number", null);
|
||||
assertNotNull(editor);
|
||||
editor.setAsText("1,6");
|
||||
assertEquals(new Float("1.6"), editor.getValue());
|
||||
}
|
||||
finally {
|
||||
LocaleContextHolder.resetLocaleContext();
|
||||
}
|
||||
}
|
||||
|
||||
public void testBindingWithAllowedFields() throws Exception {
|
||||
TestBean rod = new TestBean();
|
||||
DataBinder binder = new DataBinder(rod);
|
||||
|
|
@ -1423,56 +1385,4 @@ public class DataBinderTests extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Target({ElementType.METHOD, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Formatted(DecimalFormatter.class)
|
||||
public @interface Decimal {
|
||||
}
|
||||
|
||||
|
||||
private static class FormattedFieldTestBean {
|
||||
|
||||
@Decimal
|
||||
private Float number;
|
||||
|
||||
public Float getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public void setNumber(Float number) {
|
||||
this.number = number;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class FormattedGetterTestBean {
|
||||
|
||||
private Float number;
|
||||
|
||||
@Decimal
|
||||
public Float getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public void setNumber(Float number) {
|
||||
this.number = number;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class FormattedSetterTestBean {
|
||||
|
||||
private Float number;
|
||||
|
||||
public Float getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
@Decimal
|
||||
public void setNumber(Float number) {
|
||||
this.number = number;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
|
|
@ -38,9 +36,6 @@ public class DefaultConversionService extends GenericConversionService {
|
|||
addConverter(String.class, Character.class, new StringToCharacterConverter());
|
||||
addConverter(String.class, Locale.class, new StringToLocaleConverter());
|
||||
addConverter(Number.class, Character.class, new NumberToCharacterConverter());
|
||||
addConverter(Date.class, Calendar.class, new DateToCalendarConverter());
|
||||
addConverter(Calendar.class, Date.class, new CalendarToDateConverter());
|
||||
JodaTimeConverters.addConverters(this);
|
||||
addConverter(Object.class, String.class, new ObjectToStringConverter());
|
||||
addConverterFactory(String.class, Number.class, new StringToNumberConverterFactory());
|
||||
addConverterFactory(String.class, Enum.class, new StringToEnumConverterFactory());
|
||||
|
|
|
|||
|
|
@ -42,8 +42,6 @@ import org.springframework.util.ClassUtils;
|
|||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
* @see #addConverter(Converter)
|
||||
* @see #addConverterFactory(ConverterFactory)
|
||||
*/
|
||||
public class GenericConversionService implements ConversionService, ConverterRegistry {
|
||||
|
||||
|
|
@ -53,7 +51,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
|||
}
|
||||
};
|
||||
|
||||
private final Map<Class, Map<Class, GenericConverter>> sourceTypeConverters = new HashMap<Class, Map<Class, GenericConverter>>(36);
|
||||
private final Map<Class<?>, Map<Class<?>, GenericConverter>> sourceTypeConverters = new HashMap<Class<?>, Map<Class<?>, GenericConverter>>(36);
|
||||
|
||||
private ConversionService parent;
|
||||
|
||||
|
|
@ -90,8 +88,8 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
|||
* JavaBean-friendly alternative to calling {@link #addConverter(Converter)}.
|
||||
* @see #addConverter(Converter)
|
||||
*/
|
||||
public void setConverters(Set<Converter> converters) {
|
||||
for (Converter converter : converters) {
|
||||
public void setConverters(Set<Converter<?, ?>> converters) {
|
||||
for (Converter<?, ?> converter : converters) {
|
||||
addConverter(converter);
|
||||
}
|
||||
}
|
||||
|
|
@ -101,8 +99,8 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
|||
* JavaBean-friendly alternative to calling {@link #addConverterFactory(ConverterFactory)}.
|
||||
* @see #addConverterFactory(ConverterFactory)
|
||||
*/
|
||||
public void setConverterFactories(Set<ConverterFactory> converters) {
|
||||
for (ConverterFactory converterFactory : converters) {
|
||||
public void setConverterFactories(Set<ConverterFactory<?, ?>> converters) {
|
||||
for (ConverterFactory<?, ?> converterFactory : converters) {
|
||||
addConverterFactory(converterFactory);
|
||||
}
|
||||
}
|
||||
|
|
@ -124,24 +122,24 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
|||
// implementing ConverterRegistry
|
||||
|
||||
public void addConverter(Converter<?, ?> converter) {
|
||||
Class[] typeInfo = getRequiredTypeInfo(converter, Converter.class);
|
||||
Class<?>[] typeInfo = getRequiredTypeInfo(converter, Converter.class);
|
||||
if (typeInfo == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to the determine sourceType <S> and targetType <T> your Converter<S, T> converts between; declare these types or implement ConverterInfo");
|
||||
}
|
||||
Class sourceType = typeInfo[0];
|
||||
Class targetType = typeInfo[1];
|
||||
Class<?> sourceType = typeInfo[0];
|
||||
Class<?> targetType = typeInfo[1];
|
||||
addConverter(sourceType, targetType, converter);
|
||||
}
|
||||
|
||||
public void addConverterFactory(ConverterFactory<?, ?> converterFactory) {
|
||||
Class[] typeInfo = getRequiredTypeInfo(converterFactory, ConverterFactory.class);
|
||||
Class<?>[] typeInfo = getRequiredTypeInfo(converterFactory, ConverterFactory.class);
|
||||
if (typeInfo == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to the determine sourceType <S> and targetRangeType R your ConverterFactory<S, R> converts between; declare these types or implement ConverterInfo");
|
||||
}
|
||||
Class sourceType = typeInfo[0];
|
||||
Class targetType = typeInfo[1];
|
||||
Class<?> sourceType = typeInfo[0];
|
||||
Class<?> targetType = typeInfo[1];
|
||||
addConverterFactory(sourceType, targetType, converterFactory);
|
||||
}
|
||||
|
||||
|
|
@ -155,6 +153,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
|||
return canConvert(TypeDescriptor.valueOf(sourceType), TypeDescriptor.valueOf(targetType));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T convert(Object source, Class<T> targetType) {
|
||||
return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType));
|
||||
}
|
||||
|
|
@ -279,46 +278,46 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
|||
Assert.notNull(targetType, "The targetType to convert to is required");
|
||||
}
|
||||
|
||||
private Class[] getRequiredTypeInfo(Object converter, Class genericIfc) {
|
||||
private Class<?>[] getRequiredTypeInfo(Object converter, Class<?> genericIfc) {
|
||||
return GenericTypeResolver.resolveTypeArguments(converter.getClass(), genericIfc);
|
||||
}
|
||||
|
||||
private GenericConverter findConverterByClassPair(Class sourceType, Class targetType) {
|
||||
private GenericConverter findConverterByClassPair(Class<?> sourceType, Class<?> targetType) {
|
||||
if (sourceType.isInterface()) {
|
||||
LinkedList<Class> classQueue = new LinkedList<Class>();
|
||||
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
|
||||
classQueue.addFirst(sourceType);
|
||||
while (!classQueue.isEmpty()) {
|
||||
Class currentClass = classQueue.removeLast();
|
||||
Map<Class, GenericConverter> converters = getConvertersForSource(currentClass);
|
||||
Class<?> currentClass = classQueue.removeLast();
|
||||
Map<Class<?>, GenericConverter> converters = getConvertersForSource(currentClass);
|
||||
GenericConverter converter = getConverter(converters, targetType);
|
||||
if (converter != null) {
|
||||
return converter;
|
||||
}
|
||||
Class[] interfaces = currentClass.getInterfaces();
|
||||
for (Class ifc : interfaces) {
|
||||
Class<?>[] interfaces = currentClass.getInterfaces();
|
||||
for (Class<?> ifc : interfaces) {
|
||||
classQueue.addFirst(ifc);
|
||||
}
|
||||
}
|
||||
Map<Class, GenericConverter> objectConverters = getConvertersForSource(Object.class);
|
||||
Map<Class<?>, GenericConverter> objectConverters = getConvertersForSource(Object.class);
|
||||
return getConverter(objectConverters, targetType);
|
||||
} else {
|
||||
LinkedList<Class> classQueue = new LinkedList<Class>();
|
||||
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
|
||||
classQueue.addFirst(sourceType);
|
||||
while (!classQueue.isEmpty()) {
|
||||
Class currentClass = classQueue.removeLast();
|
||||
Map<Class, GenericConverter> converters = getConvertersForSource(currentClass);
|
||||
Class<?> currentClass = classQueue.removeLast();
|
||||
Map<Class<?>, GenericConverter> converters = getConvertersForSource(currentClass);
|
||||
GenericConverter converter = getConverter(converters, targetType);
|
||||
if (converter != null) {
|
||||
return converter;
|
||||
}
|
||||
if (currentClass.isArray()) {
|
||||
Class componentType = ClassUtils.resolvePrimitiveIfNecessary(currentClass.getComponentType());
|
||||
Class<?> componentType = ClassUtils.resolvePrimitiveIfNecessary(currentClass.getComponentType());
|
||||
if (componentType.getSuperclass() != null) {
|
||||
classQueue.addFirst(Array.newInstance(componentType.getSuperclass(), 0).getClass());
|
||||
}
|
||||
} else {
|
||||
Class[] interfaces = currentClass.getInterfaces();
|
||||
for (Class ifc : interfaces) {
|
||||
Class<?>[] interfaces = currentClass.getInterfaces();
|
||||
for (Class<?> ifc : interfaces) {
|
||||
classQueue.addFirst(ifc);
|
||||
}
|
||||
if (currentClass.getSuperclass() != null) {
|
||||
|
|
@ -330,56 +329,56 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
|||
}
|
||||
}
|
||||
|
||||
private Map<Class, GenericConverter> getSourceMap(Class sourceType) {
|
||||
Map<Class, GenericConverter> sourceMap = sourceTypeConverters.get(sourceType);
|
||||
private Map<Class<?>, GenericConverter> getSourceMap(Class<?> sourceType) {
|
||||
Map<Class<?>, GenericConverter> sourceMap = sourceTypeConverters.get(sourceType);
|
||||
if (sourceMap == null) {
|
||||
sourceMap = new HashMap<Class, GenericConverter>();
|
||||
sourceMap = new HashMap<Class<?>, GenericConverter>();
|
||||
this.sourceTypeConverters.put(sourceType, sourceMap);
|
||||
}
|
||||
return sourceMap;
|
||||
}
|
||||
|
||||
private Map<Class, GenericConverter> getConvertersForSource(Class sourceType) {
|
||||
Map<Class, GenericConverter> converters = this.sourceTypeConverters.get(sourceType);
|
||||
private Map<Class<?>, GenericConverter> getConvertersForSource(Class<?> sourceType) {
|
||||
Map<Class<?>, GenericConverter> converters = this.sourceTypeConverters.get(sourceType);
|
||||
if (converters == null) {
|
||||
converters = Collections.emptyMap();
|
||||
}
|
||||
return converters;
|
||||
}
|
||||
|
||||
private GenericConverter getConverter(Map<Class, GenericConverter> converters, Class targetType) {
|
||||
private GenericConverter getConverter(Map<Class<?>, GenericConverter> converters, Class<?> targetType) {
|
||||
if (targetType.isInterface()) {
|
||||
LinkedList<Class> classQueue = new LinkedList<Class>();
|
||||
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
|
||||
classQueue.addFirst(targetType);
|
||||
while (!classQueue.isEmpty()) {
|
||||
Class currentClass = classQueue.removeLast();
|
||||
Class<?> currentClass = classQueue.removeLast();
|
||||
GenericConverter converter = converters.get(currentClass);
|
||||
if (converter != null) {
|
||||
return converter;
|
||||
}
|
||||
Class[] interfaces = currentClass.getInterfaces();
|
||||
for (Class ifc : interfaces) {
|
||||
Class<?>[] interfaces = currentClass.getInterfaces();
|
||||
for (Class<?> ifc : interfaces) {
|
||||
classQueue.addFirst(ifc);
|
||||
}
|
||||
}
|
||||
return converters.get(Object.class);
|
||||
} else {
|
||||
LinkedList<Class> classQueue = new LinkedList<Class>();
|
||||
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
|
||||
classQueue.addFirst(targetType);
|
||||
while (!classQueue.isEmpty()) {
|
||||
Class currentClass = classQueue.removeLast();
|
||||
Class<?> currentClass = classQueue.removeLast();
|
||||
GenericConverter converter = converters.get(currentClass);
|
||||
if (converter != null) {
|
||||
return converter;
|
||||
}
|
||||
if (currentClass.isArray()) {
|
||||
Class componentType = ClassUtils.resolvePrimitiveIfNecessary(currentClass.getComponentType());
|
||||
Class<?> componentType = ClassUtils.resolvePrimitiveIfNecessary(currentClass.getComponentType());
|
||||
if (componentType.getSuperclass() != null) {
|
||||
classQueue.addFirst(Array.newInstance(componentType.getSuperclass(), 0).getClass());
|
||||
}
|
||||
} else {
|
||||
Class[] interfaces = currentClass.getInterfaces();
|
||||
for (Class ifc : interfaces) {
|
||||
Class<?>[] interfaces = currentClass.getInterfaces();
|
||||
for (Class<?> ifc : interfaces) {
|
||||
classQueue.addFirst(ifc);
|
||||
}
|
||||
if (currentClass.getSuperclass() != null) {
|
||||
|
|
@ -391,11 +390,12 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private final class ConverterAdapter implements GenericConverter {
|
||||
|
||||
private final Converter converter;
|
||||
|
||||
public ConverterAdapter(Converter converter) {
|
||||
public ConverterAdapter(Converter<?, ?> converter) {
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
|
|
@ -407,11 +407,12 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private final class ConverterFactoryAdapter implements GenericConverter {
|
||||
|
||||
private final ConverterFactory converterFactory;
|
||||
|
||||
public ConverterFactoryAdapter(ConverterFactory converterFactory) {
|
||||
public ConverterFactoryAdapter(ConverterFactory<?, ?> converterFactory) {
|
||||
this.converterFactory = converterFactory;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,130 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
import org.joda.time.DateMidnight;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.LocalDateTime;
|
||||
import org.joda.time.LocalTime;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
class JodaTimeConverters {
|
||||
|
||||
public static void addConverters(GenericConversionService registry) {
|
||||
if (!isJodaTimePresent()) {
|
||||
return;
|
||||
}
|
||||
registry.addConverter(DateTime.class, LocalDate.class, new DateTimeToLocalDateConverter());
|
||||
registry.addConverter(LocalDate.class, DateTime.class, new LocalDateToDateTimeConverter());
|
||||
|
||||
registry.addConverter(DateTime.class, LocalTime.class, new DateTimeToLocalTimeConverter());
|
||||
registry.addConverter(LocalTime.class, DateTime.class, new LocalTimeToDateTimeConverter());
|
||||
|
||||
registry.addConverter(DateTime.class, LocalDateTime.class, new DateTimeToLocalDateTimeConverter());
|
||||
registry.addConverter(LocalDateTime.class, DateTime.class, new LocalDateTimeToDateTimeConverter());
|
||||
|
||||
registry.addConverter(DateTime.class, DateMidnight.class, new DateTimeToDateMidnightConverter());
|
||||
registry.addConverter(DateMidnight.class, Date.class, new DateMidnightToDateTimeConverter());
|
||||
|
||||
registry.addConverter(DateTime.class, Date.class, new DateTimeToDateConverter());
|
||||
registry.addConverter(Date.class, DateTime.class, new DateToDateTimeConverter());
|
||||
|
||||
registry.addConverter(DateTime.class, Calendar.class, new DateTimeToCalendarConverter());
|
||||
registry.addConverter(Calendar.class, DateTime.class, new CalendarToDateTimeConverter());
|
||||
}
|
||||
|
||||
private static boolean isJodaTimePresent() {
|
||||
return ClassUtils.isPresent("org.joda.time.DateTime", JodaTimeConverters.class.getClassLoader());
|
||||
}
|
||||
|
||||
private static class DateTimeToLocalDateConverter implements Converter<DateTime, LocalDate> {
|
||||
public LocalDate convert(DateTime source) {
|
||||
return source.toLocalDate();
|
||||
}
|
||||
}
|
||||
|
||||
private static class LocalDateToDateTimeConverter implements Converter<LocalDate, DateTime> {
|
||||
public DateTime convert(LocalDate source) {
|
||||
return source.toDateTimeAtStartOfDay();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DateTimeToLocalTimeConverter implements Converter<DateTime, LocalTime> {
|
||||
public LocalTime convert(DateTime source) {
|
||||
return source.toLocalTime();
|
||||
}
|
||||
}
|
||||
|
||||
private static class LocalTimeToDateTimeConverter implements Converter<LocalTime, DateTime> {
|
||||
public DateTime convert(LocalTime source) {
|
||||
return source.toDateTimeToday();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DateTimeToLocalDateTimeConverter implements Converter<DateTime, LocalDateTime> {
|
||||
public LocalDateTime convert(DateTime source) {
|
||||
return source.toLocalDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
private static class LocalDateTimeToDateTimeConverter implements Converter<LocalDateTime, DateTime> {
|
||||
public DateTime convert(LocalDateTime source) {
|
||||
return source.toDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DateTimeToDateMidnightConverter implements Converter<DateTime, DateMidnight> {
|
||||
public DateMidnight convert(DateTime source) {
|
||||
return source.toDateMidnight();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DateMidnightToDateTimeConverter implements Converter<DateMidnight, DateTime> {
|
||||
public DateTime convert(DateMidnight source) {
|
||||
return source.toDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DateTimeToDateConverter implements Converter<DateTime, Date> {
|
||||
public Date convert(DateTime source) {
|
||||
return source.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DateToDateTimeConverter implements Converter<Date, DateTime> {
|
||||
public DateTime convert(Date source) {
|
||||
return new DateTime(source);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DateTimeToCalendarConverter implements Converter<DateTime, Calendar> {
|
||||
public Calendar convert(DateTime source) {
|
||||
return source.toGregorianCalendar();
|
||||
}
|
||||
}
|
||||
|
||||
private static class CalendarToDateTimeConverter implements Converter<Calendar, DateTime> {
|
||||
public DateTime convert(Calendar source) {
|
||||
return new DateTime(source);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -77,8 +77,6 @@ import org.springframework.stereotype.Controller;
|
|||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.ui.format.date.DateFormatter;
|
||||
import org.springframework.ui.format.support.GenericFormatterRegistry;
|
||||
import org.springframework.util.SerializationTestUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
|
@ -487,37 +485,6 @@ public class ServletAnnotationControllerTests {
|
|||
assertEquals("myView-String:myDefaultName-typeMismatch-tb1-myOriginalValue", response.getContentAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void commandProvidingFormControllerWithFormatter() throws Exception {
|
||||
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
|
||||
@Override
|
||||
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
|
||||
GenericWebApplicationContext wac = new GenericWebApplicationContext();
|
||||
wac.registerBeanDefinition("controller", new RootBeanDefinition(MyCommandProvidingFormController.class));
|
||||
wac.registerBeanDefinition("viewResolver", new RootBeanDefinition(TestViewResolver.class));
|
||||
RootBeanDefinition registryDef = new RootBeanDefinition(GenericFormatterRegistry.class);
|
||||
registryDef.getPropertyValues().addPropertyValue("formatters", new DateFormatter("yyyy-MM-dd"));
|
||||
RootBeanDefinition initializerDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
|
||||
initializerDef.getPropertyValues().addPropertyValue("formatterRegistry", registryDef);
|
||||
initializerDef.getPropertyValues().addPropertyValue("validator", new RootBeanDefinition(LocalValidatorFactoryBean.class));
|
||||
RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
|
||||
adapterDef.getPropertyValues().addPropertyValue("webBindingInitializer", initializerDef);
|
||||
wac.registerBeanDefinition("handlerAdapter", adapterDef);
|
||||
wac.refresh();
|
||||
return wac;
|
||||
}
|
||||
};
|
||||
servlet.init(new MockServletConfig());
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
||||
request.addParameter("defaultName", "myDefaultName");
|
||||
request.addParameter("age", "value2");
|
||||
request.addParameter("date", "2007-10-02");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
servlet.service(request, response);
|
||||
assertEquals("myView-String:myDefaultName-typeMismatch-tb1-myOriginalValue", response.getContentAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typedCommandProvidingFormController() throws Exception {
|
||||
@SuppressWarnings("serial") DispatcherServlet servlet = new DispatcherServlet() {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
package org.springframework.web.bind.support;
|
||||
|
||||
import org.springframework.beans.PropertyEditorRegistrar;
|
||||
import org.springframework.ui.format.FormatterRegistry;
|
||||
import org.springframework.ui.format.FormattingService;
|
||||
import org.springframework.validation.BindingErrorProcessor;
|
||||
import org.springframework.validation.MessageCodesResolver;
|
||||
import org.springframework.validation.Validator;
|
||||
|
|
@ -46,7 +46,7 @@ public class ConfigurableWebBindingInitializer implements WebBindingInitializer
|
|||
|
||||
private Validator validator;
|
||||
|
||||
private FormatterRegistry formatterRegistry;
|
||||
private FormattingService formattingService;
|
||||
|
||||
private PropertyEditorRegistrar[] propertyEditorRegistrars;
|
||||
|
||||
|
|
@ -111,17 +111,17 @@ public class ConfigurableWebBindingInitializer implements WebBindingInitializer
|
|||
}
|
||||
|
||||
/**
|
||||
* Specify a FormatterRegistry which will apply to every DataBinder.
|
||||
* Specify a FormattingService which will apply to every DataBinder.
|
||||
*/
|
||||
public final void setFormatterRegistry(FormatterRegistry formatterRegistry) {
|
||||
this.formatterRegistry = formatterRegistry;
|
||||
public final void setFormattingService(FormattingService formattingService) {
|
||||
this.formattingService = formattingService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a FormatterRegistry which will apply to every DataBinder.
|
||||
* Return the FormattingService which will apply to every DataBinder.
|
||||
*/
|
||||
public final FormatterRegistry getFormatterRegistry() {
|
||||
return this.formatterRegistry;
|
||||
public final FormattingService getFormattingService() {
|
||||
return this.formattingService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -160,8 +160,8 @@ public class ConfigurableWebBindingInitializer implements WebBindingInitializer
|
|||
this.validator.supports(binder.getTarget().getClass())) {
|
||||
binder.setValidator(this.validator);
|
||||
}
|
||||
if (this.formatterRegistry != null) {
|
||||
binder.setFormatterRegistry(this.formatterRegistry);
|
||||
if (this.formattingService != null) {
|
||||
binder.setFormattingService(this.formattingService);
|
||||
}
|
||||
if (this.propertyEditorRegistrars != null) {
|
||||
for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue