Skip to content

Commit

Permalink
GH-518 Add Option and Stream wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
dzikoysk committed Jul 2, 2020
1 parent c11b5bc commit fc958b3
Show file tree
Hide file tree
Showing 7 changed files with 670 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2020 Dzikoysk
*
* 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.panda_lang.utilities.commons.function;

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

public class Case<T, R> {

private final Predicate<T> condition;
private final Function<T, R> value;

private Case(Predicate<T> condition, Function<T, R> value) {
this.condition = condition;
this.value = value;
}

public Predicate<T> getCondition() {
return condition;
}

public Function<T, R> getValue() {
return value;
}

public static <T, R> Case<T, R> of(Predicate<T> predicate, Function<T, R> function) {
return new Case<>(predicate, function);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
/*
* Copyright (c) 2020 Dzikoysk
*
* 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.panda_lang.utilities.commons.function;

import org.jetbrains.annotations.Nullable;
import org.panda_lang.utilities.commons.ObjectUtils;
import org.panda_lang.utilities.commons.iterable.SingletonIterator;

import java.io.Serializable;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;

public class Option<T> implements Iterable<T>, Serializable {

private static final Option<?> NONE = new Option<>(null);

private final @Nullable T value;

private Option(@Nullable T value) {
this.value = value;
}

@Override
public int hashCode() {
return value != null ? value.hashCode() : 0;
}

@Override
public boolean equals(@Nullable Object to) {
if (!(to instanceof Option)) {
return false;
}

return Objects.equals(value, ((Option<?>) to).value);
}

@Override
public Iterator<T> iterator() {
return isDefined() ? new SingletonIterator<>(value) : Collections.emptyIterator();
}

public Option<T> filter(Predicate<T> predicate) {
return (isDefined() && predicate.test(value)) ? this : Option.none();
}

public Option<T> filterNot(Predicate<T> predicate) {
return filter(value -> !predicate.test(value));
}

public <R> Option<R> map(Function<T, R> function) {
return isDefined() ? Option.of(function.apply(value)) : Option.none();
}

public <R> Option<R> flatMap(Function<T, Option<R>> function) {
return isDefined() ? function.apply(value) : Option.none();
}

@SafeVarargs
public final <R> Option<R> match(Case<T, R>... cases) {
if (isEmpty()) {
return Option.none();
}

for (Case<T, R> currentCase : cases) {
if (currentCase.getCondition().test(value)) {
return Option.of(currentCase.getValue().apply(value));
}
}

return Option.none();
}

public Option<T> peek(Consumer<T> consumer) {
if (isDefined()) {
consumer.accept(value);
}

return this;
}

public Option<T> onEmpty(Runnable runnable) {
if (isEmpty()) {
runnable.run();
}

return this;
}

public Option<T> orElse(T value) {
return isDefined() ? this : of(value);
}

public Option<T> orElse(Option<T> value) {
return isDefined() ? this : value;
}

public Option<T> orElse(Supplier<Option<T>> supplier) {
return isDefined() ? this : supplier.get();
}

public <E extends Throwable> T orThrow(Supplier<E> exceptionSupplier) throws E {
if (isEmpty()) {
throw exceptionSupplier.get();
}

return value;
}

public T orElseGet(T elseValue) {
return isDefined() ? value : elseValue;
}

public T orElseGet(Supplier<T> supplier) {
return isDefined() ? value : supplier.get();
}

public @Nullable T getOrNull() {
return value;
}

public T get() throws NoSuchElementException {
if (isEmpty()) {
throw new NoSuchElementException("Value is not defined");
}

return value;
}

public boolean isPresent() {
return isDefined();
}

public boolean isDefined() {
return value != null;
}

public boolean isEmpty() {
return value == null;
}

public PandaStream<T> toStream() {
return PandaStream.of(toJavaStream());
}

public Stream<T> toJavaStream() {
return isDefined() ? Stream.of(value) : Stream.empty();
}

public Optional<T> toOptional() {
return Optional.ofNullable(value);
}

public static <T> Option<T> none() {
return ObjectUtils.cast(NONE);
}

public static <T> Option<T> of(@Nullable T value) {
return value != null? new Option<>(value) : none();
}

@SuppressWarnings({ "OptionalUsedAsFieldOrParameterType" })
public static <T> Option<T> ofOptional(Optional<T> optional) {
return of(optional.orElse(null));
}

public static <T> Option<T> when(boolean flag, @Nullable T value) {
return flag ? of(value) : Option.none();
}

public static <T, E extends Exception> Option<T> attempt(Class<E> exceptionType, ThrowingSupplier<T, E> supplier) {
try {
return of(supplier.get());
} catch (Exception e) {
if (exceptionType.isAssignableFrom(e.getClass())) {
return Option.none();
}

throw new RuntimeException("Unsupported exception", e);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright (c) 2020 Dzikoysk
*
* 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.panda_lang.utilities.commons.function;

import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class PandaStream<T> {

private Stream<T> stream;

private PandaStream(Stream<T> stream) {
this.stream = stream;
}

public <R> PandaStream<R> stream(Function<Stream<T>, Stream<R>> function) {
return new PandaStream<>(function.apply(stream));
}

public <R> PandaStream<R> map(Function<T, R> function) {
return new PandaStream<>(stream.map(function));
}

public <R> PandaStream<R> flatMap(Function<T, Iterable<R>> function) {
return new PandaStream<>(stream.flatMap(value -> StreamSupport.stream(function.apply(value).spliterator(), false)));
}

public PandaStream<T> filter(Predicate<T> predicate) {
return with(stream.filter(predicate));
}

public PandaStream<T> sorted() {
return with(stream.sorted());
}

public PandaStream<T> sorted(Comparator<? super T> comparator) {
return with(stream.sorted(comparator));
}

public Option<T> find(Predicate<T> predicate) {
return filter(predicate).head();
}

public Option<T> head() {
return Option.ofOptional(stream.findFirst());
}

public long count(Predicate<T> predicate) {
return filter(predicate).count();
}

public long count() {
return stream.count();
}

private PandaStream<T> with(Stream<T> stream) {
this.stream = stream;
return this;
}

public <A, R> R collect(Collector<? super T, A, R> collector) {
return stream.collect(collector);
}

public T[] toArray(IntFunction<T[]> function) {
return stream.toArray(function);
}

public Stream<T> toStream() {
return stream;
}

public static <T> PandaStream<T> of(Stream<T> stream) {
return new PandaStream<>(stream);
}

public static <T> PandaStream<T> of(Collection<T> collection) {
return of(collection.stream());
}

public static <T> PandaStream<T> of(Iterable<T> iterable) {
return of(StreamSupport.stream(iterable.spliterator(), false));
}

public static <T> PandaStream<T> of(T[] array) {
return of(Arrays.stream(array));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@
* limitations under the License.
*/

package org.panda_lang.utilities.commons;
package org.panda_lang.utilities.commons.function;

import java.util.Collection;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -31,16 +30,16 @@ public final class StreamUtils {

private StreamUtils() { }

public static <T> int sum(Iterable<T> iterable, ToIntFunction<? super T> function) {
return stream(iterable).mapToInt(function).sum();
public static <T> long sum(Iterable<T> iterable, ToLongFunction<? super T> function) {
return stream(iterable).mapToLong(function).sum();
}

public static <T> long sumLongs(Iterable<T> iterable, ToLongFunction<? super T> function) {
return stream(iterable).mapToLong(function).sum();
}

public static <T> int count(Iterable<T> iterable, Predicate<T> filter) {
return (int) stream(iterable).filter(filter).count();
public static <T> long count(Iterable<T> iterable, Predicate<T> filter) {
return stream(iterable).filter(filter).count();
}

public static <T> Optional<T> findFirst(Iterable<T> iterable, Predicate<T> filter) {
Expand Down
Loading

0 comments on commit fc958b3

Please sign in to comment.