package sk.stuba.fiit.perconik.utilities.configuration; import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; import java.net.InetSocketAddress; import java.net.URI; import java.net.URL; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.Map; import java.util.Map.Entry; import com.google.common.base.MoreObjects.ToStringHelper; import com.google.common.base.Splitter; import com.google.common.base.Supplier; import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedLong; import com.google.common.reflect.TypeToken; import sk.stuba.fiit.perconik.utilities.reflect.resolver.ClassResolver; import sk.stuba.fiit.perconik.utilities.reflect.resolver.ClassResolvers; import static java.lang.Integer.parseInt; import static com.google.common.base.MoreObjects.toStringHelper; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Throwables.propagate; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.newLinkedList; import static com.google.common.collect.Maps.newHashMap; import static com.google.common.collect.Maps.newLinkedHashMap; import static com.google.common.collect.Sets.newHashSet; import static com.google.common.collect.Sets.newLinkedHashSet; import static sk.stuba.fiit.perconik.utilities.MoreStrings.trimLeading; import static sk.stuba.fiit.perconik.utilities.MoreStrings.trimTrailing; import static sk.stuba.fiit.perconik.utilities.net.UniformResources.newUri; import static sk.stuba.fiit.perconik.utilities.net.UniformResources.newUrl; public final class OptionParsers { private OptionParsers() {} private enum ObjectParser implements OptionParser<Object> { INSTANCE; public Object parse(final Object object) { return checkNotNull(object); } public TypeToken<Object> type() { return TypeToken.of(Object.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } public static OptionParser<Object> objectParser() { return ObjectParser.INSTANCE; } private enum BooleanParser implements OptionParser<Boolean> { INSTANCE; public Boolean parse(final Object object) { return object instanceof Boolean ? (Boolean) object : Boolean.valueOf(object.toString()); } public TypeToken<Boolean> type() { return TypeToken.of(Boolean.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } public static OptionParser<Boolean> booleanParser() { return BooleanParser.INSTANCE; } private enum ByteParser implements OptionParser<Byte> { INSTANCE; public Byte parse(final Object object) { return object instanceof Byte ? (Byte) object : Byte.valueOf(object.toString()); } public TypeToken<Byte> type() { return TypeToken.of(Byte.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } private enum ShortParser implements OptionParser<Short> { INSTANCE; public Short parse(final Object object) { return object instanceof Short ? (Short) object : Short.valueOf(object.toString()); } public TypeToken<Short> type() { return TypeToken.of(Short.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } private enum IntegerParser implements OptionParser<Integer> { INSTANCE; public Integer parse(final Object object) { return object instanceof Integer ? (Integer) object : Integer.valueOf(object.toString()); } public TypeToken<Integer> type() { return TypeToken.of(Integer.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } private enum LongParser implements OptionParser<Long> { INSTANCE; public Long parse(final Object object) { return object instanceof Long ? (Long) object : Long.valueOf(object.toString()); } public TypeToken<Long> type() { return TypeToken.of(Long.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } public static OptionParser<Byte> byteParser() { return ByteParser.INSTANCE; } public static OptionParser<Short> shortParser() { return ShortParser.INSTANCE; } public static OptionParser<Integer> integerParser() { return IntegerParser.INSTANCE; } public static OptionParser<Long> longParser() { return LongParser.INSTANCE; } private enum FloatParser implements OptionParser<Float> { INSTANCE; public Float parse(final Object object) { return object instanceof Float ? (Float) object : Float.valueOf(object.toString()); } public TypeToken<Float> type() { return TypeToken.of(Float.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } private enum DoubleParser implements OptionParser<Double> { INSTANCE; public Double parse(final Object object) { return object instanceof Double ? (Double) object : Double.valueOf(object.toString()); } public TypeToken<Double> type() { return TypeToken.of(Double.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } public static OptionParser<Float> floatParser() { return FloatParser.INSTANCE; } public static OptionParser<Double> doubleParser() { return DoubleParser.INSTANCE; } private enum CharacterParser implements OptionParser<Character> { INSTANCE; public Character parse(final Object object) { if (object instanceof Character) { return (Character) object; } String value = object.toString(); checkArgument(value.length() == 1); return Character.valueOf(value.charAt(0)); } public TypeToken<Character> type() { return TypeToken.of(Character.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } private enum StringParser implements OptionParser<String> { INSTANCE; public String parse(final Object object) { return object instanceof String ? (String) object : object.toString(); } public TypeToken<String> type() { return TypeToken.of(String.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } public static OptionParser<Character> characterParser() { return CharacterParser.INSTANCE; } public static OptionParser<String> stringParser() { return StringParser.INSTANCE; } private enum BigIntegerParser implements OptionParser<BigInteger> { INSTANCE; public BigInteger parse(final Object object) { return object instanceof BigInteger ? (BigInteger) object : new BigInteger(object.toString()); } public TypeToken<BigInteger> type() { return TypeToken.of(BigInteger.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } private enum BigDecimalParser implements OptionParser<BigDecimal> { INSTANCE; public BigDecimal parse(final Object object) { return object instanceof BigDecimal ? (BigDecimal) object : new BigDecimal(object.toString()); } public TypeToken<BigDecimal> type() { return TypeToken.of(BigDecimal.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } public static OptionParser<BigInteger> bigIntegerParser() { return BigIntegerParser.INSTANCE; } public static OptionParser<BigDecimal> bigDecimalParser() { return BigDecimalParser.INSTANCE; } private enum UnsignedIntegerParser implements OptionParser<UnsignedInteger> { INSTANCE; public UnsignedInteger parse(final Object object) { return object instanceof UnsignedInteger ? (UnsignedInteger) object : UnsignedInteger.valueOf(object.toString()); } public TypeToken<UnsignedInteger> type() { return TypeToken.of(UnsignedInteger.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } private enum UnsignedLongParser implements OptionParser<UnsignedLong> { INSTANCE; public UnsignedLong parse(final Object object) { return object instanceof UnsignedLong ? (UnsignedLong) object : UnsignedLong.valueOf(object.toString()); } public TypeToken<UnsignedLong> type() { return TypeToken.of(UnsignedLong.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } public static OptionParser<UnsignedInteger> unsignedIntegerParser() { return UnsignedIntegerParser.INSTANCE; } public static OptionParser<UnsignedLong> unsignedLongParser() { return UnsignedLongParser.INSTANCE; } private enum PathParser implements OptionParser<Path> { INSTANCE; public Path parse(final Object object) { return object instanceof Path ? (Path) object : Paths.get(object.toString()); } public TypeToken<Path> type() { return TypeToken.of(Path.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } private enum UriParser implements OptionParser<URI> { INSTANCE; public URI parse(final Object object) { return object instanceof URI ? (URI) object : newUri(object.toString()); } public TypeToken<URI> type() { return TypeToken.of(URI.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } private enum UrlParser implements OptionParser<URL> { INSTANCE; public URL parse(final Object object) { return object instanceof URL ? (URL) object : newUrl(object.toString()); } public TypeToken<URL> type() { return TypeToken.of(URL.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } public static OptionParser<Path> pathParser() { return PathParser.INSTANCE; } public static OptionParser<URI> uriParser() { return UriParser.INSTANCE; } public static OptionParser<URL> urlParser() { return UrlParser.INSTANCE; } private enum InetSocketAddressParser implements OptionParser<InetSocketAddress> { INSTANCE; public InetSocketAddress parse(final Object object) { if (object instanceof InetSocketAddress) { return (InetSocketAddress) object; } Iterator<String> parts = Splitter.on(':').limit(2).split(object.toString()).iterator(); return new InetSocketAddress(parts.next(), parseInt(parts.next())); } public TypeToken<InetSocketAddress> type() { return TypeToken.of(InetSocketAddress.class); } @Override public String toString() { return toStringHelper(this).add("type", this.type()).toString(); } } public static OptionParser<InetSocketAddress> inetSocketAddressParser() { return InetSocketAddressParser.INSTANCE; } private static final class ClassParser implements OptionParser<Class<?>>, Serializable { private static final long serialVersionUID = 0L; @SuppressWarnings("serial") private static final TypeToken<Class<?>> wildcardClassType = new TypeToken<Class<?>>() {}; private final ClassResolver resolver; ClassParser(final ClassResolver resolver) { this.resolver = checkNotNull(resolver); } public Class<?> parse(final Object object) { try { return object instanceof Class ? (Class<?>) object : this.resolver.forName(object.toString()); } catch (ClassNotFoundException failure) { throw propagate(failure); } } public TypeToken<Class<?>> type() { return wildcardClassType; } @Override public String toString() { return toStringHelper(this).add("type", this.type()).add("resolver", this.resolver).toString(); } } public static OptionParser<Class<?>> classParser() { return new ClassParser(ClassResolvers.getDefault()); } public static OptionParser<Class<?>> classParser(final ClassResolver resolver) { return new ClassParser(resolver); } private static abstract class CollectionParser<C extends Collection<E>, E> implements OptionParser<C>, Serializable { private static final long serialVersionUID = 0L; private final OptionParser<? extends E> elementParser; private final String elementSeparator; private final String prefix; private final String suffix; CollectionParser(final OptionParser<? extends E> elementParser, final String elementSeparator, final String prefix, final String suffix) { this.elementParser = checkNotNull(elementParser); this.elementSeparator = checkNotNull(elementSeparator); this.prefix = checkNotNull(prefix); this.suffix = checkNotNull(suffix); } abstract C newCollection(); public C parse(final Object object) { C result = this.newCollection(); if (object instanceof Iterable) { Iterable<?> inputs = (Iterable<?>) object; for (Object input: inputs) { result.add(this.elementParser.parse(input)); } return result; } String value = trimTrailing(trimLeading(object.toString(), this.prefix), this.suffix); for (String input: Splitter.on(this.elementSeparator).split(value)) { result.add(this.elementParser.parse(input)); } return result; } public TypeToken<C> type() { @SuppressWarnings("serial") TypeToken<C> type = new TypeToken<C>(this.getClass()) {}; return type; } @Override public String toString() { ToStringHelper helper = toStringHelper(this); helper.add("type", this.type()); helper.add("elementParser", this.elementParser); helper.add("elementSeparator", this.elementSeparator); helper.add("prefix", this.prefix); helper.add("suffix", this.suffix); return helper.toString(); } } @SuppressWarnings("serial") private static final class CollectionParsers { static final String COLLECTION_PREFIX = "["; static final String COLLECTION_SUFFIX = "]"; static final String ELEMENT_SEPARATOR = ", "; private CollectionParsers() {} static <C extends Collection<E>, E> CollectionParser<C, E> collectionParser(final Supplier<? extends C> supplier, final OptionParser<? extends E> elementParser, final String elementSeparator, final String prefix, final String suffix) { return new CollectionParser<C, E>(elementParser, elementSeparator, prefix, suffix) { @Override C newCollection() { return supplier.get(); } }; } static <E> CollectionParser<ArrayList<E>, E> arrayListParser(final OptionParser<? extends E> elementParser, final String elementSeparator, final String prefix, final String suffix) { return new CollectionParser<ArrayList<E>, E>(elementParser, elementSeparator, prefix, suffix) { @Override ArrayList<E> newCollection() { return newArrayList(); } }; } static <E> CollectionParser<LinkedList<E>, E> linkedListParser(final OptionParser<? extends E> elementParser, final String elementSeparator, final String prefix, final String suffix) { return new CollectionParser<LinkedList<E>, E>(elementParser, elementSeparator, prefix, suffix) { @Override LinkedList<E> newCollection() { return newLinkedList(); } }; } static <E> CollectionParser<HashSet<E>, E> hashSetParser(final OptionParser<? extends E> elementParser, final String elementSeparator, final String prefix, final String suffix) { return new CollectionParser<HashSet<E>, E>(elementParser, elementSeparator, prefix, suffix) { @Override HashSet<E> newCollection() { return newHashSet(); } }; } static <E> CollectionParser<LinkedHashSet<E>, E> linkedHashSetParser(final OptionParser<? extends E> elementParser, final String elementSeparator, final String prefix, final String suffix) { return new CollectionParser<LinkedHashSet<E>, E>(elementParser, elementSeparator, prefix, suffix) { @Override LinkedHashSet<E> newCollection() { return newLinkedHashSet(); } }; } } public static <C extends Collection<E>, E> OptionParser<C> collectionParser(final Supplier<? extends C> supplier, final OptionParser<E> elementParser) { return CollectionParsers.collectionParser(supplier, elementParser, CollectionParsers.ELEMENT_SEPARATOR, CollectionParsers.COLLECTION_PREFIX, CollectionParsers.COLLECTION_SUFFIX); } public static <C extends Collection<E>, E> OptionParser<C> collectionParser(final Supplier<? extends C> supplier, final OptionParser<? extends E> elementParser, final String elementSeparator, final String prefix, final String suffix) { return CollectionParsers.collectionParser(supplier, elementParser, elementSeparator, prefix, suffix); } public static <E> OptionParser<ArrayList<E>> arrayListParser(final OptionParser<E> elementParser) { return CollectionParsers.arrayListParser(elementParser, CollectionParsers.ELEMENT_SEPARATOR, CollectionParsers.COLLECTION_PREFIX, CollectionParsers.COLLECTION_SUFFIX); } public static <E> OptionParser<ArrayList<E>> arrayListParser(final OptionParser<? extends E> elementParser, final String elementSeparator, final String prefix, final String suffix) { return CollectionParsers.arrayListParser(elementParser, elementSeparator, prefix, suffix); } public static <E> OptionParser<LinkedList<E>> linkedListParser(final OptionParser<E> elementParser) { return CollectionParsers.linkedListParser(elementParser, CollectionParsers.ELEMENT_SEPARATOR, CollectionParsers.COLLECTION_PREFIX, CollectionParsers.COLLECTION_SUFFIX); } public static <E> OptionParser<LinkedList<E>> linkedListParser(final OptionParser<? extends E> elementParser, final String elementSeparator, final String prefix, final String suffix) { return CollectionParsers.linkedListParser(elementParser, elementSeparator, prefix, suffix); } public static <E> OptionParser<HashSet<E>> hashSetParser(final OptionParser<E> elementParser) { return CollectionParsers.hashSetParser(elementParser, CollectionParsers.ELEMENT_SEPARATOR, CollectionParsers.COLLECTION_PREFIX, CollectionParsers.COLLECTION_SUFFIX); } public static <E> OptionParser<HashSet<E>> hashSetParser(final OptionParser<? extends E> elementParser, final String elementSeparator, final String prefix, final String suffix) { return CollectionParsers.hashSetParser(elementParser, elementSeparator, prefix, suffix); } public static <E> OptionParser<LinkedHashSet<E>> linkedHashSetParser(final OptionParser<E> elementParser) { return CollectionParsers.linkedHashSetParser(elementParser, CollectionParsers.ELEMENT_SEPARATOR, CollectionParsers.COLLECTION_PREFIX, CollectionParsers.COLLECTION_SUFFIX); } public static <E> OptionParser<LinkedHashSet<E>> linkedHashSetParser(final OptionParser<? extends E> elementParser, final String elementSeparator, final String prefix, final String suffix) { return CollectionParsers.linkedHashSetParser(elementParser, elementSeparator, prefix, suffix); } private static abstract class MapParser<M extends Map<K, V>, K, V> implements OptionParser<M>, Serializable { private static final long serialVersionUID = 0L; private final OptionParser<? extends K> keyParser; private final OptionParser<? extends V> valueParser; private final String entrySeparator; private final String keyValueSeparator; private final String prefix; private final String suffix; MapParser(final OptionParser<? extends K> keyParser, final OptionParser<? extends V> valueParser, final String entrySeparator, final String keyValueSeparator, final String prefix, final String suffix) { this.keyParser = checkNotNull(keyParser); this.valueParser = checkNotNull(valueParser); this.entrySeparator = checkNotNull(entrySeparator); this.keyValueSeparator = checkNotNull(keyValueSeparator); this.prefix = checkNotNull(prefix); this.suffix = checkNotNull(suffix); } abstract M newMap(); public M parse(final Object object) { M result = this.newMap(); if (object instanceof Map || object instanceof Iterable) { Iterable<?> inputs = object instanceof Map ? ((Map<?, ?>) object).entrySet() : (Iterable<?>) object; for (Object input: inputs) { if (input instanceof Entry) { Entry<?, ?> entry = (Entry<?, ?>) input; result.put(this.keyParser.parse(entry.getKey()), this.valueParser.parse(entry.getValue())); continue; } String value = input.toString(); Iterator<?> parts = Splitter.on(this.keyValueSeparator).limit(2).split(value).iterator(); result.put(this.keyParser.parse(parts.next()), this.valueParser.parse(parts.next())); } return result; } String value = trimTrailing(trimLeading(object.toString(), this.prefix), this.suffix); for (Entry<?, ?> input: Splitter.on(this.entrySeparator).withKeyValueSeparator(this.keyValueSeparator).split(value).entrySet()) { result.put(this.keyParser.parse(input.getKey()), this.valueParser.parse(input.getValue())); } return result; } public TypeToken<M> type() { @SuppressWarnings("serial") TypeToken<M> type = new TypeToken<M>(this.getClass()) {}; return type; } @Override public String toString() { ToStringHelper helper = toStringHelper(this); helper.add("type", this.type()); helper.add("keyParser", this.keyParser); helper.add("valueParser", this.valueParser); helper.add("entrySeparator", this.entrySeparator); helper.add("keyValueSeparator", this.keyValueSeparator); helper.add("prefix", this.prefix); helper.add("suffix", this.suffix); return helper.toString(); } } @SuppressWarnings("serial") private static final class MapParsers { static final String MAP_PREFIX = "["; static final String MAP_SUFFIX = "]"; static final String ENTRY_SEPARATOR = ", "; static final String KEY_VALUE_SEPARATOR = "="; private MapParsers() {} static <M extends Map<K, V>, K, V> MapParser<M, K, V> mapParser(final Supplier<? extends M> supplier, final OptionParser<? extends K> keyParser, final OptionParser<? extends V> valueParser, final String entrySeparator, final String keyValueSeparator, final String prefix, final String suffix) { return new MapParser<M, K, V>(keyParser, valueParser, entrySeparator, keyValueSeparator, prefix, suffix) { @Override M newMap() { return supplier.get(); } }; } static <K, V> MapParser<HashMap<K, V>, K, V> hashMapParser(final OptionParser<? extends K> keyParser, final OptionParser<? extends V> valueParser, final String entrySeparator, final String keyValueSeparator, final String prefix, final String suffix) { return new MapParser<HashMap<K, V>, K, V>(keyParser, valueParser, entrySeparator, keyValueSeparator, prefix, suffix) { @Override HashMap<K, V> newMap() { return newHashMap(); } }; } static <K, V> MapParser<LinkedHashMap<K, V>, K, V> linkedHashMapParser(final OptionParser<? extends K> keyParser, final OptionParser<? extends V> valueParser, final String entrySeparator, final String keyValueSeparator, final String prefix, final String suffix) { return new MapParser<LinkedHashMap<K, V>, K, V>(keyParser, valueParser, entrySeparator, keyValueSeparator, prefix, suffix) { @Override LinkedHashMap<K, V> newMap() { return newLinkedHashMap(); } }; } } public static <M extends Map<K, V>, K, V> OptionParser<M> mapParser(final Supplier<? extends M> supplier, final OptionParser<? extends K> keyParser, final OptionParser<? extends V> valueParser) { return MapParsers.mapParser(supplier, keyParser, valueParser, MapParsers.ENTRY_SEPARATOR, MapParsers.KEY_VALUE_SEPARATOR, MapParsers.MAP_PREFIX, MapParsers.MAP_SUFFIX); } public static <M extends Map<K, V>, K, V> OptionParser<M> mapParser(final Supplier<? extends M> supplier, final OptionParser<? extends K> keyParser, final OptionParser<? extends V> valueParser, final String entrySeparator, final String keyValueSeparator, final String prefix, final String suffix) { return MapParsers.mapParser(supplier, keyParser, valueParser, entrySeparator, keyValueSeparator, prefix, suffix); } public static <K, V> OptionParser<HashMap<K, V>> hashMapParser(final OptionParser<? extends K> keyParser, final OptionParser<? extends V> valueParser) { return MapParsers.hashMapParser(keyParser, valueParser, MapParsers.ENTRY_SEPARATOR, MapParsers.KEY_VALUE_SEPARATOR, MapParsers.MAP_PREFIX, MapParsers.MAP_SUFFIX); } public static <K, V> OptionParser<HashMap<K, V>> hashMapParser(final OptionParser<? extends K> keyParser, final OptionParser<? extends V> valueParser, final String entrySeparator, final String keyValueSeparator, final String prefix, final String suffix) { return MapParsers.hashMapParser(keyParser, valueParser, entrySeparator, keyValueSeparator, prefix, suffix); } public static <K, V> OptionParser<LinkedHashMap<K, V>> linkedHashMapParser(final OptionParser<? extends K> keyParser, final OptionParser<? extends V> valueParser) { return MapParsers.linkedHashMapParser(keyParser, valueParser, MapParsers.ENTRY_SEPARATOR, MapParsers.KEY_VALUE_SEPARATOR, MapParsers.MAP_PREFIX, MapParsers.MAP_SUFFIX); } public static <K, V> OptionParser<LinkedHashMap<K, V>> linkedHashMapParser(final OptionParser<? extends K> keyParser, final OptionParser<? extends V> valueParser, final String entrySeparator, final String keyValueSeparator, final String prefix, final String suffix) { return MapParsers.linkedHashMapParser(keyParser, valueParser, entrySeparator, keyValueSeparator, prefix, suffix); } }