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

org.pkl.thirdparty.paguro.collections.UnmodIterable Maven / Gradle / Ivy

Go to download

Fat Jar containing pkl-cli, pkl-codegen-java, pkl-codegen-kotlin, pkl-config-java, pkl-core, pkl-doc, and their shaded third-party dependencies.

There is a newer version: 0.27.1
Show newest version
package org.pkl.thirdparty.paguro.collections;

import org.pkl.thirdparty.jetbrains.annotations.NotNull;
import org.pkl.thirdparty.jetbrains.annotations.Nullable;
import org.pkl.thirdparty.paguro.function.Fn1;
import org.pkl.thirdparty.paguro.function.Fn2;
import org.pkl.thirdparty.paguro.oneOf.Option;
import org.pkl.thirdparty.paguro.oneOf.Or;
import org.pkl.thirdparty.paguro.xform.Transformable;
import org.pkl.thirdparty.paguro.xform.Xform;

import java.util.Iterator;
import java.util.Objects;

import static org.pkl.thirdparty.paguro.FunctionUtils.stringify;

/** An unmodifiable Iterable, without any guarantee about order. */
public interface UnmodIterable extends Iterable, Transformable {
    // ========================================== Static ==========================================

    @SuppressWarnings("rawtypes") // Need raw types here.
    enum UnIterable implements UnmodIterable {
        EMPTY {
            @Override
            public @NotNull UnmodIterator iterator() { return UnmodIterator.emptyUnmodIterator(); }
        }
    }

    /** We only ever need one empty iterable in memory. */
    @SuppressWarnings("unchecked")
    static  @NotNull UnmodIterable emptyUnmodIterable() {
        return (UnmodIterable) UnIterable.EMPTY;
    }

    //    /**
// Caution: this is a convenient optimization for immutable data structures and a nightmare
// waiting to happen to mutable ones.  Don't go slapping this on immutable wrappers for mutable
// data.  If all the underlying data is truly immutable, this allows you to compute the hashCode
// the first time it is needed, then return that same code without re-computing it again.  It's
// the internal version of a memoizer.  Also, use this only for decent sized collections.  If you
// only have 2 or 3 fields, this isn't buying you anything.
//     */
//    static Lazy.Int lazyHashCode(UnmodIterable iter) {
//        if (iter == null) { throw new IllegalArgumentException("Can't have a null iterable."); }
//        return Lazy.Int.of(() -> UnmodIterable.hashCode(iter));
//    }
//
//    /**
//     Caution: this is a convenient optimization for immutable data structures and a nightmare
//     waiting to happen to mutable ones.  Don't go slapping this on immutable wrappers for mutable
//     data structures.  If all the underlying data is truly immutable, this allows you to compute a
//     reasonable toString() the first time it is needed, then return that same String without
//     re-computing it again.  It's the internal version of a memoizer.
//     */
//    static LazyRef lazyToString(String name, UnmodIterable iter) {
//        if (name == null) { throw new IllegalArgumentException("Can't have a null name."); }
//        if (iter == null) { throw new IllegalArgumentException("Can't have a null iterable."); }
//        return LazyRef.of(() -> UnmodIterable.toString(name, iter));
//    }

//    /** Lets underlying compareTo method handle comparing nulls to non-null values. */
//    static > int compareHelper(E e1, E e2) {
//        if (e1 == e2) { return 0; }
//        if (e1 == null) {
//            //noinspection ConstantConditions
//            return -e2.compareTo(e1);
//        }
//        return e1.compareTo(e2);
//    }
//
//    /** A default comparator for UnIterables comparable */
//    static ,E extends UnmodIterable> Comparator
//    iterableComparator() {
//        return new Comparator() {
//            @Override
//            public int compare(E o1, E o2) {
//                if (o1 == null) {
//                    if (o2 == null) {
//                        return 0;
//                    } else {
//                        //noinspection ConstantConditions
//                        return -compare(o2, o1);
//                    }
//                }
//                UnmodIterator as = o1.iterator();
//                UnmodIterator bs = o2.iterator();
//                while (as.hasNext() && bs.hasNext()) {
//                    int ret = compareHelper(as.next(), bs.next());
//                    if (ret != 0) {
//                        return ret;
//                    }
//                }
//                // If we run out of items in one, the longer one is considered greater, just like
//                // ordering words in a dictionary.
//                if (as.hasNext()) { return -1; }
//                if (bs.hasNext()) { return 1; }
//                // All items compare 0 and same number of items - these are sorted the same (and
//                // probably equal)
//                return 0;
//            }
//        };
//    }

