All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tools4j.spockito.table.SpockitoValueConverter Maven / Gradle / Ivy

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017-2021 tools4j.org (Marco Terzer)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.tools4j.spockito.table;

import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.MonthDay;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Period;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.regex.Pattern;

/**
 * Default value converter implementation for Spockito. All supported conversions are defined as a constant in
 * {@link Converters}.
 * 

* Constom value converter implementations may want to extend this class and add additional converters by overriding * {@link #initConverterFunctions()}. Converters for single types can be registered via * {@link #registerConverterFunction(Class, Function)} and the more generic * {@link #registerConverter(BiPredicate, ValueConverter)}. */ public class SpockitoValueConverter implements ValueConverter { public static final SpockitoValueConverter DEFAULT_INSTANCE = new SpockitoValueConverter(); private final Map, Function> convertersByType = new HashMap<>(); private final List, ? super Type>, ValueConverter>> convertersByPredicate = new ArrayList<>(); public SpockitoValueConverter() { initConverterFunctions(); } @Override public T convert(final Class type, final Type genericType, final String value) { if (value == null || "null".equals(value)) { return null; } ValueConverter converter = converterByTypeOrNull(type); if (converter == null) { converter = converterByPredicateOrNull(type, genericType); } if (converter == null) { throw new IllegalArgumentException("No value converter is defined for type " + typeName(type, genericType)); } try { return converter.convert(type, genericType, value); } catch (final Exception e) { throw new IllegalArgumentException("Conversion to " + typeName(type, genericType) + " failed for value: " + value, e); } } /** * Registers converters for individual types via calls to * {@link #registerConverterFunction(Class, Function)} and the more generic * {@link #registerConverter(BiPredicate, ValueConverter)}. All converters registered by this method are defined by * {@link Converters}. */ protected void initConverterFunctions() { registerConverterFunction(Object.class, Converters.OBJECT_CONVERTER); registerConverterFunction(String.class, Converters.STRING_CONVERTER); registerConverterFunction(long.class, Converters.LONG_CONVERTER); registerConverterFunction(int.class, Converters.INTEGER_CONVERTER); registerConverterFunction(short.class, Converters.SHORT_CONVERTER); registerConverterFunction(byte.class, Converters.BYTE_CONVERTER); registerConverterFunction(double.class, Converters.DOUBLE_CONVERTER); registerConverterFunction(float.class, Converters.FLOAT_CONVERTER); registerConverterFunction(char.class, Converters.CHAR_CONVERTER); registerConverterFunction(boolean.class, Converters.BOOLEAN_CONVERTER); registerConverterFunction(BigInteger.class, Converters.BIG_INTEGER_CONVERTER); registerConverterFunction(BigDecimal.class, Converters.BIG_DECIMAL_CONVERTER); registerConverterFunction(LocalDate.class, Converters.LOCAL_DATE_CONVERTER); registerConverterFunction(LocalTime.class, Converters.LOCAL_TIME_CONVERTER); registerConverterFunction(LocalDateTime.class, Converters.LOCAL_DATE_TIME_CONVERTER); registerConverterFunction(OffsetTime.class, Converters.OFFSET_TIME_CONVERTER); registerConverterFunction(OffsetDateTime.class, Converters.OFFSET_DATE_TIME_CONVERTER); registerConverterFunction(ZonedDateTime.class, Converters.ZONED_DATE_TIME_CONVERTER); registerConverterFunction(Instant.class, Converters.INSTANT_CONVERTER); registerConverterFunction(Month.class, Converters.MONTH_CONVERTER); registerConverterFunction(Year.class, Converters.YEAR_CONVERTER); registerConverterFunction(YearMonth.class, Converters.YEAR_MONTH_CONVERTER); registerConverterFunction(MonthDay.class, Converters.MONTH_DAY_CONVERTER); registerConverterFunction(DayOfWeek.class, Converters.DAY_OF_WEEK_CONVERTER); registerConverterFunction(Duration.class, Converters.DURATION_CONVERTER); registerConverterFunction(Period.class, Converters.PERIOD_CONVERTER); registerConverterFunction(ZoneOffset.class, Converters.ZONE_OFFSET_CONVERTER); registerConverterFunction(ZoneId.class, Converters.ZONE_ID_CONVERTER); registerConverterFunction(DateTimeFormatter.class, Converters.DATE_TIME_FORMATTER_CONVERTER); registerConverterFunction(Date.class, Converters.DATE_CONVERTER); registerConverterFunction(java.sql.Date.class, Converters.SQL_DATE_CONVERTER); registerConverterFunction(java.sql.Time.class, Converters.SQL_TIME_CONVERTER); registerConverterFunction(java.sql.Timestamp.class, Converters.SQL_TIMESTAMP_CONVERTER); registerConverterFunction(TimeUnit.class, Converters.TIME_UNIT_CONVERTER); registerConverterFunction(Pattern.class, Converters.PATTERN_CONVERTER); registerConverterFunction(StringBuilder.class, Converters.STRING_BUILDER_CONVERTER); registerConverterFunction(StringBuffer.class, Converters.STRING_BUFFER_CONVERTER); registerConverter((t, g) -> t.isEnum(), Converters.ENUM_CONVERTER); registerConverter((t, g) -> t == Optional.class, new Converters.OptionalConverter(this)); registerConverter((t, g) -> t.isArray(), new Converters.ArrayConverter(this)); registerConverter((t, g) -> Class.class.isAssignableFrom(t), Converters.CLASS_CONVERTER); registerConverter((t, g) -> Collection.class.isAssignableFrom(t), new Converters.CollectionConverter(this)); registerConverter((t, g) -> Map.class.isAssignableFrom(t), new Converters.MapConverter(this)); registerConverter((t, g) -> Converters.BeanConverter.isBeanClass(t), new Converters.BeanConverter(this)); } /** * Register a conversion fuction for the specified type. The function is applied for types exactly matching the * given type only (but not to supertypes of this type). * * @param type the target type * @param converter a function converting from string to target type * @param the targe parameter */ protected void registerConverterFunction(final Class type, final Function converter) { convertersByType.put(type, converter); if (type.isPrimitive()) { convertersByType.put(Primitives.boxingTypeFor(type), converter); } } /** * Register a value converter for some subgroup of types selected for by the given predicate. The two arguments * passed to the applicable predicate are type and generic type of the target value. The predicate returns true if * the specified value converter is applicable for such a value. *

* Value converters registered via this method will be queried in registration order. If two converters are * applicable to some value, the first whose predicate returns true will be used. * * @param applicable a predicate returning true if the specified converter can be applied for a value of the * type/generic-type passed to the predicate, and false otherwise * @param converter a value converter from string to target type */ protected void registerConverter(final BiPredicate, ? super Type> applicable, final ValueConverter converter) { convertersByPredicate.add(new AbstractMap.SimpleImmutableEntry<>(applicable, converter)); } private ValueConverter converterByTypeOrNull(final Class type) { final Function function = convertersByType.get(type); if (function != null) { return new ValueConverter() { @Override public T convert(final Class type, final Type genericType, final String value) { final Class boxingType = type.isPrimitive() ? Primitives.boxingTypeFor(type) : type; return boxingType.cast(function.apply(value)); } }; } return null; } private ValueConverter converterByPredicateOrNull(final Class type, final Type genericType) { return convertersByPredicate.stream() .filter(e -> e.getKey().test(type, genericType)) .findFirst() .map(Entry::getValue) .orElse(null); } private static String typeName(final Class type, final Type genericType) { return type == genericType || genericType == null ? type.getName() : genericType.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy