/*
* Copyright 2004-2010 the Seasar Foundation and the Others.
*
* 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.slim3.util;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A copy options.
*
* @author higa
* @since 1.0.0
*
*/
public class CopyOptions {
/**
* The empty strings.
*/
protected static final String[] EMPTY_STRINGS = new String[0];
/**
* The included property names.
*/
protected String[] includedPropertyNames = EMPTY_STRINGS;
/**
* The excluded property names.
*/
protected String[] excludedPropertyNames = EMPTY_STRINGS;
/**
* Whether null is excluded.
*/
protected boolean excludeNull = false;
/**
* Whether empty string is copied.
*/
protected boolean excludeEmptyString = false;
/**
* The converters that are bound to the specific property.
*/
protected Map<String, Converter<?>> converterMap =
new HashMap<String, Converter<?>>();
/**
* The converters that are not bound to any properties.
*/
protected List<Converter<?>> converters = new ArrayList<Converter<?>>();
/**
* Specifies the included property names.
*
* @param propertyNames
* the included property names
* @return this instance
*/
public CopyOptions include(CharSequence... propertyNames) {
final int length = propertyNames.length;
includedPropertyNames = new String[length];
for (int i = 0; i < length; i++) {
includedPropertyNames[i] = propertyNames[i].toString();
}
return this;
}
/**
* Specifies the excluded property names.
*
* @param propertyNames
* the excluded property names
* @return this instance
*/
public CopyOptions exclude(CharSequence... propertyNames) {
final int length = propertyNames.length;
excludedPropertyNames = new String[length];
for (int i = 0; i < length; i++) {
excludedPropertyNames[i] = propertyNames[i].toString();
}
return this;
}
/**
* Specifies whether null is excluded.
*
* @return this instance
*/
public CopyOptions excludeNull() {
excludeNull = true;
return this;
}
/**
* Specifies whether empty string is excluded.
*
* @return this instance
*/
public CopyOptions excludeEmptyString() {
excludeEmptyString = true;
return this;
}
/**
* Specifies the converter.
*
* @param converter
* the converter
* @param propertyNames
* the property names
* @return this instance
* @throws NullPointerException
* if the converter parameter is null
*/
public CopyOptions converter(Converter<?> converter,
CharSequence... propertyNames) throws NullPointerException {
if (converter == null) {
throw new NullPointerException("The converter parameter is null.");
}
if (propertyNames.length == 0) {
converters.add(converter);
} else {
for (CharSequence name : propertyNames) {
converterMap.put(name.toString(), converter);
}
}
return this;
}
/**
* Specifies the converter for {@link Date}.
*
* @param pattern
* the pattern for {@link SimpleDateFormat}
* @param propertyNames
* the property names
* @return this instance
*/
public CopyOptions dateConverter(String pattern,
CharSequence... propertyNames) {
return converter(new DateConverter(pattern), propertyNames);
}
/**
* Specifies the number converter.
*
* @param pattern
* the pattern for {@link DecimalFormat}
* @param propertyNames
* the property names
* @return this instance
*/
public CopyOptions numberConverter(String pattern,
CharSequence... propertyNames) {
return converter(new NumberConverter(pattern), propertyNames);
}
/**
* Determines if the property is target.
*
* @param name
* the property name
* @return whether the property is target
*/
protected boolean isTargetProperty(String name) {
if (includedPropertyNames.length > 0) {
for (String s : includedPropertyNames) {
if (s.equals(name)) {
for (String s2 : excludedPropertyNames) {
if (s2.equals(name)) {
return false;
}
}
return true;
}
}
return false;
}
if (excludedPropertyNames.length > 0) {
for (String s : excludedPropertyNames) {
if (s.equals(name)) {
return false;
}
}
return true;
}
return true;
}
/**
* Determines if the value is target.
*
* @param value
* the value
* @return whether the value is target
*/
protected boolean isTargetValue(Object value) {
if (value == null) {
return !excludeNull;
}
if ("".equals(value)) {
return !excludeEmptyString;
}
return true;
}
/**
* Converts the value.
*
* @param value
* the value
* @param propertyName
* the property name
* @param destPropertyClass
* the destination property class
* @return the converted value
* @throws IllegalArgumentException
* if an exception occurred while converting
*
*/
protected Object convertValue(Object value, String propertyName,
Class<?> destPropertyClass) throws IllegalArgumentException {
if (value == null) {
return null;
}
try {
if (value.getClass() == String.class) {
return convertString(
(String) value,
propertyName,
destPropertyClass);
}
return convertObject(value, propertyName, destPropertyClass);
} catch (Throwable cause) {
throw new IllegalArgumentException("The value("
+ value
+ ") of the property("
+ propertyName
+ ") can not be converted. Error message: "
+ cause.getMessage(), cause);
}
}
/**
* Converts string value.
*
* @param value
* string value
* @param propertyName
* the property name
* @param destPropertyClass
* the destination property class
* @return converted value
*/
protected Object convertString(String value, String propertyName,
Class<?> destPropertyClass) {
Converter<?> converter = converterMap.get(propertyName);
if (converter != null) {
return converter.getAsObject(value);
}
if (destPropertyClass == null) {
return value;
}
if (destPropertyClass == String.class) {
return value;
}
converter = findConverter(destPropertyClass);
if (converter != null) {
return converter.getAsObject(value);
}
return value;
}
/**
* Converts object value.
*
* @param value
* object value
* @param propertyName
* the property name
* @param destPropertyClass
* the destination property class
* @return converted value
*/
protected Object convertObject(Object value, String propertyName,
Class<?> destPropertyClass) {
Converter<?> converter = converterMap.get(propertyName);
if (converter != null) {
return converter.getAsString(value);
}
if (destPropertyClass == null) {
return value;
}
if (destPropertyClass != String.class) {
return value;
}
converter = findConverter(value.getClass());
if (converter != null) {
return converter.getAsString(value);
}
return value;
}
/**
* Finds the converter for the target class.
*
* @param targetClass
* the target class.
* @return converter
*/
protected Converter<?> findConverter(Class<?> targetClass) {
for (Class<?> clazz = targetClass; clazz != null
&& clazz != Object.class; clazz = clazz.getSuperclass()) {
for (Converter<?> c : converters) {
if (c.isTarget(clazz)) {
return c;
}
}
}
return null;
}
}