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

jsonvalues.Prism Maven / Gradle / Ivy

package jsonvalues;

import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;


/**
 A Prism is an optic that can be seen as a pair of functions:
 {@code
 - getOptional: S -> Optional
 - reverseGet : T -> S
 }
 Typically a Prism encodes the relation between a Sum or CoProduct type and one of its element.

 @param  the source of a prism
 @param  the target of a prism */
public class Prism {
    /**
     get the target of a Prism or nothing if there is no target
     */
    public final Function> getOptional;

    /** get the modified source of a Prism */
    public final Function reverseGet;

    /**
     check if there is no target
     */
    public final Predicate isEmpty;

    /**
     check if there is a target
     */
    public final Predicate nonEmpty;

    /**
     modify the target of a Prism with a function, returning the same source if the prism is not matching. Basically
     it means we dont care about the success of the operation
     */
    public final Function,Function> modify;

    /**
     modify the target of a Prism with a function, returning empty if the prism is not matching. Unless modify, we
     need to know the success of the operation
     */
    public final Function,Function>> modifyOpt;

    /**
     find if the target satisfies the predicate
     */
    public final Function,Function>> find;

    /**
     check if there is a target and it satisfies the predicate
     */
    public final Function,Predicate> exists;

    /**
     check if there is no target or the target satisfies the predicate
     */
    public final Function,Predicate> all;

    Prism(final Function> getOptional,
          final Function reverseGet
         ) {
        this.getOptional = getOptional;
        this.reverseGet = reverseGet;
        this.modify = f -> {
            Objects.requireNonNull(f);
            return v ->
            {
                final Optional opt = getOptional.apply(v);
                if (opt.isPresent()) return reverseGet.apply(f.apply(opt.get()));
                else return v;
            };
        };
        this.modifyOpt = f -> {
            return v ->
            {
                final Optional opt = getOptional.apply(v);
                return opt.map(t -> reverseGet.apply(f.apply(t)));
            };
        };
        this.isEmpty = target -> !getOptional.apply(target)
                                            .isPresent();
        this.nonEmpty = target -> getOptional.apply(target)
                                              .isPresent();
        this.find = predicate -> v -> getOptional.apply(v)
                                                 .filter(predicate);

        this.exists = predicate -> v -> getOptional.apply(v)
                                                   .filter(predicate)
                                                   .isPresent();
        this.all = predicate -> v ->
        {
            final Optional value = getOptional.apply(v);
            return value.map(predicate::test)
                        .orElse(true);
        };
    }

}