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

org.reactfx.util.Lists Maven / Gradle / Ivy

package org.reactfx.util;

import static org.reactfx.util.Tuples.*;

import java.util.AbstractList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
import java.util.function.Function;

public final class Lists {

    // private constructor to prevent instantiation
    private Lists() {}

    public static  int hashCode(List list) {
        int hashCode = 1;
        for(E e: list) {
            hashCode = 31 * hashCode + Objects.hashCode(e);
        }
        return hashCode;
    }

    public static boolean equals(List list, Object o) {
        if(o == list) return true;

        if(!(o instanceof List)) return false;

        List that = (List) o;

        if(list.size() != that.size()) return false;

        Iterator it1 = list.iterator();
        Iterator it2 = that.iterator();
        while(it1.hasNext()) {
            if(!Objects.equals(it1.next(), it2.next())) {
                return false;
            }
        }

        return true;
    }

    public static String toString(List list) {
        StringBuilder res = new StringBuilder();
        res.append('[');
        Iterator it = list.iterator();
        while(it.hasNext()) {
            res.append(it.next());
            if(it.hasNext()) {
                res.append(", ");
            }
        }
        res.append(']');
        return res.toString();
    }

    public static  Iterator readOnlyIterator(Collection col) {
        return new Iterator() {
            private final Iterator delegate = col.iterator();

            @Override
            public boolean hasNext() {
                return delegate.hasNext();
            }

            @Override
            public E next() {
                return delegate.next();
            }
        };
    }

    public static boolean isValidIndex(int index, int size) {
        return isValidIndex(0, index, size);
    }

    public static boolean isValidIndex(int min, int index, int max) {
        return min <= index && index < max;
    }

    public static void checkIndex(int index, int size) {
        checkIndex(0, index, size);
    }

    public static void checkIndex(int min, int index, int max) {
        if(!isValidIndex(min, index, max)) {
            throw new IndexOutOfBoundsException(index + " not in [" + min + ", " + max + ")");
        }
    }

    public static boolean isValidPosition(int position, int size) {
        return isValidPosition(0, position, size);
    }

    public static boolean isValidPosition(int min, int position, int max) {
        return min <= position && position <= max;
    }

    public static void checkPosition(int position, int size) {
        checkPosition(0, position, size);
    }

    public static void checkPosition(int min, int position, int max) {
        if(!isValidPosition(min, position, max)) {
            throw new IndexOutOfBoundsException(position + " not in [" + min + ", " + max + "]");
        }
    }

    public static boolean isValidRange(int from, int to, int size) {
        return isValidRange(0, from, to, size);
    }

    public static boolean isValidRange(int min, int from, int to, int max) {
        return min <= from && from <= to && to <= max;
    }

    public static void checkRange(int from, int to, int size) {
        checkRange(0, from, to, size);
    }

    public static void checkRange(int min, int from, int to, int max) {
        if(!isValidRange(min, from, to, max)) {
            throw new IndexOutOfBoundsException(
                    "[" + from + ", " + to + ") is not a valid range within " +
                    "[" + min + ", " + max + ")");
        }
    }

    public static boolean isNonEmptyRange(int from, int to, int size) {
        return isNonEmptyRange(0, from, to, size);
    }

    public static boolean isNonEmptyRange(int min, int from, int to, int max) {
        return min <= from && from < to && to <= max;
    }

    public static boolean isProperRange(int from, int to, int size) {
        return isProperRange(0, from, to, size);
    }

    public static boolean isProperRange(int min, int from, int to, int max) {
        return isValidRange(min, from, to, max) && (min < from || to < max);
    }

    public static boolean isProperNonEmptyRange(int from, int to, int size) {
        return isProperNonEmptyRange(0, from, to, size);
    }

    public static boolean isProperNonEmptyRange(int min, int from, int to, int max) {
        return isNonEmptyRange(min, from, to, max) && (min < from || to < max);
    }

    public static boolean isStrictlyInsideRange(int from, int to, int size) {
        return isStrictlyInsideRange(0, from, to, size);
    }

    public static boolean isStrictlyInsideRange(int min, int from, int to, int max) {
        return min < from && from <= to && to < max;
    }

    public static boolean isStrictlyInsideNonEmptyRange(int from, int to, int size) {
        return isStrictlyInsideNonEmptyRange(0, from, to, size);
    }

    public static boolean isStrictlyInsideNonEmptyRange(int min, int from, int to, int max) {
        return min < from && from < to && to < max;
    }

    public static  List mappedView(
            List source,
            Function f) {
        return new AbstractList() {

            @Override
            public F get(int index) {
                return f.apply(source.get(index));
            }

            @Override
            public int size() {
                return source.size();
            }
        };
    }

    @SafeVarargs
    public static  List concatView(List... lists) {
        return concatView(Arrays.asList(lists));
    }

    /**
     * Returns a list that is a concatenation of the given lists. The returned
     * list is a view of the underlying lists, without copying the elements.
     * The returned list is unmodifiable. Modifications to underlying lists
     * will be visible through the concatenation view.
     */
    public static  List concatView(List> lists) {
        if(lists.isEmpty()) {
            return Collections.emptyList();
        } else {
            return ConcatView.create(lists);
        }
    }

