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

io.smallrye.config.Converters Maven / Gradle / Ivy

/*
 * Copyright 2017 Red Hat, Inc.
 *
 * 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 io.smallrye.config;

import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.Comparator;
import java.util.Currency;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.UUID;
import java.util.function.IntFunction;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.eclipse.microprofile.config.spi.Converter;

import io.smallrye.config.common.AbstractDelegatingConverter;
import io.smallrye.config.common.AbstractSimpleDelegatingConverter;
import io.smallrye.config.common.utils.StringUtil;

/**
 * General converter utilities and constants.
 *
 * @author Jeff Mesnil (c) 2017 Red Hat inc.
 */
public final class Converters {
    private Converters() {
    }

    static final Converter STRING_CONVERTER = BuiltInConverter.of(0, newEmptyValueConverter(value -> value));

    static final Converter BOOLEAN_CONVERTER = BuiltInConverter.of(1, newTrimmingConverter(newEmptyValueConverter(
            value -> Boolean.valueOf(
                    "TRUE".equalsIgnoreCase(value)
                            || "1".equalsIgnoreCase(value)
                            || "YES".equalsIgnoreCase(value)
                            || "Y".equalsIgnoreCase(value)
                            || "ON".equalsIgnoreCase(value)
                            || "JA".equalsIgnoreCase(value)
                            || "J".equalsIgnoreCase(value)
                            || "SI".equalsIgnoreCase(value)
                            || "SIM".equalsIgnoreCase(value)
                            || "OUI".equalsIgnoreCase(value)))));

    static final Converter DOUBLE_CONVERTER = BuiltInConverter.of(2,
            newTrimmingConverter(newEmptyValueConverter(value -> {
                try {
                    return Double.valueOf(value);
                } catch (NumberFormatException nfe) {
                    throw ConfigMessages.msg.doubleExpected(value);
                }
            })));

    static final Converter FLOAT_CONVERTER = BuiltInConverter.of(3,
            newTrimmingConverter(newEmptyValueConverter(value -> {
                try {
                    return Float.valueOf(value);
                } catch (NumberFormatException nfe) {
                    throw ConfigMessages.msg.floatExpected(value);
                }
            })));

    static final Converter LONG_CONVERTER = BuiltInConverter.of(4,
            newTrimmingConverter(newEmptyValueConverter(value -> {
                try {
                    return Long.valueOf(value);
                } catch (NumberFormatException nfe) {
                    throw ConfigMessages.msg.longExpected(value);
                }
            })));

    static final Converter INTEGER_CONVERTER = BuiltInConverter.of(5,
            newTrimmingConverter(newEmptyValueConverter(value -> {
                try {
                    return Integer.valueOf(value);
                } catch (NumberFormatException nfe) {
                    throw ConfigMessages.msg.integerExpected(value);
                }
            })));

    static final Converter> CLASS_CONVERTER = BuiltInConverter.of(6,
            newTrimmingConverter(newEmptyValueConverter(value -> {
                try {
                    return Class.forName(value, true, SecuritySupport.getContextClassLoader());
                } catch (ClassNotFoundException e) {
                    throw ConfigMessages.msg.classConverterNotFound(e, value);
                }
            })));

    static final Converter OPTIONAL_INT_CONVERTER = BuiltInConverter.of(7,
            newOptionalIntConverter(INTEGER_CONVERTER));

    static final Converter OPTIONAL_LONG_CONVERTER = BuiltInConverter.of(8,
            newOptionalLongConverter(LONG_CONVERTER));

    static final Converter OPTIONAL_DOUBLE_CONVERTER = BuiltInConverter.of(9,
            newOptionalDoubleConverter(DOUBLE_CONVERTER));

    static final Converter INET_ADDRESS_CONVERTER = BuiltInConverter.of(10,
            newTrimmingConverter(newEmptyValueConverter(value -> {
                try {
                    return InetAddress.getByName(value);
                } catch (UnknownHostException e) {
                    throw ConfigMessages.msg.unknownHost(e, value);
                }
            })));

    static final Converter CHARACTER_CONVERTER = BuiltInConverter.of(11, newEmptyValueConverter(value -> {
        if (value.length() == 1) {
            return value.charAt(0);
        }
        throw ConfigMessages.msg.failedCharacterConversion(value);
    }));

