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

net.javapla.jawn.core.Value Maven / Gradle / Ivy

The newest version!
package net.javapla.jawn.core;

import java.io.InputStream;
import java.nio.charset.Charset;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import net.javapla.jawn.core.internal.ValueParser;
import net.javapla.jawn.core.util.DateUtil;

public interface Value extends Iterable { // SimpleValue
    
    /**
     * {@link Boolean#parseBoolean(String)} always returns either true or false
     * regardless of input
     * 
     * (That is why no asBoolean(default) exists)
     * 
     * @see Boolean#parseBoolean
     * @return
     */
    default boolean asBoolean() {
        return Boolean.parseBoolean(value());
    }
    
    default double asDouble() {
        return simpleValue(Double::parseDouble, double.class);
    }
    
    default double asDouble(final double fallback) {
        try {
            return asDouble();
        } catch (Up.ParseError e) {
            return fallback;
        }
        //return toOptional().map(Double::parseDouble).orElse(fallback);//toOptional(Double.class).orElse(fallback);
    }
    
    default double asDouble(final Function mapper, final double fallback) {
        try {
            return mapper.apply(asDouble());
        } catch (Up.ParseError e) {
            return fallback;
        }
    }
    
    default int asInt() {
        return simpleValue(Integer::parseInt, int.class);
    }
    
    default int asInt(final int fallback) {
        //return map((s) -> (Integer::parseInt)).orElse(fallback);
        try {
            return asInt();
        } catch (Up.ParseError e) {
            return fallback;
        }
    }
    
    /**
     * Applies the mapper to the {@link #asInt()}, if a value is present.
     * Otherwise returns the fallback
     * 
     * @param fallback
     * @param mapper
     * @return
     */
    default int asInt(final Function mapper, final int fallback) {
        try {
            return mapper.apply(asInt());
        } catch (Up.ParseError e) {
            return fallback;
        }
    }
    
    default long asLong() {
        try {
            return Long.valueOf(value());
        } catch (NumberFormatException e) {
            // might be a date
            try {
                return DateUtil.fromDateString(value()).toEpochMilli();
            } catch (DateTimeParseException ignored) { }
            throw new Up.ParseError("Failed to parse value as: " + long.class, e);
        }
    }
    
    default long asLong(final long fallback) {
        try {
            return asLong();
        } catch (Up.ParseError e) {
            return fallback;
        }
        //return toOptional().map(Long::parseLong).orElse(fallback);
    }
    
    default long asLong(final Function mapper, final long fallback) {
        try {
            return mapper.apply(asLong());
        } catch (Up.ParseError e) {
            return fallback;
        }
    }
    
    private  T simpleValue(final Function callback, Class type) {
        try {
            return callback.apply(value());
        } catch (NumberFormatException e) {
            throw Up.ParseError("Failed to parse value as: " + type, e);
        }
    }
    
    
    @Override
    default Iterator iterator() {
        return Collections.singleton(this).iterator();
    }
    
    default String value(final String fallback) {
        return asOptional().orElse(fallback);
    }
    
    default  T value(final Function mapper, final T fallback) {
        if (!isPresent()) return fallback;
        try {
            return mapper.apply(value());
        } catch (Exception e) {
            return fallback;
        }
    }
    
    /**
     * Simply another name for {@link #value()}
     */
    default String asString() {
        return value();
    }
    
    default > T asEnum(final Class type) {
        EnumSet set = EnumSet.allOf(type);
        return set
            .stream()
            .filter(e -> e.name().equalsIgnoreCase(value()))
            .findFirst()
            .orElseGet(() -> java.lang.Enum.valueOf(type, value()));
    }
    
    default  T as(Class type) {
        return ValueParser.to(this, type);
    }
    
    // Commented out until we know if we ever are going to need it
    /*default Object as(Type type) {
        return ValueParser.to(this, type);
    }*/
    
    default List asList() {
        if (isPresent())
            return Collections.singletonList(value());
        return Collections.emptyList();
    }
    
    
    default  List asList(final Class type) {
        return ValueParser.toCollection(this, type, new ArrayList(4));
    }
    
    default Set asSet() {
        return Collections.singleton(value());
    }
    
    default  Set asSet(final Class type) {
        return ValueParser.toCollection(this, type, new HashSet(4));
    }
    
    default Optional asOptional() {
        if (!isPresent()) return Optional.empty();
        try {
            return Optional.of(value());
        } catch (Exception e) {
            return Optional.empty();
        }
    }
    
    default  Optional asOptional(final Class type) {
        //if (type.isPrimitive()) throw new IllegalArgumentException("Primitive types are not allowed in type parameters: " + type);
        if (!isPresent()) return Optional.empty();
        try {
            return Optional.ofNullable(ValueParser.to(this, type));//to(type));
        } catch (Exception e) {
            return Optional.empty();
        }
    }
    
