001/**
002 * The MIT License (MIT)
003 *
004 * Copyright (c) 2017 tools4j.org (Marco Terzer)
005 *
006 * Permission is hereby granted, free of charge, to any person obtaining a copy
007 * of this software and associated documentation files (the "Software"), to deal
008 * in the Software without restriction, including without limitation the rights
009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010 * copies of the Software, and to permit persons to whom the Software is
011 * furnished to do so, subject to the following conditions:
012 *
013 * The above copyright notice and this permission notice shall be included in all
014 * copies or substantial portions of the Software.
015 *
016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
022 * SOFTWARE.
023 */
024package org.tools4j.spockito;
025
026import java.lang.reflect.Type;
027import java.math.BigDecimal;
028import java.math.BigInteger;
029import java.time.*;
030import java.util.*;
031import java.util.function.BiPredicate;
032import java.util.function.Function;
033
034/**
035 * Default value converter implementation for Spockito. All supported conversions are defined as a constant in
036 * {@link Converters}.
037 * <p>
038 * Constom value converter implementations may want to extend this class and add additional converters by overriding
039 * {@link #initConverterFunctions()}. Converters for single types can be registered via
040 * {@link #registerConverterFunction(Class, Function)} and the more generic
041 * {@link #registerConverter(BiPredicate, ValueConverter)}.
042 */
043public class SpockitoValueConverter implements ValueConverter {
044
045    public static final SpockitoValueConverter DEFAULT_INSTANCE = new SpockitoValueConverter();
046
047    private final Map<Class<?>, Function<? super String, ?>> convertersByType = new HashMap<>();
048    private final List<Map.Entry<BiPredicate<Class<?>, ? super Type>, ValueConverter>> convertersByPredicate = new ArrayList<>();
049
050    public SpockitoValueConverter() {
051        initConverterFunctions();
052    }
053
054    @Override
055    public <T> T convert(final Class<T> type, final Type genericType, final String value) {
056        if (value == null || "null".equals(value)) {
057            return null;
058        }
059        ValueConverter converter = converterByTypeOrNull(type);;
060        if (converter == null) {
061            converter = converterByPredicateOrNull(type, genericType);
062        }
063        if (converter == null) {
064            throw new IllegalArgumentException("No value converter is defined for type " + typeName(type, genericType));
065        }
066        try {
067            return converter.convert(type, genericType, value);
068        } catch (final Exception e) {
069            throw new IllegalArgumentException("Conversion to " + typeName(type, genericType) + " failed for value: " + value, e);
070        }
071    }
072
073    /**
074     * Registers converters for individual types via calls to
075     * {@link #registerConverterFunction(Class, Function)} and the more generic
076     * {@link #registerConverter(BiPredicate, ValueConverter)}. All converters registered by this method are defined by
077     * {@link Converters}.
078     */
079    protected void initConverterFunctions() {
080        registerConverterFunction(Object.class, Converters.OBJECT_CONVERTER);
081        registerConverterFunction(String.class, Converters.STRING_CONVERTER);
082        registerConverterFunction(long.class, Converters.LONG_CONVERTER);
083        registerConverterFunction(int.class, Converters.INTEGER_CONVERTER);
084        registerConverterFunction(short.class, Converters.SHORT_CONVERTER);
085        registerConverterFunction(byte.class, Converters.BYTE_CONVERTER);
086        registerConverterFunction(double.class, Converters.DOUBLE_CONVERTER);
087        registerConverterFunction(float.class, Converters.FLOAT_CONVERTER);
088        registerConverterFunction(char.class, Converters.CHAR_CONVERTER);
089        registerConverterFunction(boolean.class, Converters.BOOLEAN_CONVERTER);
090        registerConverterFunction(BigInteger.class, Converters.BIG_INTEGER_CONVERTER);
091        registerConverterFunction(BigDecimal.class, Converters.BIG_DECIMAL_CONVERTER);
092        registerConverterFunction(LocalDate.class, Converters.LOCAL_DATE_CONVERTER);
093        registerConverterFunction(LocalTime.class, Converters.LOCAL_TIME_CONVERTER);
094        registerConverterFunction(LocalDateTime.class, Converters.LOCAL_DATE_TIME_CONVERTER);
095        registerConverterFunction(ZonedDateTime.class, Converters.ZONED_DATE_TIME_CONVERTER);
096        registerConverterFunction(OffsetDateTime.class, Converters.OFFSET_DATE_TIME_CONVERTER);
097        registerConverterFunction(Instant.class, Converters.INSTANT_CONVERTER);
098        registerConverterFunction(Date.class, Converters.DATE_CONVERTER);
099        registerConverterFunction(java.sql.Date.class, Converters.SQL_DATE_CONVERTER);
100        registerConverterFunction(java.sql.Time.class, Converters.SQL_TIME_CONVERTER);
101        registerConverterFunction(java.sql.Timestamp.class, Converters.SQL_TIMESTAMP_CONVERTER);
102        registerConverterFunction(StringBuilder.class, Converters.STRING_BUILDER_CONVERTER);
103        registerConverterFunction(StringBuffer.class, Converters.STRING_BUFFER_CONVERTER);
104
105        registerConverter((t, g) -> t.isEnum(), Converters.ENUM_CONVERTER);
106        registerConverter((t, g) -> t == Optional.class, new Converters.OptionalConverter(this));
107        registerConverter((t, g) -> t.isArray(), new Converters.ArrayConverter(this));
108        registerConverter((t, g) -> Class.class.isAssignableFrom(t), Converters.CLASS_CONVERTER);
109        registerConverter((t, g) -> Collection.class.isAssignableFrom(t), new Converters.CollectionConverter(this));
110        registerConverter((t, g) -> Map.class.isAssignableFrom(t), new Converters.MapConverter(this));
111        registerConverter((t, g) -> Converters.BeanConverter.isBeanClass(t), new Converters.BeanConverter(this));
112    }
113
114    /**
115     * Register a conversion fuction for the specified type. The function is applied for types exactly matching the
116     * given type only (but not to supertypes of this type).
117     *
118     * @param type          the target type
119     * @param converter     a function converting from string to target type
120     * @param <T>           the targe parameter
121     */
122    protected <T> void registerConverterFunction(final Class<T> type, final Function<? super String, ? extends T> converter) {
123        convertersByType.put(type, converter);
124        if (type.isPrimitive()) {
125            convertersByType.put(Primitives.boxingTypeFor(type), converter);
126        }
127    }
128
129    /**
130     * Register a value converter for some subgroup of types selected for by the given predicate. The two arguments
131     * passed to the applicable predicate are type and generic type of the target value. The predicate returns true if
132     * the specified value converter is applicable for such a value.
133     * <p>
134     * Value converters registered via this method will be queried in registration order. If two converters are
135     * applicable to some value, the first whose predicate returns true will be used.
136     *
137     * @param applicable    a predicate returning true if the specified converter can be applied for a value of the
138     *                      type/generic-type passed to the predicate, and false otherwise
139     * @param converter     a value converter from string to target type
140     */
141    protected void registerConverter(final BiPredicate<Class<?>, ? super Type> applicable, final ValueConverter converter) {
142        convertersByPredicate.add(new AbstractMap.SimpleImmutableEntry<>(applicable, converter));
143    }
144
145    private ValueConverter converterByTypeOrNull(final Class<?> type) {
146        final Function<? super String, ?> function = convertersByType.get(type);
147        if (function != null) {
148            final Function<? super String, ?> f = function;
149            return new ValueConverter() {
150                @Override
151                public <T> T convert(final Class<T> type, final Type genericType, final String value) {
152                    final Class<T> boxingType = type.isPrimitive() ? Primitives.boxingTypeFor(type) : type;
153                    return boxingType.cast(f.apply(value));
154                }
155            };
156        }
157        return null;
158    }
159
160    private ValueConverter converterByPredicateOrNull(final Class<?> type, final Type genericType) {
161        return convertersByPredicate.stream()
162                .filter(e -> e.getKey().test(type, genericType))
163                .findFirst()
164                .map(e -> e == null ? null : e.getValue())
165                .orElse(null);
166    }
167
168    private static String typeName(final Class<?> type, final Type genericType) {
169        return type == genericType || genericType == null ? type.getName() : genericType.toString();
170    }
171
172}