/*
* Copyright (c) 2017, Fernando Miguel Carvalho, [email protected]
*
* 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 org.jayield;
import org.jayield.ops.FromArray;
import org.jayield.ops.Concat;
import org.jayield.ops.Distinct;
import org.jayield.ops.DropWhile;
import org.jayield.ops.Filter;
import org.jayield.ops.FlatMap;
import org.jayield.ops.Generate;
import org.jayield.ops.Iterate;
import org.jayield.ops.Limit;
import org.jayield.ops.FromList;
import org.jayield.ops.Mapping;
import org.jayield.ops.Peek;
import org.jayield.ops.Skip;
import org.jayield.ops.FromStream;
import org.jayield.ops.TakeWhile;
import org.jayield.ops.Zip;
import org.jayield.boxes.BoolBox;
import org.jayield.boxes.Box;
import org.jayield.primitives.dbl.DoubleAdvancer;
import org.jayield.primitives.dbl.DoubleQuery;
import org.jayield.primitives.dbl.DoubleTraverser;
import org.jayield.primitives.intgr.IntAdvancer;
import org.jayield.primitives.intgr.IntQuery;
import org.jayield.primitives.intgr.IntTraverser;
import org.jayield.primitives.lng.LongAdvancer;
import org.jayield.primitives.lng.LongQuery;
import org.jayield.primitives.lng.LongTraverser;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators.AbstractSpliterator;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* A sequence of elements supporting sequential operations.
* Query operations are composed into a pipeline to perform
* computation.
*
* @author Miguel Gamboa
* created on 04-06-2017
*/
public class Query {
private final Advancer adv;
private final Traverser trav;
public Query(Advancer adv, Traverser trav) {
this.adv = adv;
this.trav = trav;
}
/**
* Yields elements sequentially in the current thread,
* until all elements have been processed or an
* exception is thrown.
*/
public final void traverse(Yield super T> yield) {
this.trav.traverse(yield);
}
/**
* If a remaining element exists, yields that element through
* the given action.
*/
public boolean tryAdvance(Yield super T> action) {
return this.adv.tryAdvance(action);
}
/**
* Yields elements sequentially in the current thread,
* until all elements have been processed or the traversal
* exited normally through the invocation of yield.bye().
*/
public final void shortCircuit(Yield yield) {
try{
this.trav.traverse(yield);
}catch(TraversableFinishError e){
/* Proceed */
}
}
/**
* Returns a sequential ordered query whose elements
* are the specified values in data parameter.
*/
public static Query of(U...data) {
FromArray adv = new FromArray<>(data);
return new Query<>(adv, adv);
}
/**
* Returns a sequential ordered query with elements
* from the provided List data.
*/
public static Query fromList(List data) {
FromList adv = new FromList<>(data);
return new Query<>(adv, adv);
}
/**
* Returns a sequential ordered query with elements
* from the provided stream data.
*/
public static Query fromStream(Stream data) {
FromStream adv = new FromStream<>(data);
return new Query<>(adv, adv);
}
/**
* Returns an infinite sequential ordered {@code Query} produced by iterative
* application of a function {@code f} to an initial element {@code seed},
* producing a {@code Query} consisting of {@code seed}, {@code f(seed)},
* {@code f(f(seed))}, etc.
*
*/
public static Query iterate(U seed, UnaryOperator f) {
Iterate iter = new Iterate<>(seed, f);
return new Query<>(iter, iter);
}
/**
* Returns a query consisting of the results of applying the given
* function to the elements of this query.
*/
public final Query map(Function super T,? extends R> mapper) {
Mapping map = new Mapping<>(this, mapper);
return new Query<>(map, map);
}
/**
* Applies a specified function to the corresponding elements of two
* sequences, producing a sequence of the results.
*/
public final Query zip(Query other, BiFunction super T, ? super U, ? extends R> zipper) {
Zip zip = new Zip<>(this, other, zipper);
return new Query<>(zip, zip);
}
/**
* Returns a {@link IntQuery} with the elements of this {@code Query} mapped by
* a {@link ToIntFunction}
*
* @param mapper
* ToIntFunction used to map elements of this {@code Query} to int
*/
public final IntQuery mapToInt(ToIntFunction super T> mapper) {
return new IntQuery(IntAdvancer.from(adv, mapper), IntTraverser.from(trav, mapper));
}
/**
* Returns a {@link LongQuery} with the elements of this {@code Query} mapped by
* a {@link ToLongFunction}
*
* @param mapper
* ToLongFunction used to map elements of this {@code Query} to long
*/
public final LongQuery mapToLong(ToLongFunction super T> mapper) {
return new LongQuery(LongAdvancer.from(adv, mapper), LongTraverser.from(trav, mapper));
}
/**
* Returns a {@link DoubleQuery} with the elements of this {@code Query} mapped by
* a {@link ToDoubleFunction}
*
* @param mapper
* ToLongFunction used to map elements of this {@code Query} to double
*/
public final DoubleQuery mapToDouble(ToDoubleFunction super T> mapper) {
return new DoubleQuery(DoubleAdvancer.from(adv, mapper), DoubleTraverser.from(trav, mapper));
}
/**
* Returns a query consisting of the elements of this query that match
* the given predicate.
*/
public final Query filter(Predicate super T> p) {
Filter filter = new Filter<>(this, p);
return new Query<>(filter, filter);
}
/**
* Returns a query consisting of the remaining elements of this query
* after discarding the first {@code n} elements of the query.
*/
public final Query skip(int n){
Skip skip = new Skip<>(this, n);
return new Query<>(skip, skip);
}
/**
* Returns a query consisting of the elements of this query, truncated
* to be no longer than {@code n} in length.
*/
public final Query limit(int n){
Limit limit = new Limit<>(this, n);
return new Query<>(limit, limit);
}
/**
* Returns a query consisting of the distinct elements (according to
* {@link Object#equals(Object)}) of this query.
*/
public final Query distinct(){
Distinct dis = new Distinct<>(this);
return new Query<>(dis, dis);
}
/**
* Returns a query consisting of the results of replacing each element of
* this query with the contents of a mapped query produced by applying
* the provided mapping function to each element.
*/
public final Query flatMap(Function super T,? extends Query extends R>> mapper){
FlatMap map = new FlatMap<>(this, mapper);
return new Query<>(map, map);
}
/**
* Returns a query consisting of the elements of this query, additionally
* performing the provided action on each element as elements are consumed
* from the resulting query.
*/
public final Query peek(Consumer super T> action) {
Peek peek = new Peek<>(this, action);
return new Query<>(peek, peek);
}
/**
* Returns a query consisting of the longest prefix of elements taken from
* this query that match the given predicate.
*/
public final Query takeWhile(Predicate super T> predicate){
TakeWhile take = new TakeWhile<>(this, predicate);
return new Query<>(take, take);
}
/**
* The {@code then} operator lets you encapsulate a piece of an operator
* chain into a function.
* That function {@code next} is applied to this query to produce a new
* {@code Traverser} object that is encapsulated in the resulting query.
* On the other hand, the {@code nextAdv} is applied to this query to produce a new
* {@code Advancer} object that is encapsulated in the resulting query.
*/
public final Query then(Function, Advancer> nextAdv, Function, Traverser> next) {
return new Query<>(nextAdv.apply(this), next.apply(this));
}
/**
* The {@code then} operator lets you encapsulate a piece of an operator
* chain into a function.
* That function {@code next} is applied to this query to produce a new
* {@code Traverser} object that is encapsulated in the resulting query.
*/
public final Query then(Function, Traverser> next) {
Advancer nextAdv = item -> { throw new UnsupportedOperationException(
"Missing tryAdvance() implementation! Use the overloaded then() providing both Advancer and Traverser!");
};
return new Query<>(nextAdv, next.apply(this));
}
/**
* Returns a list containing the elements of this query.
*/
public final List toList() {
List data = new ArrayList<>();
this.traverse(data::add);
return data;
}
/**
* Returns an array containing the elements of this query.
*/
public final Object[] toArray() {
return this.toArray(Object[]::new);
}
public final Stream toStream() {
Spliterator iter = new AbstractSpliterator(Long.MAX_VALUE, Spliterator.ORDERED) {
@Override
public boolean tryAdvance(Consumer super T> action) {
return adv.tryAdvance(action::accept);
}
@Override
public void forEachRemaining(Consumer super T> action) {
trav.traverse(action::accept);
}
};
return StreamSupport.stream(iter, false);
}
/**
* Returns an {@link Optional} describing the first element of this query,
* or an empty {@code Optional} if this query is empty.
*/
public final Optional findFirst(){
Box box = new Box<>();
this.tryAdvance(box::turnPresent);
return box.isPresent()
? Optional.of(box.getValue())
: Optional.empty();
}
/**
* Returns the maximum element of this query according to the provided
* {@code Comparator}. This is a special case of a reduction.
*/
public final Optional max(Comparator super T> cmp){
class BoxMax extends Box implements Yield {
@Override
public final void ret(T item) {
if(!isPresent()) turnPresent(item);
else if(cmp.compare(item, value) > 0) value = item;
}
}
BoxMax b = new BoxMax();
this.traverse(b);
return b.isPresent() ? Optional.of(b.getValue()) : Optional.empty();
}
/**
* Returns whether any elements of this query match the provided
* predicate. May not evaluate the predicate on all elements if not
* necessary for determining the result. If the query is empty then
* {@code false} is returned and the predicate is not evaluated.
*/
public final boolean anyMatch(Predicate super T> p) {
BoolBox found = new BoolBox();
shortCircuit(item -> {
if(p.test(item)) {
found.set();
Yield.bye();
}
});
return found.isTrue();
}
/**
* Returns whether all elements of this query match the provided
* predicate. May not evaluate the predicate on all elements if not
* necessary for determining the result. If the query is empty then
* {@code true} is returned and the predicate is not evaluated.
*/
public final boolean allMatch(Predicate super T> p) {
BoolBox succeed = new BoolBox(true);
shortCircuit(item -> {
if(!p.test(item)) {
succeed.set(false);
Yield.bye();
}
});
return succeed.isTrue();
}
/**
* Returns the count of elements in this query.
*/
public final long count() {
class Counter implements Yield {
long n = 0;
@Override
public void ret(T item) {
++n;
}
}
Counter c = new Counter();
this.traverse(c);
return c.n;
}
/**
* Returns an {@link Optional} with the resulting reduction of the elements of this {@code Query},
* if a reduction can be made, using the provided accumulator.
*/
public Optional reduce(BinaryOperator accumulator) {
Box box = new Box<>();
if(this.tryAdvance(box::setValue)) {
return Optional.of(this.reduce(box.getValue(), accumulator));
} else {
return Optional.empty();
}
}
/**
* Returns the result of the reduction of the elements of this query,
* using the provided identity value and accumulator.
*/
public T reduce(T identity, BinaryOperator accumulator) {
class BoxAccumulator extends Box implements Yield {
public BoxAccumulator(T identity) {
super(identity);
}
@Override
public final void ret(T item) {
this.value = accumulator.apply(value, item);
}
}
BoxAccumulator box = new BoxAccumulator(identity);
this.traverse(box);
return box.getValue();
}
/**
* Yields elements sequentially in the current thread,
* until all elements have been processed or an
* exception is thrown.
*/
public final void forEach(Yield super T> yield) {
this.traverse(yield);
}
/**
* Returns a {@link Set} containing the elements of this query.
*/
public final Set toSet() {
Set data = new HashSet<>();
this.traverse(data::add);
return data;
}
/**
* Returns an array containing the elements of this query.
*/
public final U[] toArray(IntFunction generator) {
return this.toList().toArray(generator);
}
/**
* Returns the concatenation of the input elements into a String, in encounter order.
*/
public final String join() {
return this.map(String::valueOf)
.collect(StringBuilder::new, StringBuilder::append)
.toString();
}
/**
* Returns an {@link Optional} describing any element of this query,
* or an empty {@code Optional} if this query is empty.
*/
public final Optional findAny(){
return this.findFirst();
}
/**
* Returns the minimum element of this query according to the provided
* {@code Comparator}. This is a special case of a reduction.
*/
public final Optional min(Comparator super T> cmp) {
return this.max((a, b) -> cmp.compare(a, b) * -1);
}
/**
* Returns whether no elements of this query match the provided
* predicate. May not evaluate the predicate on all elements if not
* necessary for determining the result. If the query is empty then
* {@code true} is returned and the predicate is not evaluated.
*/
public final boolean noneMatch(Predicate super T> p) {
return !this.anyMatch(p);
}
/**
* Returns an infinite sequential unordered {@code Query}
* where each element is generated by the provided Supplier.
*/
public static Query generate(Supplier s) {
Generate gen = new Generate<>(s);
return new Query<>(gen, gen);
}
/**
* Performs a mutable reduction operation on the elements of this {@code Query}.
* A mutable reduction is one in which the reduced value is a mutable result container, such as an ArrayList,
* and elements are incorporated by updating the state of the result rather than by replacing the result.
*/
public R collect(Supplier supplier, BiConsumer accumulator) {
R result = supplier.get();
this.traverse(elem -> accumulator.accept(result, elem));
return result;
}
/**
* Creates a concatenated {@code Query} in which the elements are
* all the elements of this {@code Query} followed by all the
* elements of the other {@code Query}.
*/
public final Query concat(Query other) {
Concat con = new Concat<>(this, other);
return new Query<>(con, con);
}
/**
* Returns a {@code Query} consisting of the elements of this {@code Query},
* sorted according to the provided Comparator.
*
* This is a stateful intermediate operation.
*/
public final Query sorted(Comparator comparator) {
T[] state = (T[]) this.toArray();
Arrays.sort(state, comparator);
FromArray sorted = new FromArray<>(state);
return new Query<>(sorted, sorted);
}
/**
* Returns a {@code Query} consisting of the remaining elements of this query
* after discarding the first sequence of elements that match the given Predicate.
*/
public final Query dropWhile(Predicate predicate) {
DropWhile drop = new DropWhile<>(this, predicate);
return new Query<>(drop, drop);
}
}