    static final Converter SHORT_CONVERTER = BuiltInConverter.of(12,
            newTrimmingConverter(newEmptyValueConverter(Short::valueOf)));

    static final Converter BYTE_CONVERTER = BuiltInConverter.of(13,
            newTrimmingConverter(newEmptyValueConverter(Byte::valueOf)));

    static final Converter UUID_CONVERTER = BuiltInConverter.of(14,
            newTrimmingConverter(newEmptyValueConverter((s) -> {
                try {
                    return UUID.fromString(s);
                } catch (IllegalArgumentException e) {
                    throw ConfigMessages.msg.malformedUUID(e, s);
                }
            })));

    static final Converter CURRENCY_CONVERTER = BuiltInConverter.of(15,
            newTrimmingConverter(newEmptyValueConverter((s) -> Currency.getInstance(s))));

    static final Converter BITSET_CONVERTER = BuiltInConverter.of(16,
            newTrimmingConverter(newTrimmingConverter((s) -> {
                int len = s.length();
                byte[] data = new byte[len / 2];
                for (int i = 0; i < len; i += 2) {
                    data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                            + Character.digit(s.charAt(i + 1), 16));
                }
                return BitSet.valueOf(data);
            })));

    static final Map, Class> PRIMITIVE_TYPES;

    static final Map> ALL_CONVERTERS = new HashMap<>();

    static {
        ALL_CONVERTERS.put(String.class, STRING_CONVERTER);

        ALL_CONVERTERS.put(Boolean.class, BOOLEAN_CONVERTER);

        ALL_CONVERTERS.put(Double.class, DOUBLE_CONVERTER);

        ALL_CONVERTERS.put(Float.class, FLOAT_CONVERTER);

        ALL_CONVERTERS.put(Long.class, LONG_CONVERTER);

        ALL_CONVERTERS.put(Integer.class, INTEGER_CONVERTER);

        ALL_CONVERTERS.put(Short.class, SHORT_CONVERTER);

        ALL_CONVERTERS.put(Class.class, CLASS_CONVERTER);
        ALL_CONVERTERS.put(InetAddress.class, INET_ADDRESS_CONVERTER);

        ALL_CONVERTERS.put(OptionalInt.class, OPTIONAL_INT_CONVERTER);
        ALL_CONVERTERS.put(OptionalLong.class, OPTIONAL_LONG_CONVERTER);
        ALL_CONVERTERS.put(OptionalDouble.class, OPTIONAL_DOUBLE_CONVERTER);

        ALL_CONVERTERS.put(Character.class, CHARACTER_CONVERTER);

        ALL_CONVERTERS.put(Byte.class, BYTE_CONVERTER);

        ALL_CONVERTERS.put(UUID.class, UUID_CONVERTER);

        ALL_CONVERTERS.put(Currency.class, CURRENCY_CONVERTER);

        ALL_CONVERTERS.put(BitSet.class, BITSET_CONVERTER);

        Map, Class> primitiveTypes = new HashMap<>(9);
        primitiveTypes.put(byte.class, Byte.class);
        primitiveTypes.put(short.class, Short.class);
        primitiveTypes.put(int.class, Integer.class);
        primitiveTypes.put(long.class, Long.class);

        primitiveTypes.put(float.class, Float.class);
        primitiveTypes.put(double.class, Double.class);

        primitiveTypes.put(char.class, Character.class);

        primitiveTypes.put(boolean.class, Boolean.class);

        primitiveTypes.put(void.class, Void.class);
        PRIMITIVE_TYPES = primitiveTypes;
    }

    static Class wrapPrimitiveType(Class primitiveType) {
        assert primitiveType.isPrimitive();
        return PRIMITIVE_TYPES.get(primitiveType);
    }

    /**
     * Get the type of the converter specified by {@code clazz}. If the given class is not a valid
     * converter, then {@code null} is returned.
     *
     * @param clazz the converter class (must not be {@code null})
     * @return the converter target type, or {@code null} if the class does not represent a valid configuration
     * @throws IllegalStateException if the given converter class is not properly parameterized
     */
    public static Type getConverterType(Class clazz) {
        if (clazz.equals(Object.class)) {
            return null;
        }

        for (Type type : clazz.getGenericInterfaces()) {
            if (type instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) type;
                if (pt.getRawType().equals(Converter.class)) {
                    Type[] typeArguments = pt.getActualTypeArguments();
                    if (typeArguments.length != 1) {
                        throw ConfigMessages.msg.singleTypeConverter(clazz.getName());
                    }
                    return typeArguments[0];
                }
            }
        }

        return getConverterType(clazz.getSuperclass());
    }

    /**
     * Get the implicit converter for the given type class, if any.
     *
     * @param type the type class
     * @param  the type
     * @return the implicit converter for the given type class, or {@code null} if none exists
     */
    public static  Converter getImplicitConverter(Class type) {
        return ImplicitConverters.getConverter(type);
    }

    /**
     * Get a converter that converts a comma-separated string into a list of converted items.
     *
     * @param itemConverter the item converter (must not be {@code null})
     * @param collectionFactory the collection factory (must not be {@code null})
     * @param  the item type
     * @param  the collection type
     * @return the new converter (not {@code null})
     */
    public static > Converter newCollectionConverter(Converter itemConverter,
            IntFunction collectionFactory) {
        return new CollectionConverter<>(itemConverter, collectionFactory);
    }

    /**
     * Get a converter that converts a comma-separated string into an array of converted items.
     *
     * @param itemConverter the item converter (must not be {@code null})
     * @param arrayType the array type class (must not be {@code null})
     * @param  the item type
     * @param  the array type
     * @return the new converter (not {@code null})
     */
    public static  Converter newArrayConverter(Converter itemConverter, Class arrayType) {
        if (!arrayType.isArray()) {
            throw ConfigMessages.msg.notArrayType(arrayType.toString());
        }
        return new ArrayConverter<>(itemConverter, arrayType);
    }

    /**
     * Get a converter which wraps another converter's result into an {@code Optional}. If the delegate converter
     * returns {@code null}, this converter returns {@link Optional#empty()}.
     *
     * @param delegateConverter the delegate converter (must not be {@code null})
     * @param  the item type
     * @return the new converter (not {@code null})
     */
    public static  Converter> newOptionalConverter(Converter delegateConverter) {
        return new OptionalConverter<>(delegateConverter);
    }

    /**
     * Get a converter which wraps another converter's result into an {@code OptionalInt}. If the delegate converter
     * returns {@code null}, this converter returns {@link Optional#empty()}.
     *
     * @param delegateConverter the delegate converter (must not be {@code null})
     * @return the new converter (not {@code null})
     */
    public static Converter newOptionalIntConverter(Converter delegateConverter) {
        return new OptionalIntConverter(delegateConverter);
    }

    /**
     * Get a converter which wraps another converter's result into an {@code OptionalLong}. If the delegate converter
     * returns {@code null}, this converter returns {@link Optional#empty()}.
     *
     * @param delegateConverter the delegate converter (must not be {@code null})
     * @return the new converter (not {@code null})
     */
    public static Converter newOptionalLongConverter(Converter delegateConverter) {
        return new OptionalLongConverter(delegateConverter);
    }

    /**
     * Get a converter which wraps another converter's result into an {@code OptionalDouble}. If the delegate converter
     * returns {@code null}, this converter returns {@link Optional#empty()}.
     *
     * @param delegateConverter the delegate converter (must not be {@code null})
     * @return the new converter (not {@code null})
     */
    public static Converter newOptionalDoubleConverter(Converter delegateConverter) {
        return new OptionalDoubleConverter(delegateConverter);
    }

    /**
     * Get a converter which wraps another converter and returns a special value to represent empty.
     *
     * @param delegateConverter the converter to delegate to (must not be {@code null})
     * @param emptyValue the empty value to return
     * @param  the value type
     * @return the converter
     */
    public static  Converter newEmptyValueConverter(Converter delegateConverter, T emptyValue) {
        if (emptyValue == null) {
            return delegateConverter;
        }
        return new EmptyValueConverter<>(delegateConverter, emptyValue);
    }

    /**
     * Get a converter which wraps another converter and handles empty values correctly. This allows the
     * delegate converter to assume that the value being converted will not be {@code null} or empty.
     *
     * @param delegateConverter the converter to delegate to (must not be {@code null})
     * @param  the value type
     * @return the converter
     */
    public static  Converter newEmptyValueConverter(Converter delegateConverter) {
        return new EmptyValueConverter<>(delegateConverter, null);
    }

    /**
     * Get a converter which trims the string input before passing it on to the delegate converter.
     *
     * @param delegateConverter the converter to delegate to (must not be {@code null})
     * @param  the value type
     * @return the converter
     */
    public static  Converter newTrimmingConverter(Converter delegateConverter) {
        return new TrimmingConverter<>(delegateConverter);
    }

    /**
     * Get a wrapping converter which verifies that the configuration value is greater than, or optionally equal to,
     * the given minimum value.
     *
     * @param delegate the delegate converter (must not be {@code null})
     * @param minimumValue the minimum value (must not be {@code null})
     * @param inclusive {@code true} if the minimum value is inclusive, {@code false} otherwise
     * @param  the converter target type
     * @return a range-validating converter
     */
    public static > Converter minimumValueConverter(Converter delegate, T minimumValue,
            boolean inclusive) {
        return new RangeCheckConverter<>(Comparator.naturalOrder(), delegate, minimumValue, inclusive, null, false);
    }

    /**
     * Get a wrapping converter which verifies that the configuration value is greater than, or optionally equal to,
     * the given minimum value.
     *
     * @param comparator the comparator to use (must not be {@code null})
     * @param delegate the delegate converter (must not be {@code null})
     * @param minimumValue the minimum value (must not be {@code null})
     * @param inclusive {@code true} if the minimum value is inclusive, {@code false} otherwise
     * @param  the converter target type
     * @return a range-validating converter
     */
    public static  Converter minimumValueConverter(Comparator comparator, Converter delegate,
            T minimumValue, boolean inclusive) {
        return new RangeCheckConverter<>(comparator, delegate, minimumValue, inclusive, null, false);
    }

    /**
     * Get a wrapping converter which verifies that the configuration value is greater than, or optionally equal to,
     * the given minimum value (in string form).
     *
     * @param delegate the delegate converter (must not be {@code null})
     * @param minimumValue the minimum value (must not be {@code null})
     * @param inclusive {@code true} if the minimum value is inclusive, {@code false} otherwise
     * @param  the converter target type
     * @return a range-validating converter
     * @throws IllegalArgumentException if the given minimum value fails conversion
     */
    public static > Converter minimumValueStringConverter(Converter delegate,
            String minimumValue, boolean inclusive) {
        return minimumValueConverter(delegate, delegate.convert(minimumValue), inclusive);
    }

    /**
     * Get a wrapping converter which verifies that the configuration value is greater than, or optionally equal to,
     * the given minimum value (in string form).
     *
     * @param comparator the comparator to use (must not be {@code null})
     * @param delegate the delegate converter (must not be {@code null})
     * @param minimumValue the minimum value (must not be {@code null})
     * @param inclusive {@code true} if the minimum value is inclusive, {@code false} otherwise
     * @param  the converter target type
     * @return a range-validating converter
     * @throws IllegalArgumentException if the given minimum value fails conversion
     */
    public static  Converter minimumValueStringConverter(Comparator comparator,
            Converter delegate, String minimumValue, boolean inclusive) {
        return minimumValueConverter(comparator, delegate, delegate.convert(minimumValue), inclusive);
    }

    /**
     * Get a wrapping converter which verifies that the configuration value is less than, or optionally equal to,
     * the given maximum value.
     *
     * @param delegate the delegate converter (must not be {@code null})
     * @param maximumValue the maximum value (must not be {@code null})
     * @param inclusive {@code true} if the maximum value is inclusive, {@code false} otherwise
     * @param  the converter target type
     * @return a range-validating converter
     */
    public static > Converter maximumValueConverter(Converter delegate, T maximumValue,
            boolean inclusive) {
        return new RangeCheckConverter<>(Comparator.naturalOrder(), delegate, null, false, maximumValue, inclusive);
    }

    /**
     * Get a wrapping converter which verifies that the configuration value is less than, or optionally equal to,
     * the given maximum value.
     *
     * @param comparator the comparator to use (must not be {@code null})
     * @param delegate the delegate converter (must not be {@code null})
     * @param maximumValue the maximum value (must not be {@code null})
     * @param inclusive {@code true} if the maximum value is inclusive, {@code false} otherwise
     * @param  the converter target type
     * @return a range-validating converter
     */
    public static  Converter maximumValueConverter(Comparator comparator, Converter delegate,
            T maximumValue, boolean inclusive) {
        return new RangeCheckConverter<>(comparator, delegate, null, false, maximumValue, inclusive);
    }

    /**
     * Get a wrapping converter which verifies that the configuration value is less than, or optionally equal to,
     * the given maximum value (in string form).
     *
     * @param delegate the delegate converter (must not be {@code null})
     * @param maximumValue the maximum value (must not be {@code null})
     * @param inclusive {@code true} if the maximum value is inclusive, {@code false} otherwise
     * @param  the converter target type
     * @return a range-validating converter
     * @throws IllegalArgumentException if the given maximum value fails conversion
     */
    public static > Converter maximumValueStringConverter(Converter delegate,
            String maximumValue, boolean inclusive) {
        return maximumValueConverter(delegate, delegate.convert(maximumValue), inclusive);
    }

    /**
     * Get a wrapping converter which verifies that the configuration value is less than, or optionally equal to,
     * the given maximum value (in string form).
     *
     * @param comparator the comparator to use (must not be {@code null})
     * @param delegate the delegate converter (must not be {@code null})
     * @param maximumValue the maximum value (must not be {@code null})
     * @param inclusive {@code true} if the maximum value is inclusive, {@code false} otherwise
     * @param  the converter target type
     * @return a range-validating converter
     * @throws IllegalArgumentException if the given maximum value fails conversion
     */
    public static  Converter maximumValueStringConverter(Comparator comparator,
            Converter delegate, String maximumValue, boolean inclusive) {
        return maximumValueConverter(comparator, delegate, delegate.convert(maximumValue), inclusive);
    }

    /**
     * Get a wrapping converter which verifies that the configuration value is within the given range.
     *
     * @param delegate the delegate converter (must not be {@code null})
     * @param maximumValue the maximum value (must not be {@code null})
     * @param maxInclusive {@code true} if the maximum value is inclusive, {@code false} otherwise
     * @param  the converter target type
     * @return a range-validating converter
     */
    public static > Converter rangeValueConverter(Converter delegate, T minimumValue,
            boolean minInclusive, T maximumValue, boolean maxInclusive) {
        return new RangeCheckConverter<>(Comparator.naturalOrder(), delegate, minimumValue, minInclusive, maximumValue,
                maxInclusive);
    }

    /**
     * Get a wrapping converter which verifies that the configuration value is within the given range.
     *
     * @param comparator the comparator to use (must not be {@code null})
     * @param delegate the delegate converter (must not be {@code null})
     * @param maximumValue the maximum value (must not be {@code null})
     * @param maxInclusive {@code true} if the maximum value is inclusive, {@code false} otherwise
     * @param  the converter target type
     * @return a range-validating converter
     */
    public static  Converter rangeValueConverter(Comparator comparator, Converter delegate,
            T minimumValue, boolean minInclusive, T maximumValue, boolean maxInclusive) {
        return new RangeCheckConverter<>(comparator, delegate, minimumValue, minInclusive, maximumValue, maxInclusive);
    }

    /**
     * Get a wrapping converter which verifies that the configuration value is within the given range (in string form).
     *
     * @param delegate the delegate converter (must not be {@code null})
     * @param maximumValue the maximum value (must not be {@code null})
     * @param maxInclusive {@code true} if the maximum value is inclusive, {@code false} otherwise
     * @param  the converter target type
     * @return a range-validating converter
     * @throws IllegalArgumentException if the given minimum or maximum value fails conversion
     */
    public static > Converter rangeValueStringConverter(Converter delegate,
            String minimumValue, boolean minInclusive, String maximumValue, boolean maxInclusive) {
        return rangeValueConverter(delegate, delegate.convert(minimumValue), minInclusive, delegate.convert(maximumValue),
                maxInclusive);
    }

    /**
     * Get a wrapping converter which verifies that the configuration value is within the given range (in string form).
     *
     * @param comparator the comparator to use (must not be {@code null})
     * @param delegate the delegate converter (must not be {@code null})
     * @param maximumValue the maximum value (must not be {@code null})
     * @param maxInclusive {@code true} if the maximum value is inclusive, {@code false} otherwise
     * @param  the converter target type
     * @return a range-validating converter
     * @throws IllegalArgumentException if the given minimum or maximum value fails conversion
     */
    public static  Converter rangeValueStringConverter(Comparator comparator, Converter delegate,
            String minimumValue, boolean minInclusive, String maximumValue, boolean maxInclusive) {
        return rangeValueConverter(comparator, delegate, delegate.convert(minimumValue), minInclusive,
                delegate.convert(maximumValue), maxInclusive);
    }

    /**
     * Get a wrapping converter which verifies that the configuration value matches the given pattern.
     *
     * @param delegate the delegate converter (must not be {@code null})
     * @param pattern the pattern to match (must not be {@code null})
     * @param  the converter target type
     * @return a pattern-validating converter
     */
    public static  Converter patternValidatingConverter(Converter delegate, Pattern pattern) {
        return new PatternCheckConverter<>(delegate, pattern);
    }

    /**
     * Get a wrapping converter which verifies that the configuration value matches the given pattern.
     *
     * @param delegate the delegate converter (must not be {@code null})
     * @param pattern the pattern string to match (must not be {@code null})
     * @param  the converter target type
     * @return a pattern-validating converter
     * @throws PatternSyntaxException if the given pattern has invalid syntax
     */
    public static  Converter patternValidatingConverter(Converter delegate, String pattern) {
        return patternValidatingConverter(delegate, Pattern.compile(pattern));
    }

    static final class PatternCheckConverter implements Converter, Serializable {
        private static final long serialVersionUID = 358813973126582008L;

        private final Converter delegate;
        private final Pattern pattern;

        PatternCheckConverter(final Converter delegate, final Pattern pattern) {
            this.delegate = delegate;
            this.pattern = pattern;
        }

        public T convert(final String value) {
            if (value == null) {
                return null;
            }
            if (pattern.matcher(value).matches()) {
                return delegate.convert(value);
            }
            throw ConfigMessages.msg.valueNotMatchPattern(pattern, value);
        }
    }

    static final class RangeCheckConverter implements Converter, Serializable {

        private static final long serialVersionUID = 2764654140347010865L;

        private final Comparator comparator;
        private final Converter delegate;
        private final T min;
        private final boolean minInclusive;
        private final T max;
        private final boolean maxInclusive;

        RangeCheckConverter(final Comparator cmp, final Converter delegate, final T min,
                final boolean minInclusive, final T max, final boolean maxInclusive) {
            this.comparator = cmp;
            this.delegate = delegate;
            this.min = min;
            this.minInclusive = minInclusive;
            this.max = max;
            this.maxInclusive = maxInclusive;
        }

        public T convert(final String value) {
            final T result = delegate.convert(value);
            if (result == null) {
                return null;
            }
            if (min != null) {
                final int cmp = comparator.compare(result, min);
                if (minInclusive) {
                    if (cmp < 0) {
                        throw ConfigMessages.msg.lessThanMinimumValue(min, value);
                    }
                } else {
                    if (cmp <= 0) {
                        throw ConfigMessages.msg.lessThanEqualToMinimumValue(min, value);
                    }
                }
            }
            if (max != null) {
                final int cmp = comparator.compare(result, max);
                if (maxInclusive) {
                    if (cmp > 0) {
                        throw ConfigMessages.msg.greaterThanMaximumValue(max, value);
                    }
                } else {
                    if (cmp >= 0) {
                        throw ConfigMessages.msg.greaterThanEqualToMaximumValue(max, value);
                    }
                }
            }
            return result;
        }

        @SuppressWarnings("unchecked")
        Object readResolve() {
            return comparator != null ? this
                    : new RangeCheckConverter(Comparator.naturalOrder(), delegate, min, minInclusive, max, maxInclusive);
        }
    }

    static final class CollectionConverter> extends AbstractDelegatingConverter {
        private static final long serialVersionUID = -8452214026800305628L;

        private final IntFunction collectionFactory;

        CollectionConverter(final Converter delegate, final IntFunction collectionFactory) {
            super(delegate);
            this.collectionFactory = collectionFactory;
        }

        protected Converter create(final Converter newDelegate) {
            return new CollectionConverter<>(newDelegate, collectionFactory);
        }

        public C convert(final String str) {
            if (str.isEmpty()) {
                // empty collection
                return null;
            }
            final String[] itemStrings = StringUtil.split(str);
            final C collection = collectionFactory.apply(itemStrings.length);
            for (String itemString : itemStrings) {
                if (!itemString.isEmpty()) {
                    final T item = getDelegate().convert(itemString);
                    if (item != null) {
                        collection.add(item);
                    }
                }
            }
            return collection.isEmpty() ? null : collection;
        }
    }

    static final class ArrayConverter extends AbstractDelegatingConverter {
        private static final long serialVersionUID = 2630282286159527380L;

        private final Class arrayType;

        ArrayConverter(final Converter delegate, final Class arrayType) {
            super(delegate);
            this.arrayType = arrayType;
        }

        protected ArrayConverter create(final Converter newDelegate) {
            return new ArrayConverter<>(newDelegate, arrayType);
        }

        public A convert(final String str) {
            if (str.isEmpty()) {
                // empty array
                return null;
            }
            final String[] itemStrings = StringUtil.split(str);
            final A array = arrayType.cast(Array.newInstance(arrayType.getComponentType(), itemStrings.length));
            int size = 0;
            for (String itemString : itemStrings) {
                if (!itemString.isEmpty()) {
                    final T item = getDelegate().convert(itemString);
                    if (item != null) {
                        Array.set(array, size++, item);
                    }
                }
            }
            return size == 0 ? null : size < itemStrings.length ? copyArray(array, arrayType, size) : array;
        }

        private static  A copyArray(A array, Class arrayType, int newSize) {
            if (array instanceof Object[]) {
                return arrayType.cast(Arrays.copyOf((Object[]) array, newSize));
            } else if (array instanceof boolean[]) {
                return arrayType.cast(Arrays.copyOf((boolean[]) array, newSize));
            } else if (array instanceof char[]) {
                return arrayType.cast(Arrays.copyOf((char[]) array, newSize));
            } else if (array instanceof byte[]) {
                return arrayType.cast(Arrays.copyOf((byte[]) array, newSize));
            } else if (array instanceof short[]) {
                return arrayType.cast(Arrays.copyOf((short[]) array, newSize));
            } else if (array instanceof int[]) {
                return arrayType.cast(Arrays.copyOf((int[]) array, newSize));
            } else if (array instanceof long[]) {
                return arrayType.cast(Arrays.copyOf((long[]) array, newSize));
            } else if (array instanceof float[]) {
                return arrayType.cast(Arrays.copyOf((float[]) array, newSize));
            } else if (array instanceof double[]) {
                return arrayType.cast(Arrays.copyOf((double[]) array, newSize));
            } else {
                throw ConfigMessages.msg.unknownArrayType();
            }
        }
    }

    static final class OptionalConverter extends AbstractDelegatingConverter> {
        private static final long serialVersionUID = -4051551570591834428L;

        OptionalConverter(final Converter delegate) {
            super(delegate);
        }

        protected OptionalConverter create(final Converter newDelegate) {
            return new OptionalConverter(newDelegate);
        }

        public Optional convert(final String value) {
            if (value.isEmpty()) {
                try {
                    return Optional.ofNullable(getDelegate().convert(value));
                } catch (IllegalArgumentException ignored) {
                    return Optional.empty();
                }
            } else {
                return Optional.ofNullable(getDelegate().convert(value));
            }
        }
    }

    static final class OptionalIntConverter extends AbstractDelegatingConverter {
        private static final long serialVersionUID = 4331039532024222756L;

        protected OptionalIntConverter(final Converter delegate) {
            super(delegate);
        }

        protected OptionalIntConverter create(final Converter newDelegate) {
            return new OptionalIntConverter(newDelegate);
        }

        public OptionalInt convert(final String value) {
            if (value.isEmpty()) {
                return OptionalInt.empty();
            } else {
                final Integer converted = getDelegate().convert(value);
                return converted == null ? OptionalInt.empty() : OptionalInt.of(converted.intValue());
            }
        }
    }

    static final class OptionalLongConverter extends AbstractDelegatingConverter {
        private static final long serialVersionUID = 140937551800590852L;

        protected OptionalLongConverter(final Converter delegate) {
            super(delegate);
        }

        protected OptionalLongConverter create(final Converter newDelegate) {
            return new OptionalLongConverter(newDelegate);
        }

        public OptionalLong convert(final String value) {
            if (value.isEmpty()) {
                return OptionalLong.empty();
            } else {
                final Long converted = getDelegate().convert(value);
                return converted == null ? OptionalLong.empty() : OptionalLong.of(converted.longValue());
            }
        }
    }

    static final class OptionalDoubleConverter extends AbstractDelegatingConverter {
        private static final long serialVersionUID = -2882741842811044902L;

        OptionalDoubleConverter(final Converter delegate) {
            super(delegate);
        }

        protected OptionalDoubleConverter create(final Converter newDelegate) {
            return new OptionalDoubleConverter(newDelegate);
        }

        public OptionalDouble convert(final String value) {
            if (value.isEmpty()) {
                return OptionalDouble.empty();
            } else {
                final Double converted = getDelegate().convert(value);
                return converted == null ? OptionalDouble.empty() : OptionalDouble.of(converted.doubleValue());
            }
        }
    }

    static final class BuiltInConverter implements Converter, Serializable {
        private final int id;
        private final Converter function;

        static  BuiltInConverter of(int id, Converter function) {
            return new BuiltInConverter<>(id, function);
        }

        private BuiltInConverter(final int id, final Converter function) {
            this.id = id;
            this.function = function;
        }

        public T convert(final String value) {
            return function.convert(value);
        }

        Object writeReplace() {
            return new Ser(id);
        }
    }

    static final class Ser implements Serializable {
        private static final long serialVersionUID = 5646753664957303950L;

        private final short id;

        Ser(final int id) {
            this.id = (short) id;
        }

        Object readResolve() throws ObjectStreamException {
            switch (id) {
                case 0:
                    return STRING_CONVERTER;
                case 1:
                    return BOOLEAN_CONVERTER;
                case 2:
                    return DOUBLE_CONVERTER;
                case 3:
                    return FLOAT_CONVERTER;
                case 4:
                    return LONG_CONVERTER;
                case 5:
                    return INTEGER_CONVERTER;
                case 6:
                    return CLASS_CONVERTER;
                case 7:
                    return OPTIONAL_INT_CONVERTER;
                case 8:
                    return OPTIONAL_LONG_CONVERTER;
                case 9:
                    return OPTIONAL_DOUBLE_CONVERTER;
                case 10:
                    return INET_ADDRESS_CONVERTER;
                case 11:
                    return CHARACTER_CONVERTER;
                case 12:
                    return SHORT_CONVERTER;
                case 13:
                    return BYTE_CONVERTER;
                case 14:
                    return UUID_CONVERTER;
                case 15:
                    return CURRENCY_CONVERTER;
                case 16:
                    return BITSET_CONVERTER;
                default:
                    throw ConfigMessages.msg.unknownConverterId(id);
            }
        }
    }

    static class EmptyValueConverter extends AbstractSimpleDelegatingConverter {
        private static final long serialVersionUID = 5607979836385662739L;

        private final T emptyValue;

        EmptyValueConverter(final Converter delegate, final T emptyValue) {
            super(delegate);
            this.emptyValue = emptyValue;
        }

        protected EmptyValueConverter create(final Converter newDelegate) {
            return new EmptyValueConverter<>(newDelegate, emptyValue);
        }

        public T convert(final String value) {
            if (value.isEmpty()) {
                return emptyValue;
            }
            final T result = getDelegate().convert(value);
            if (result == null) {
                return emptyValue;
            } else {
                return result;
            }
        }
    }

    static class TrimmingConverter extends AbstractSimpleDelegatingConverter {
        private static final long serialVersionUID = 3241445721544473135L;

        TrimmingConverter(final Converter delegate) {
            super(delegate);
        }

        protected TrimmingConverter create(final Converter newDelegate) {
            return new TrimmingConverter<>(newDelegate);
        }

        public T convert(final String value) {
            if (value == null) {
                throw ConfigMessages.msg.converterNullValue();
            }
            return getDelegate().convert(value.trim());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy