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

com.jongsoft.lang.collection.impl.TailedList Maven / Gradle / Ivy

/*
 * The MIT License
 *
 * Copyright 2016-2018 Jong Soft.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.jongsoft.lang.collection.impl;

import static java.lang.String.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;

import com.jongsoft.lang.collection.Iterator;
import com.jongsoft.lang.collection.Map;
import com.jongsoft.lang.collection.Sequence;
import com.jongsoft.lang.collection.support.AbstractIterator;
import com.jongsoft.lang.collection.support.Collections;

/**
 * A {@link TailedList} is an {@link Sequence} implementation where each entry in the list points to the next
 * entry in the list.
 *
 * @param  the type of elements contained within the {@link TailedList}
 * @see Linked list documentation
 * @since 0.0.1
 */
public class TailedList implements Sequence {
    private static final TailedList EMPTY = new TailedList<>(null, null);

    private final Object element;
    private final TailedList tail;

    private TailedList(T element, TailedList tail) {
        this.element = element;
        this.tail = tail;
    }

    @Override
    public Sequence append(T value) {
        return new TailedList<>(value, (TailedList) reverse()).reverse();
    }

    @Override
    public Sequence union(final Iterable iterable) {
        Sequence reversed = reverse();
        for (T value : iterable) {
            reversed = new TailedList<>(value, (TailedList) reversed);
        }

        return reversed.reverse();
    }

    @Override
    public Sequence insert(int index, T value) {
        validateIndexOutOfBounds(index);

        TailedList result = this;
        for (int i = 0; i < index; i++) {
            result = result.tail;
        }

        result = new TailedList<>(value, result);
        for (int i = index - 1; i >= 0; i--) {
            result = new TailedList<>(get(i), result);
        }

        return result;
    }

    @Override
    public int firstIndexWhere(final Predicate predicate) {
        int index = 0;
        for (TailedList list = this; !list.isEmpty(); list = list.tail, index++) {
            if (predicate.test(list.get())) {
                return index;
            }
        }
        return -1;
    }

    @Override
    @SuppressWarnings("unchecked")
    public T get(int index) {
        validateIndexOutOfBounds(index);

        int loopIdx = 0;
        TailedList computed;
        for (computed = this; loopIdx < index; computed = computed.tail, loopIdx++);
        return (T) computed.element;
    }

    @Override
    public Sequence tail() {
        return tail;
    }

    @Override
    public Sequence remove(int index) {
        validateIndexOutOfBounds(index);

        Sequence reversed = empty();
        for (TailedList newTail = this; !newTail.isEmpty(); newTail = newTail.tail, index--) {
            if (index != 0) {
                reversed = reversed.append(newTail.get());
            }
        }

        return reversed;
    }

    @Override
    public Sequence filter(Predicate predicate) {
        Objects.requireNonNull(predicate, "The predicate may not be null");

        Sequence filtered = empty();
        for (T value : reverse()) {
            if (predicate.test(value)) {
                filtered = new TailedList<>(value, (TailedList) filtered);
            }
        }

        return filtered;
    }

    @Override
    public Sequence distinct() {
        return null;
    }

    @Override
    public  Sequence map(final Function mapper) {
        Sequence mappedTail = empty();
        for (TailedList processing = this; !processing.isEmpty(); processing = processing.tail) {
            mappedTail = new TailedList<>(mapper.apply(processing.get()), (TailedList) mappedTail);
        }
        return mappedTail.reverse();
    }

    @Override
    public  Map> groupBy(final Function keyGenerator) {
        return Collections.groupBy(TailedList::empty, this, keyGenerator);
    }

    @Override
    public int size() {
        int size = 0;
        for (TailedList list = this; !list.isEmpty(); list = list.tail, size++);
        return size;
    }

    @Override
    public boolean isEmpty() {
        return EMPTY.equals(this);
    }

    @Override
    public Iterator iterator() {
        return new IteratorImpl<>(this);
    }

    @Override
    public List toJava() {
        java.util.List result = new java.util.ArrayList<>(size());
        forEach(result::add);
        return result;
    }

    public static  Collector, Sequence> collector() {
        return Collections.collector(TailedList::ofAll);
    }

    private void validateIndexOutOfBounds(int index) {
        if (index >= size()) {
            throw new IndexOutOfBoundsException(format("%s is not in the bounds of 0 and %s", index, size()));
        }
    }

    @Override
    public Sequence reverse() {
        Sequence corrected = empty();
        for (int i = 0; i < size(); i++) {
            corrected = new TailedList<>(get(i), (TailedList) corrected);
        }
        return corrected;
    }

    class IteratorImpl extends AbstractIterator {

        private TailedList start;
        private TailedList position;

        private IteratorImpl(TailedList position) {
            this.start = position;
            this.position = position;
        }

        @Override
        public void reset() {
            position = start;
        }

        @Override
        public boolean hasNext() {
            return !position.isEmpty();
        }

        @Override
        public T getNext() {
            T value = position.get();
            position = position.tail;
            return value;
        }

    }

    //------------------------------------------------------------------
    //-- Static supporting methods

    /**
     * Creates a new empty TailedList.
     *
     * @param    the type of the list
     * @return      the created list
     */
    @SuppressWarnings("unchecked")
    public static  Sequence empty() {
        return (TailedList) EMPTY;
    }

    /**
     * Create a new {@link TailedList} containing exactly one entry being the provided element.
     *
     * @param element the element to wrap in a {@link TailedList}
     * @param      the type of the element
     * @return        the {@link TailedList} containing the provided element
     */
    public static  Sequence of(T element) {
        return TailedList.of((T[]) new Object[]{element});
    }

    /**
     * Create a new {@link TailedList} containing all the elements provided in the order they were provided.
     *
     * @param elements  the elements to wrap in a {@link TailedList}
     * @param        the type of the elements
     * @return          the {@link TailedList} containing the provided elements
     */
    @SafeVarargs
    public static  Sequence of(T...elements) {
        return ofAll(Iterator.of(elements));
    }

    /**
     * Create a new {@link TailedList} containing all the elements provided in the order they were provided.
     *
     * @param iterable  the elements to wrap in a {@link TailedList}
     * @param        the type of the elements
     * @return          the {@link TailedList} containing the provided elements
     */
    public static  Sequence ofAll(Iterable iterable) {
        TailedList reversed = (TailedList) TailedList.EMPTY;
        for (T element : iterable) {
            reversed = new TailedList<>(element, reversed);
        }

        return reversed.reverse();
    }
}