/*
* Copyright 2015 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.gradle.internal.typeconversion;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import org.gradle.internal.Cast;
import org.gradle.internal.exceptions.DiagnosticsVisitor;
import org.gradle.internal.file.PathToFileResolver;
import java.io.File;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Map;
public class DefaultTypeConverter implements TypeConverter {
private static final Map<Class<?>, Class<?>> UNBOXED_TYPES = ImmutableMap.<Class<?>, Class<?>>builder()
.put(Byte.class, byte.class)
.put(Short.class, short.class)
.put(Integer.class, int.class)
.put(Boolean.class, boolean.class)
.put(Float.class, float.class)
.put(Character.class, char.class)
.put(Double.class, double.class)
.put(Long.class, long.class)
.build();
private final Map<Class<?>, NotationParser<Object, ?>> parsers = Maps.newHashMap();
private static <T> NotationParser<Object, T> build(NotationConverter<Object, T> converter, Class<T> type) {
return NotationParserBuilder
.toType(type)
.noImplicitConverters()
.converter(converter)
.toComposite();
}
private <T> void registerConverter(NotationConverter<Object, T> converter, Class<T> type) {
parsers.put(type, build(converter, type));
}
private <T> void registerStringConverter(NotationConverter<String, T> converter, Class<T> type) {
parsers.put(type, build(new CharSequenceNotationConverter<Object, T>(converter), type));
}
private void registerConverters() {
registerConverter(new DoubleNumberConverter(Double.class), Double.class);
registerConverter(new DoubleNumberConverter(double.class), double.class);
registerConverter(new FloatNumberConverter(Float.class), Float.class);
registerConverter(new FloatNumberConverter(float.class), float.class);
registerConverter(new IntegerNumberConverter(Integer.class), Integer.class);
registerConverter(new IntegerNumberConverter(int.class), int.class);
registerConverter(new LongNumberConverter(Long.class), Long.class);
registerConverter(new LongNumberConverter(long.class), long.class);
registerConverter(new ShortNumberConverter(Short.class), Short.class);
registerConverter(new ShortNumberConverter(short.class), short.class);
registerConverter(new ByteNumberConverter(Byte.class), Byte.class);
registerConverter(new ByteNumberConverter(byte.class), byte.class);
registerConverter(new BigDecimalNumberConverter(), BigDecimal.class);
registerConverter(new BigIntegerNumberConverter(), BigInteger.class);
CharSequenceConverter<Boolean> booleanConverter = new BooleanConverter();
registerStringConverter(booleanConverter, Boolean.class);
registerStringConverter(booleanConverter, boolean.class);
registerStringConverter(new CharacterConverter(Character.class, Character.class), Character.class);
registerStringConverter(new CharacterConverter(Character.class, char.class), char.class);
registerConverter(new StringConverter(), String.class);
}
private abstract static class CharSequenceConverter<T> implements NotationConverter<String, T> {
final Class<T> type;
public CharSequenceConverter(Class<T> type) {
this.type = type;
}
public void describe(DiagnosticsVisitor visitor) {
visitor.candidate("A String or CharSequence");
visitor.candidate("A " + type.getSimpleName());
}
}
private static class StringConverter implements NotationConverter<Object, String> {
@Override
public void convert(Object notation, NotationConvertResult<? super String> result) throws TypeConversionException {
if (notation instanceof CharSequence || notation instanceof Number || notation instanceof Boolean || notation instanceof Character || notation instanceof File) {
result.converted(notation.toString());
}
}
@Override
public void describe(DiagnosticsVisitor visitor) {
visitor.candidate("A String or CharSequence or Character");
visitor.candidate("Any Number");
visitor.candidate("A Boolean");
visitor.candidate("A File");
}
}
private abstract static class NumberConverter<T extends Number> implements NotationConverter<Object, T> {
private final Class<T> type;
protected NumberConverter(Class<T> type) {
this.type = type;
}
@Override
public void describe(DiagnosticsVisitor visitor) {
visitor.candidate("A String or CharSequence");
visitor.candidate("Any Number");
}
public void convert(Object notation, NotationConvertResult<? super T> result) throws TypeConversionException {
if (notation instanceof CharSequence) {
try {
convertNumberToNumber(new BigDecimal(notation.toString().trim()), result);
} catch (ArithmeticException e) {
throw new TypeConversionException(String.format("Cannot convert value '%s' to type %s",
notation, type.getSimpleName()), e);
} catch (NumberFormatException e) {
throw new TypeConversionException(String.format("Cannot convert value '%s' to type %s",
notation, type.getSimpleName()), e);
}
} else if (notation instanceof Number) {
try {
convertNumberToNumber(toBigDecimal((Number) notation), result);
} catch (ArithmeticException e) {
throw new TypeConversionException(String.format("Cannot convert value '%s' to type %s",
notation, type.getSimpleName()), e);
}
}
}
private static BigDecimal toBigDecimal(Number notation) {
if (notation instanceof BigDecimal) {
return (BigDecimal) notation;
}
if (notation instanceof BigInteger) {
return new BigDecimal((BigInteger) notation);
}
if (notation instanceof Float) {
return new BigDecimal(notation.floatValue());
}
if (notation instanceof Double) {
return new BigDecimal(notation.doubleValue());
}
return new BigDecimal(notation.longValue());
}
protected abstract void convertNumberToNumber(BigDecimal n, NotationConvertResult<? super T> result);
}
public DefaultTypeConverter(final PathToFileResolver fileResolver) {
registerConverter(new CharSequenceNotationConverter<Object, File>(new CharSequenceConverter<File>(File.class) {
public void convert(String notation, NotationConvertResult<? super File> result) throws TypeConversionException {
result.converted(fileResolver.resolve(notation));
}
}), File.class);
registerConverters();
}
public Object convert(Object notation, Class<?> type, boolean primitive) throws TypeConversionException {
if (type.isInstance(notation)) {
return notation;
}
if (!primitive && notation == null) {
return null;
}
if (type.isEnum()) {
Class<? extends Enum> enumType = Cast.uncheckedCast(type);
return convertEnum(enumType, notation);
}
NotationParser<Object, ?> parser;
parser = parsers.get(primitive ? UNBOXED_TYPES.get(type) : type);
if (parser == null) {
throw new IllegalArgumentException("Don't know how to convert to type " + type.getName());
}
return parser.parseNotation(notation);
}
private <T extends Enum<T>> T convertEnum(Class<T> type, Object notation) {
return NotationParserBuilder
.toType(type)
.noImplicitConverters()
.fromCharSequence(new EnumFromCharSequenceNotationParser<T>(type))
.toComposite()
.parseNotation(notation);
}
private static class DoubleNumberConverter extends NumberConverter<Double> {
public DoubleNumberConverter(Class<Double> cl) {
super(cl);
}
@Override
protected void convertNumberToNumber(BigDecimal n, NotationConvertResult<? super Double> result) {
result.converted(n.doubleValue());
}
}
private static class FloatNumberConverter extends NumberConverter<Float> {
public FloatNumberConverter(Class<Float> cl) {
super(cl);
}
@Override
protected void convertNumberToNumber(BigDecimal n, NotationConvertResult<? super Float> result) {
result.converted(n.floatValue());
}
}
private static class IntegerNumberConverter extends NumberConverter<Integer> {
public IntegerNumberConverter(Class<Integer> cl) {
super(cl);
}
@Override
protected void convertNumberToNumber(BigDecimal n, NotationConvertResult<? super Integer> result) {
result.converted(n.intValueExact());
}
}
private static class LongNumberConverter extends NumberConverter<Long> {
public LongNumberConverter(Class<Long> cl) {
super(cl);
}
@Override
protected void convertNumberToNumber(BigDecimal n, NotationConvertResult<? super Long> result) {
result.converted(n.longValueExact());
}
}
private static class ShortNumberConverter extends NumberConverter<Short> {
public ShortNumberConverter(Class<Short> cl) {
super(cl);
}
@Override
protected void convertNumberToNumber(BigDecimal n, NotationConvertResult<? super Short> result) {
result.converted(n.shortValueExact());
}
}
private static class ByteNumberConverter extends NumberConverter<Byte> {
public ByteNumberConverter(Class<Byte> cl) {
super(cl);
}
@Override
protected void convertNumberToNumber(BigDecimal n, NotationConvertResult<? super Byte> result) {
result.converted(n.byteValueExact());
}
}
private static class BigDecimalNumberConverter extends NumberConverter<BigDecimal> {
public BigDecimalNumberConverter() {
super(BigDecimal.class);
}
@Override
protected void convertNumberToNumber(BigDecimal n, NotationConvertResult<? super BigDecimal> result) {
result.converted(n);
}
}
private static class BigIntegerNumberConverter extends NumberConverter<BigInteger> {
public BigIntegerNumberConverter() {
super(BigInteger.class);
}
@Override
protected void convertNumberToNumber(BigDecimal n, NotationConvertResult<? super BigInteger> result) {
result.converted(n.toBigIntegerExact());
}
}
private static class BooleanConverter extends CharSequenceConverter<Boolean> {
public BooleanConverter() {
super(Boolean.class);
}
public void convert(String notation, NotationConvertResult<? super Boolean> result) throws TypeConversionException {
result.converted("true".equals(notation));
}
}
private class CharacterConverter extends CharSequenceConverter<Character> {
private final Class<Character> target;
public CharacterConverter(Class<Character> boxed, Class<Character> target) {
super(boxed);
this.target = target;
}
public void convert(String notation, NotationConvertResult<? super Character> result) throws TypeConversionException {
if (notation.length() != 1) {
throw new TypeConversionException(String.format("Cannot convert string value '%s' with length %d to type %s",
notation, notation.length(), target.getSimpleName()));
}
result.converted(notation.charAt(0));
}
}
}