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

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

package com.jongsoft.lang.collection.impl;

import static java.lang.String.*;

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

import com.jongsoft.lang.API;
import com.jongsoft.lang.collection.Iterator;
import com.jongsoft.lang.collection.Set;
import com.jongsoft.lang.collection.support.Collections;

abstract class AbstractSet implements Set {

    private final Object[] delegate;

    AbstractSet(Object[] delegate) {
        this.delegate = delegate;
    }

    @Override
    public Set append(final T value) {
        if (contains(value)) {
            return this;
        }

        Object[] newDelegate = new Object[delegate.length + 1];
        System.arraycopy(delegate, 0, newDelegate, 0, delegate.length);
        newDelegate[delegate.length] = value;
        return this.wrapperSupplier().apply(newDelegate);
    }

    @Override
    @SuppressWarnings("Duplicates")
    public Set remove(final int index) {
        validateOutOfBounds(index);
        Object[] clone = new Object[delegate.length - 1];

        System.arraycopy(delegate, 0, clone, 0, index);
        System.arraycopy(delegate, index  + 1, clone, index, delegate.length - index - 1);

        return this.wrapperSupplier().apply(clone);
    }

    @Override
    @SuppressWarnings("Duplicates")
    public int firstIndexWhere(final Predicate predicate) {
        for (int i = 0; i < size(); i++) {
            if (predicate.test(get(i))) {
                return i;
            }
        }

        return -1;
    }

    @Override
    @SuppressWarnings("unchecked")
    public T get(final int index) {
        validateOutOfBounds(index);
        return (T) delegate[index];
    }

    @Override
    @SuppressWarnings("Duplicates")
    public Set tail() {
        if (size() == 0) {
            throw new NoSuchElementException("Cannot call tail on empty collection");
        } else if (size() == 1) {
            return this.emptySupplier().get();
        }

        Object[] tail = new Object[delegate.length - 1];
        System.arraycopy(delegate, 1, tail, 0, delegate.length - 1);
        return this.wrapperSupplier().apply(tail);
    }

    @Override
    public Set filter(final Predicate predicate) {
        return stream()
                .filter(predicate)
                .collect(collector());
    }

    @Override
    @SuppressWarnings("Duplicates")
    public  Set map(final Function mapper) {
        Objects.requireNonNull(mapper, "The mapper cannot be null for this operation.");

        Set mappedSet = this.emptySupplier().get();
        for (int i = 0; i < size(); i++) {
            mappedSet = mappedSet.append(mapper.apply(get(i)));
        }

        return mappedSet;
    }

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

    @Override
    @SuppressWarnings("unchecked")
    public Iterator iterator() {
        return Iterator.of((T[]) delegate);
    }

    @Override
    @SuppressWarnings("unchecked")
    public java.util.Set toJava() {
        java.util.Set result = new java.util.HashSet<>(delegate.length);
        for (Object o : delegate) {
            result.add((T) o);
        }
        return result;
    }

    @Override
    public Set union(final Iterable iterable) {
        return setTheory(this, iterable, Predicate.not(this::contains));
    }

    @Override
    @SafeVarargs
    public final Set intersect(final Iterable...iterable) {
        if (iterable.length == 0) {
            return this.emptySupplier().get();
        }

        Predicate operation = API.Set(iterable)
                .map(API::Set)
                .foldLeft(x -> true, (x, xs) -> x.and(xs::contains));

        return setTheory(this.emptySupplier().get(), this, operation);
    }

    @Override
    @SafeVarargs
    public final Set complement(final Iterable...iterables) {
        if (iterables.length == 0) {
            return this;
        }

        Predicate operation = API.Set(iterables)
                .map(API::Set)
                .foldLeft(x -> true, (x, xs) -> x.and(Predicate.not(xs::contains)));

        return setTheory(this.emptySupplier().get(), this, operation);
    }

    @Override
    public String toString() {
        return Collections.textValueOf("Set", this);
    }

    private void validateOutOfBounds(int index) {
        if (index >= delegate.length || index < 0) {
            throw new IndexOutOfBoundsException(format("%s is not in the bounds of 0 and %s", index, delegate.length));
        }
    }

    private Set setTheory(Set seed, Iterable iterable, Predicate theoryRule) {
        Set result = seed;

        for (T element : iterable) {
            if (theoryRule.test(element)) {
                result = result.append(element);
            }
        }

        return result;

    }

    protected abstract  Supplier> emptySupplier();
    protected abstract Function> wrapperSupplier();
    public abstract Collector, Set> collector();

}