    @SafeVarargs
    public static  List concat(List... lists) {
        return concat(Arrays.asList(lists));
    }

    /**
     * Returns a list that is a concatenation of the given lists. The returned
     * list is a view of the underlying lists, without copying the elements.
     * As opposed to {@link #concatView(List)}, the underlying lists must not
     * be modified while the returned concatenation view is in use. On the other
     * hand, this method guarantees balanced nesting if some of the underlying
     * lists are already concatenations created by this method.
     */
    public static  List concat(List> lists) {
        return ListConcatenation.create(lists);
    }

    public static int commonPrefixLength(List l, List m) {
        ListIterator i = l.listIterator();
        ListIterator j = m.listIterator();
        while(i.hasNext() && j.hasNext()) {
            if(!Objects.equals(i.next(), j.next())) {
                return i.nextIndex() - 1;
            }
        }
        return i.nextIndex();
    }

    public static int commonSuffixLength(List l, List m) {
        ListIterator i = l.listIterator(l.size());
        ListIterator j = m.listIterator(m.size());
        while(i.hasPrevious() && j.hasPrevious()) {
            if(!Objects.equals(i.previous(), j.previous())) {
                return l.size() - i.nextIndex() - 1;
            }
        }
        return l.size() - i.nextIndex();
    }

    /**
     * Returns the lengths of common prefix and common suffix of two lists.
     * The total of the two lengths returned is not greater than either of
     * the list sizes. Prefix is prioritized: for lists [a, b, a, b], [a, b]
     * returns (2, 0); although the two lists have a common suffix of length 2,
     * the length of the second list is already included in the length of the
     * common prefix.
     */
    public static Tuple2 commonPrefixSuffixLengths(List l1, List l2) {
        int n1 = l1.size();
        int n2 = l2.size();

        if(n1 == 0 || n2 == 0) {
            return t(0, 0);
        }

        int pref = commonPrefixLength(l1, l2);
        if(pref == n1 || pref == n2) {
            return t(pref, 0);
        }

        int suff = commonSuffixLength(l1, l2);

        return t(pref, suff);
    }
}

class ConcatView extends AbstractList {

    static  List create(List> lists) {
        return concatView(lists, true);
    }

    private static  List concatView(
            List> lists, boolean makeUnmodifiable) {
        int len = lists.size();
        if(len < 1) {
            throw new AssertionError("Supposedly unreachable code");
        } else if(len == 1) {
            List list = lists.get(0);
            if(makeUnmodifiable) {
                return Collections.unmodifiableList(list);
            } else {
                @SuppressWarnings("unchecked")
                List lst = (List) list;
                return lst;
            }
        } else {
            int mid = len / 2;
            return new ConcatView<>(
                    concatView(lists.subList(0, mid), false),
                    concatView(lists.subList(mid, len), false));
        }
    }

    private final List first;
    private final List second;

    ConcatView(List first, List second) {
        this.first = first;
        this.second = second;
    }

    @Override
    public E get(int index) {
        if(index < first.size()) {
            return first.get(index);
        } else {
            return second.get(index - first.size());
        }
    }

    @Override
    public int size() {
        return first.size() + second.size();
    }
}

class ListConcatenation extends AbstractList {

    private static final ToSemigroup, Integer> LIST_SIZE_MONOID =
            new ToSemigroup, Integer>() {

                @Override
                public Integer apply(List t) {
                    return t.size();
                }

                @Override
                public Integer reduce(Integer left, Integer right) {
                    return left + right;
                }

            };

    static  List create(List> lists) {
        return lists.stream()
            .filter(l -> !l.isEmpty())
            .map(l -> {
                @SuppressWarnings("unchecked") // cast safe because l is unmodifiable
                List lst = (List) l;
                return lst instanceof ListConcatenation
                    ? ((ListConcatenation) lst).ft
                    : FingerTree.mkTree(Collections.singletonList(lst), LIST_SIZE_MONOID);
            })
            .reduce(FingerTree::join)
            .>map(ListConcatenation::new)
            .orElse(Collections.emptyList());
    }

    private final FingerTree, Integer> ft;

    ListConcatenation(FingerTree, Integer> ft) {
        this.ft = ft;
    }

    @Override
    public E get(int index) {
        return ft.get(Integer::intValue, index, List::get);
    }

    @Override
    public int size() {
        return ft.getSummary(0);
    }

    @Override
    public List subList(int from, int to) {
        Lists.checkRange(from, to, size());
        return trim(to).drop(from);
    }

    private ListConcatenation trim(int limit) {
        return ft.caseEmpty().>unify(
                emptyTree -> this,
                neTree -> neTree.split(Integer::intValue, limit).map((l, m, r) -> {
                    FingerTree, Integer> t =
                            m.map((lst, i) -> i == 0 ? l : l.append(lst.subList(0, i)));
                    return new ListConcatenation<>(t);
                }));
    }

    private ListConcatenation drop(int n) {
        return ft.caseEmpty().>unify(
                emptyTree -> this,
                neTree -> neTree.split(Integer::intValue, n).map((l, m, r) -> {
                    FingerTree, Integer> t =
                            m.map((lst, i) -> i == lst.size() ? r : r.prepend(lst.subList(i, lst.size())));
                    return new ListConcatenation<>(t);
                }));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy