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

jsonvalues.Lens Maven / Gradle / Ivy

package jsonvalues;

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

import static java.util.Objects.requireNonNull;

/**
 * A Lens is an optic that can be seen as a pair of functions:
 {@code
  - get: S      => O i.e. from an S, we can extract an O
  - set: (O, S) => S i.e. from an S and a O, we obtain a S. Unless a prism, to go back to S we need another S.
 }
 * Typically a Lens can be defined between a Product (e.g. record, tuple) and one of its component.
 * Given a lens there are essentially three things you might want to do:
 * -view the subpart
 * -modify the whole by changing the subpart
 * -combine this lens with another lens to look even deeper
 *
 * @param  the source of a lens
 * @param  the target of a lens
 */
public class Lens {

  /**
   * function to view the part
   */
  public final Function get;
  /**
   * function to modify the whole by setting the subpart
   */
  public final Function> set;

    /**
     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;
  /**
   * function to modify the whole by modifying the subpart with a function
   */
  public final Function, Function> modify;

  Lens(final Function get,
       final Function> set) {

    this.set = set;
    this.get = get;
    this.modify = f -> json -> set.apply(f.apply(get.apply(json))).apply(json);
    this.find = predicate -> s -> predicate.test(get.apply(s)) ?
                                     Optional.of((get.apply(s))) :
                                     Optional.empty();
    this.exists = predicate -> s -> predicate.test(get.apply(s));
  }


    /**
     Composing a Lens and a Prism returns and Optional
     @param prism A Prism from the focus of the lens to the new focus of the Optional
     @param  the type of the new focus of the Optional
     @return an Optional
     */
  public  Option compose(final Prism prism) {
        return new Option<>(json -> requireNonNull(prism).getOptional.apply(get.apply(json)),
                            value -> json -> set.apply(prism.reverseGet.apply(value))
                                                .apply(json)
        );


    }

    /**
     Compose this lens with another one
     @param other the other lens
     @param  the type of the focus on the new lens
     @return a new Lens
     */
    public  Lens compose(final Lens other){

      return new Lens<>(this.get.andThen(other.get),
                           b-> s -> {
                               O o = other.set.apply(b).apply(this.get.apply(s));
                               return this.set.apply(o).apply(s);
                           }
      );
    }

}