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

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

There is a newer version: 2.0-M5
Show newest version
package org.reactfx.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.IntFunction;

public abstract class ListHelper {

    public static  T get(ListHelper listHelper, int index) {
        Lists.checkIndex(index, size(listHelper)); // always throws for listHelper == null
        return listHelper.get(index);
    }

    public static  ListHelper add(ListHelper listHelper, T elem) {
        if(listHelper == null) {
            return new SingleElemHelper<>(elem);
        } else {
            return listHelper.add(elem);
        }
    }

    public static  ListHelper remove(ListHelper listHelper, T elem) {
        if(listHelper == null) {
            return listHelper;
        } else {
            return listHelper.remove(elem);
        }
    }

    public static  void forEach(ListHelper listHelper, Consumer f) {
        if(listHelper != null) {
            listHelper.forEach(f);
        }
    }

    public static  void forEachBetween(
            ListHelper listHelper, int from, int to, Consumer f) {
        Lists.checkRange(from, to, size(listHelper));
        if(from < to) {
            listHelper.forEachBetween(from, to, f);
        }
    }

    public static  Iterator iterator(ListHelper listHelper) {
        if(listHelper != null) {
            return listHelper.iterator();
        } else {
            return Collections.emptyIterator();
        }
    }

    public static  Iterator iterator(ListHelper listHelper, int from, int to) {
        Lists.checkRange(from, to, size(listHelper));
        if(from < to) {
            return listHelper.iterator(from, to);
        } else {
            return Collections.emptyIterator();
        }
    }

    public static  Optional reduce(ListHelper listHelper, BinaryOperator f) {
        if(listHelper == null) {
            return Optional.empty();
        } else {
            return listHelper.reduce(f);
        }
    }

    public static  U reduce(ListHelper listHelper, U unit, BiFunction f) {
        if(listHelper == null) {
            return unit;
        } else {
            return listHelper.reduce(unit, f);
        }
    }

    public static  T[] toArray(ListHelper listHelper, IntFunction allocator) {
        if(listHelper == null) {
            return allocator.apply(0);
        } else {
            return listHelper.toArray(allocator);
        }
    }

    public static  boolean isEmpty(ListHelper listHelper) {
        return listHelper == null;
    }

    public static  int size(ListHelper listHelper) {
        if(listHelper == null) {
            return 0;
        } else {
            return listHelper.size();
        }
    }

    private ListHelper() {
        // private constructor to prevent subclassing
    };

    abstract T get(int index);
    abstract ListHelper add(T elem);
    abstract ListHelper remove(T elem);
    abstract void forEach(Consumer f);
    abstract void forEachBetween(int from, int to, Consumer f);
    abstract Iterator iterator();
    abstract Iterator iterator(int from, int to);
    abstract Optional reduce(BinaryOperator f);
    abstract  U reduce(U unit, BiFunction f);
    abstract T[] toArray(IntFunction allocator);
    abstract int size();


    private static class SingleElemHelper extends ListHelper {
        private final T elem;

        SingleElemHelper(T elem) {
            this.elem = elem;
        }

        @Override
        T get(int index) {
            assert index == 0;
            return elem;
        }

        @Override
        ListHelper add(T elem) {
            return new MultiElemHelper<>(this.elem, elem);
        }

        @Override
        ListHelper remove(T elem) {
            if(Objects.equals(this.elem, elem)) {
                return null;
            } else {
                return this;
            }
        }

        @Override
        void forEach(Consumer f) {
            f.accept(elem);
        }

        @Override
        void forEachBetween(int from, int to, Consumer f) {
            assert from == 0 && to == 1;
            f.accept(elem);
        }

        @Override
        Iterator iterator() {
            return new Iterator() {
                boolean hasNext = true;

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

                @Override
                public T next() {
                    if(hasNext) {
                        hasNext = false;
                        return elem;
                    } else {
                        throw new NoSuchElementException();
                    }
                }
            };
        }

        @Override
        Iterator iterator(int from, int to) {
            assert from == 0 && to == 1;
            return iterator();
        }

        @Override
        Optional reduce(BinaryOperator f) {
            return Optional.of(elem);
        }

        @Override
         U reduce(U unit, BiFunction f) {
            return f.apply(unit, elem);
        }

        @Override
        T[] toArray(IntFunction allocator) {
            T[] res = allocator.apply(1);
            res[0] = elem;
            return res;
        }

        @Override
        int size() {
            return 1;
        }
    }

    private static class MultiElemHelper extends ListHelper {
        private final List elems;

        // when > 0, this ListHelper must be immutable,
        // i.e. use copy-on-write for mutating operations
        private int iterating = 0;

        @SafeVarargs
        MultiElemHelper(T... elems) {
            this(Arrays.asList(elems));
        }

        private MultiElemHelper(List elems) {
            this.elems = new ArrayList<>(elems);
        }

        private MultiElemHelper copy() {
            return new MultiElemHelper<>(elems);
        }

        @Override
        T get(int index) {
            return elems.get(index);
        }

        @Override
        ListHelper add(T elem) {
            if(iterating > 0) {
                return copy().add(elem);
            } else {
                elems.add(elem);
                return this;
            }
        }

        @Override
        ListHelper remove(T elem) {
            int idx = elems.indexOf(elem);
            if(idx == -1) {
                return this;
            } else {
                switch(elems.size()) {
                case 0: // fall through
                case 1: throw new AssertionError();
                case 2: return new SingleElemHelper<>(elems.get(1-idx));
                default:
                    if(iterating > 0) {
                        return copy().remove(elem);
                    } else {
                        elems.remove(elem);
                        return this;
                    }
                }
            }
        }

        @Override
        void forEach(Consumer f) {
            ++iterating;
            try {
                elems.forEach(f);
            } finally {
                --iterating;
            }
        }

        @Override
        void forEachBetween(int from, int to, Consumer f) {
            ++iterating;
            try {
                elems.subList(from, to).forEach(f);
            } finally {
                --iterating;
            }
        }

        @Override
        Iterator iterator() {
            return iterator(0, elems.size());
        }

        @Override
        Iterator iterator(int from, int to) {
            assert from < to;

            ++iterating;
            return new Iterator() {
                int next = from;

                @Override
                public boolean hasNext() {
                    return next < to;
                }

                @Override
                public T next() {
                    if(next < to) {
                        T res = elems.get(next);
                        ++next;
                        if(next == to) {
                            --iterating;
                        }
                        return res;
                    } else {
                        throw new NoSuchElementException();
                    }
                }
            };
        }

        @Override
        Optional reduce(BinaryOperator f) {
            return elems.stream().reduce(f);
        }

        @Override
         U reduce(U unit, BiFunction f) {
            U u = unit;
            for(T elem: elems) {
                u = f.apply(u, elem);
            }
            return u;
        }

        @Override
        T[] toArray(IntFunction allocator) {
            return elems.toArray(allocator.apply(size()));
        }

        @Override
        int size() {
            return elems.size();
        }
    }
}