/* * 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)); } } }