Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
alexh.weak.Converter Maven / Gradle / Ivy
/*
* Copyright 2015 Alex Butler
*
* 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 alexh.weak;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableMap;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.StreamSupport.stream;
import alexh.Fluent;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Logic container for converting weakly typed objects into core types,
* ie
* {@code convert(1234.5678d).intoDecimal()} returns a {@link BigDecimal} of 1234.5678
*
* {@code convert(1234.5678d).intoInteger()} returns a rounded int of 1235
*
* {@code convert("2015-03").intoLocalDateTime()} returns a {@link LocalDateTime} of 2015-03-01T00:00:00.000
*
* {@code convert(Optional.of("8484")).intoDouble()} returns a double of 8484.0
*
* Supports conversions from most basic types, in addition to java.util.Date & java.time instances,
* object arrays, Iterables, Maps & Optionals + more from #toString conversions
*
* @author Alex Butler
*/
public class Converter {
private static final String DEFAULT_MAP_KEY = "value";
private static final Map, Function> typeConverters =
unmodifiableMap(
new Fluent.LinkedHashMap, Function>()
.append(Integer.class, IntConverter::new)
.append(Long.class, LongConverter::new)
.append(Double.class, DoubleConverter::new)
.append(BigDecimal.class, DecimalConverter::new)
.append(Weak.class, o -> convert(((Weak) o).asObject()))
.append(OptionalWeak.class, o -> new OptionalConverter(((OptionalWeak) o).asObject()))
.append(Map.class, MapConverter::new)
.append(Iterable.class, IterableConverter::new)
.append(Optional.class, OptionalConverter::new)
.append(java.util.Date.class, UtilDateInstantConverter::new)
// fallback
.append(Object.class, Converter::new)
);
/**
* @param value some object to convert
* @return new Converter instance wrapper for the input value
*/
public static Converter convert(Object value) {
requireNonNull(value);
if (value instanceof Object[])
return convert(asList((Object[]) value));
return typeConverters.getOrDefault(value.getClass(), typeConverters.entrySet().stream()
.filter(entry -> !entry.getKey().equals(java.util.Date.class) && entry.getKey().isInstance(value))
.findFirst()
.map(Map.Entry::getValue).get()).apply(value);
}
private static boolean doesNotThrow(Supplier> method) {
try {
method.get();
return true;
}
catch (RuntimeException ex) { return false; }
}
/** wrapped inner value, the conversion target */
protected final Object o;
Converter(Object o) {
this.o = o;
}
/**
* Converts the inner value into a String, should always work
* @return conversion
*/
public String intoString() {
return o instanceof String ? (String) o : o.toString();
}
/** @return {@link #intoString} will not throw an exception */
public boolean intoStringWorks() {
return doesNotThrow(this::intoString);
}
/**
* @return conversion
* @throws java.lang.RuntimeException cannot be converted
*/
public int intoInteger() {
return intoDecimal().setScale(0, RoundingMode.HALF_UP).intValueExact();
}
/** @return {@link #intoInteger} will not throw an exception */
public boolean intoIntegerWorks() {
return doesNotThrow(this::intoInteger);
}
/**
* @return conversion
* @throws java.lang.RuntimeException cannot be converted
*/
public long intoLong() {
return intoDecimal().setScale(0, RoundingMode.HALF_UP).longValueExact();
}
/** @return {@link #intoLong} will not throw an exception */
public boolean intoLongWorks() {
return doesNotThrow(this::intoLong);
}
/**
* @return conversion
* @throws java.lang.RuntimeException cannot be converted
*/
public double intoDouble() {
return intoDecimal().doubleValue();
}
/** @return {@link #intoDouble} will not throw an exception */
public boolean intoDoubleWorks() {
return doesNotThrow(this::intoDouble);
}
/**
* @return conversion
* @throws java.lang.RuntimeException cannot be converted
*/
public BigDecimal intoDecimal() {
return new BigDecimal(intoString());
}
/** @return {@link #intoDecimal} will not throw an exception */
public boolean intoDecimalWorks() {
return doesNotThrow(this::intoDecimal);
}
/**
* Converts the inner value into a Map
* will convert an Iterable into an index -> value map
* will convert simple types into a size 1 map of {"value": ?}
* as such this should always work
* @return conversion
*/
public Map intoMap() {
return new Fluent.HashMap<>().append(DEFAULT_MAP_KEY, o);
}
/** @return {@link #intoMap} will not throw an exception */
public boolean intoMapWorks() {
return doesNotThrow(this::intoMap);
}
/**
* Converts the inner value into a List
* will convert a Map into a value list
* will convert simple types into a size 1 list [?]
* as such this should always work
* @return conversion
*/
public List intoList() {
List list = new ArrayList();
list.add(o);
return list;
}
/** @return {@link #intoList} will not throw an exception */
public boolean intoListWorks() {
return doesNotThrow(this::intoList);
}
/**
* Converts to string & permissively parses as a date, ignoring time zone
* defaults day->1, month->1, hour->0, minute->0, second->0, nanoseconds->0
* @throws java.time.format.DateTimeParseException converted string is in an invalid format
*/
public LocalDateTime intoLocalDateTime() {
return LocalDateTime.from(ConverterTimeFormats.parseWithDefaults(intoString()));
}
/** @return {@link #intoLocalDateTime} will not throw an exception */
public boolean intoLocalDateTimeWorks() {
return doesNotThrow(this::intoLocalDateTime);
}
/**
* Converts to string & permissively parses as a date, requiring a time zone to be parsed
* defaults day->1, month->1, hour->0, minute->0, second->0, nanoseconds->0
* @throws java.time.format.DateTimeParseException converted string is in an invalid format
*/
public ZonedDateTime intoZonedDateTime() {
return ZonedDateTime.from(ConverterTimeFormats.parseWithDefaults(intoString()));
}
/** @return {@link #intoZonedDateTime} will not throw an exception */
public boolean intoZonedDateTimeWorks() {
return doesNotThrow(this::intoZonedDateTime);
}
/**
* Converts to string & permissively parses as a date, uses parsed time zone or falls back on input
* defaults day->1, month->1, hour->0, minute->0, second->0, nanoseconds->0, time-zone->input
* @param fallback time zone to use if value has none
* @throws java.time.format.DateTimeParseException converted string is in an invalid format
*/
public ZonedDateTime intoZonedDateTimeOrUse(ZoneId fallback){
try { return intoZonedDateTime(); }
catch (RuntimeException ex) { return intoLocalDateTime().atZone(fallback); }
}
/** @return ConverterMaybe instance, for fluent handling of non-convertibles */
public ConverterMaybe maybe() {
return new ConverterMaybe(o);
}
static abstract class TypeConverter extends Converter {
TypeConverter(Object o) {
super(o);
}
protected T literal() {
return (T) o;
}
}
static class IntConverter extends TypeConverter {
IntConverter(Object o) {
super(o);
}
@Override
public int intoInteger() {
return literal();
}
@Override
public long intoLong() {
return (long) literal();
}
@Override
public double intoDouble() {
return (double) literal();
}
@Override
public BigDecimal intoDecimal() {
return new BigDecimal(literal());
}
}
static class LongConverter extends TypeConverter {
LongConverter(Object o) {
super(o);
}
@Override
public int intoInteger() {
if (literal() < Integer.MIN_VALUE || literal() > Integer.MAX_VALUE)
throw new IllegalArgumentException(literal() + " too large/small to be cast to int");
return literal().intValue();
}
@Override
public long intoLong() {
return literal();
}
@Override
public double intoDouble() {
return (double) literal();
}
@Override
public BigDecimal intoDecimal() {
return new BigDecimal(literal());
}
}
static class DoubleConverter extends TypeConverter {
DoubleConverter(Object o) {
super(o);
}
@Override
public double intoDouble() {
return literal();
}
@Override
public BigDecimal intoDecimal() {
return new BigDecimal(literal());
}
}
static class DecimalConverter extends TypeConverter {
DecimalConverter(Object o) {
super(o);
}
@Override
public BigDecimal intoDecimal() {
return literal();
}
}
static class MapConverter extends TypeConverter> {
MapConverter(Object o) {
super(o);
}
private Optional value() {
return Optional.ofNullable(literal().get(DEFAULT_MAP_KEY));
}
@Override
public String intoString() {
return value().map(o -> convert(o).intoString()).orElseGet(super::intoString);
}
@Override
public int intoInteger() {
return value().map(o -> convert(o).intoInteger()).orElseGet(super::intoInteger);
}
@Override
public long intoLong() {
return value().map(o -> convert(o).intoLong()).orElseGet(super::intoLong);
}
@Override
public double intoDouble() {
return value().map(o -> convert(o).intoDouble()).orElseGet(super::intoDouble);
}
@Override
public BigDecimal intoDecimal() {
return value().map(o -> convert(o).intoDecimal()).orElseGet(super::intoDecimal);
}
@Override
public Map intoMap() {
return new LinkedHashMap<>(literal());
}
@Override
public List intoList() {
return new ArrayList<>(literal().values());
}
}
static class IterableConverter extends TypeConverter> {
IterableConverter(Object o) {
super(o);
}
private Optional onlyElement() {
Iterator> iterator = literal().iterator();
return Optional.ofNullable(iterator.hasNext() ? iterator.next() : null)
.filter(o -> !iterator.hasNext());
}
@Override
public String intoString() {
return onlyElement().map(o -> convert(o).intoString()).orElseGet(super::intoString);
}
@Override
public int intoInteger() {
return onlyElement().map(o -> convert(o).intoInteger()).orElseGet(super::intoInteger);
}
@Override
public long intoLong() {
return onlyElement().map(o -> convert(o).intoLong()).orElseGet(super::intoLong);
}
@Override
public double intoDouble() {
return onlyElement().map(o -> convert(o).intoDouble()).orElseGet(super::intoDouble);
}
@Override
public BigDecimal intoDecimal() {
return onlyElement().map(o -> convert(o).intoDecimal()).orElseGet(super::intoDecimal);
}
@Override
public Map intoMap() {
Map map = new LinkedHashMap<>();
Iterator> iterator = literal().iterator();
for (int i = 0; iterator.hasNext(); ++i)
map.put(i, iterator.next());
return map;
}
@Override
public List intoList() {
return stream(literal().spliterator(), false).collect(toCollection(ArrayList::new));
}
}
static class OptionalConverter extends TypeConverter> {
OptionalConverter(Object o) {
super(o);
}
@Override
public String intoString() {
return literal().map(o -> convert(o).intoString()).orElseGet(super::intoString);
}
@Override
public int intoInteger() {
return literal().map(o -> convert(o).intoInteger()).orElseGet(super::intoInteger);
}
@Override
public long intoLong() {
return literal().map(o -> convert(o).intoLong()).orElseGet(super::intoLong);
}
@Override
public double intoDouble() {
return literal().map(o -> convert(o).intoDouble()).orElseGet(super::intoDouble);
}
@Override
public BigDecimal intoDecimal() {
return literal().map(o -> convert(o).intoDecimal()).orElseGet(super::intoDecimal);
}
@Override
public Map intoMap() {
return literal().map(o -> convert(o).intoMap()).orElseGet(LinkedHashMap::new);
}
@Override
public List intoList() {
return literal().map(o -> convert(o).intoList()).orElseGet(ArrayList::new);
}
}
static class UtilDateInstantConverter extends TypeConverter {
UtilDateInstantConverter(Object o) {
super(o);
}
@Override
public BigDecimal intoDecimal() {
return new BigDecimal(intoLong());
}
@Override
public double intoDouble() {
return intoLong();
}
@Override
public long intoLong() {
return literal().getTime();
}
@Override
public String intoString() {
return String.valueOf(literal().getTime());
}
}
}