    /**
     This is correct, but O(n).  It also works regardless of the order of the items because
     a + b = b + a, even when an overflow occurs.
     */
    static int hash(@NotNull Iterable is) {
//        System.out.println("hashCode for: " + is);
        int ret = 0;
        for (Object t : is) {
            if (t != null) {
//                System.out.println("\tt: " + t + " hashCode: " + t.hashCode());
                ret = ret + t.hashCode();
            }
        }
        return ret;
    }

    /** Computes a reasonable to-string. */
    static @NotNull String toString(@NotNull String name, @NotNull Iterable iterable) {
        StringBuilder sB = new StringBuilder();
        sB.append(name).append("(");
        int i = 0;
        Iterator iter = iterable.iterator();
        while (iter.hasNext()) {
            if (i > 0) { sB.append(","); }
//            if (i > 4) { break; }
            Object item = iter.next();
            sB.append(stringify(item));
            i++;
        }
//        if (iter.hasNext()) {
//            sB.append("...");
//        }
        return sB.append(")").toString();
    }

    // ================================== Inherited from Iterable ==================================
    /**
     A one-time use, mutable, not-thread-safe way to get each value of the underling collection in
     turn. I experimented with various thread-safe alternatives, but the JVM is optimized around
     iterators so this is the lowest common denominator of collection iteration, even though
     iterators are inherently mutable.
     */
    @Override @NotNull UnmodIterator iterator();

    // =============================== Inherited from Transformable ===============================

    /** {@inheritDoc} */
    @Override
    default @NotNull UnmodIterable concat(@Nullable Iterable list) {
        return Xform.of(this).concat(list);
    }

    /** {@inheritDoc} */
    @Override
    default @NotNull UnmodIterable precat(@Nullable Iterable list) {
        return Xform.of(this).precat(list);
    }

    /** {@inheritDoc} */
    @Override
    default @NotNull UnmodIterable drop(long n) {
        return Xform.of(this).drop(n);
    }

    /** {@inheritDoc} */
    @Override
    default @NotNull UnmodIterable dropWhile(@NotNull Fn1 predicate) {
        return Xform.of(this).dropWhile(predicate);
    }

    /** {@inheritDoc} */
    @Override
    default  B fold(B ident, @NotNull Fn2 reducer) {
        return Xform.of(this).fold(ident, reducer);
    }

    /** {@inheritDoc} */
    @Override
    default  @NotNull Or foldUntil(
            G accum,
            @Nullable Fn2 terminator,
            @NotNull Fn2 reducer
    ) {

        return Xform.of(this).foldUntil(accum, terminator, reducer);
    }

    /** {@inheritDoc} */
    @Override default @NotNull UnmodIterable filter(@NotNull Fn1 f) {
        return Xform.of(this).filter(f);
    }

    /** {@inheritDoc} */
    @Override default @NotNull UnmodIterable<@NotNull T> whereNonNull() {
        return Xform.of(this).filter(Objects::nonNull);
    }

    /** {@inheritDoc} */
    @Override default  @NotNull UnmodIterable flatMap(@NotNull Fn1> f) {
        return Xform.of(this).flatMap(f);
    }

    /** {@inheritDoc} */
    @Override default  @NotNull UnmodIterable map(@NotNull Fn1 f) {
        return Xform.of(this).map(f);
    }

    /** {@inheritDoc} */
    @Override default @NotNull UnmodIterable take(long numItems) {
        return Xform.of(this).take(numItems);
    }

    /** {@inheritDoc} */
    @Override default @NotNull UnmodIterable takeWhile(@NotNull Fn1 f) {
        return Xform.of(this).takeWhile(f);
    }

    /** The first item in this iterable. */
    @Override
    default @NotNull Option head() {
        Iterator iter = iterator();
        return iter.hasNext() ? Option.some(iter.next())
                              : Option.none();
    }
}