    String value();

    boolean isPresent();
    
    default boolean isMissing() {
        return !isPresent();
    }
    
    default void ifPresent(final Consumer action) {
        if (isPresent()) {
            action.accept(value());
        }
    }
    
    default  void ifPresent(final Class type, Consumer action) {
        if (isPresent()) {
            action.accept(as(type));
        }
    }
    
    default Value orElse(Value other) {
        return isPresent() ? this : other;
    }
    
    default  Optional map(Function mapper) {
        if (!isPresent()) {
            return Optional.empty();
        } else {
            return Optional.ofNullable(mapper.apply(value()));
        }
    }
    
    default  Optional map(Class type, Function mapper) {
        if (!isPresent()) {
            return Optional.empty();
        } else {
            return Optional.ofNullable(mapper.apply(ValueParser.to(this, type)));
        }
    }
    
    static Value of(final String value) {
        return new StringValue(value);
    }
    
    static Value of(Number value) {
        return new NumberValue(value);
    }
    
    static Value of(final String ... valuables) {
        return of(Arrays.asList(valuables));//new ListValue(Arrays.asList(valuables).stream().map(StringValue::new).collect(Collectors.toList()));
    }
    
    static Value of(final List valuables) {
        if (valuables == null || valuables.size() == 0) return empty();
        if (valuables.size() == 1) return Value.of(valuables.get(0));
        return new ListValue(valuables.stream().map(StringValue::new).collect(Collectors.toList()));
    }
    
    static Value of(final Optional opt) {
        return opt.map(StringValue::new).orElse(new StringValue());
    }
    
    static Value of(final Value value) {
        return value;
    }
    
    static Value of(final Value ... valuables) {
        return new ListValue(Arrays.asList(valuables));
    }
    
    /*static Value of(final List valuables) {
        return new ListValue(valuables);
    }*/
    
    
    final class StringValue implements Value {
        private final String value;
        
        public StringValue() {
            this(null);
        }
        
        public StringValue(final String value) {
            this.value = value;
        }

        @Override
        public String value() {
            return value;
        }
        
        @Override
        public boolean isPresent() {
            return value != null && !value.isBlank();
        }
        
        @Override
        public String toString() {
            return value != null
                ? String.format("%s[%s]", this.getClass().getSimpleName(), value)
                : "Value.empty";
        }
    }
    
    final class NumberValue implements Value {
        private final Number value;
        
        public NumberValue() {
            this(null);
        }
        
        public NumberValue(Number n) {
            this.value = n;
        }

        @Override
        public String value() {
            return String.valueOf(value);
        }
        
        @Override
        public int asInt() {
            if (!isPresent()) throw Up.ParseError("Value is missing");
            return value.intValue();
        }
        
        @Override
        public long asLong() {
            if (!isPresent()) throw Up.ParseError("Value is missing");
            return value.longValue();
        }
        
        @Override
        public double asDouble() {
            if (!isPresent()) throw Up.ParseError("Value is missing");
            return value.doubleValue();
        }
        
        @Override
        public boolean isPresent() {
            return value != null;
        }
        
        @Override
        public String toString() {
            return value != null
                ? String.format("%s[%s]", this.getClass().getSimpleName(), value)
                : "Value.empty";
        }
    }
    
    final class ListValue implements Value {
        
        private final List valuables;

        public ListValue(final List valuables) {
            this.valuables = valuables;
        }

        @Override
        public String value() {
            return valuables.get(0).value();
        }

        @Override
        public List asList() {
            return valuables.stream().map(Value::value).collect(Collectors.toList());
        }

        @Override
        public Set asSet() {
            return valuables.stream().map(Value::value).collect(Collectors.toSet());
        }

        @Override
        public boolean isPresent() {
            return !valuables.isEmpty();
        }
        
        @Override
        public Iterator iterator() {
            return valuables.iterator();
        }
        
        @Override
        public String toString() {
            return valuables != null
                ? String.format("%s[%s]", this.getClass().getSimpleName(), valuables)
                : "Value.empty";
        }
    }
    
    interface ByteArrayValue extends Value {
        
        default String value(Charset charset) {
            return new String(bytes(), charset);
        }
        
        byte[] bytes();
        
        /**
         * The same as Content-Length header
         * @return size in bytes
         */
        long size();
        
        /**
         * Is the data contained in memory
         * @return if data is in memory
         */
        boolean inMemory();
        
        InputStream stream();
    }
    
    static Value empty() {
        return new Value() {

            @Override
            public String value() {
                return null;
            }

            @Override
            public boolean isPresent() {
                return false;
            }
            
        };
    }
}