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

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

The newest version!
/*
 * The MIT License
 *
 * Copyright 2016-2019 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.Arrays;
import java.util.Comparator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

import com.jongsoft.lang.API;
import com.jongsoft.lang.collection.*;
import com.jongsoft.lang.collection.support.Collections;
import com.jongsoft.lang.collection.support.PipeCommand;
import com.jongsoft.lang.collection.tuple.Pair;

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("unchecked")
    public Set distinctBy(Comparator comparator) {
        return com.jongsoft.lang.Collections.Set(comparator, (T[]) delegate);
    }

    @Override
    public List sorted() {
        Object[] clone = Arrays.copyOf(delegate, delegate.length);
        Arrays.sort(clone);
        return new Array<>(clone);
    }

    @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
    public Set replace(int index, T replacement) {
        validateOutOfBounds(index);
        Object[] clone = new Object[delegate.length];
        System.arraycopy(delegate, 0, clone, 0, delegate.length);
        clone[index] = replacement;

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

    @Override
    @SuppressWarnings("unchecked")
    public Set replaceIf(Predicate predicate, T replacement) {
        Objects.requireNonNull(predicate, "The predicate cannot be null for this operation.");

        Object[] clone = new Object[delegate.length];
        System.arraycopy(delegate, 0, clone, 0, delegate.length);
        for (int index = 0; index < clone.length; index++) {
            if (predicate.test((T) clone[index])) {
                clone[index] = replacement;
            }
        }

        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("unchecked")
    public  Map> groupBy(final Function keyGenerator) {
        final Supplier> setSupplier = this.emptySupplier();
        return (Map>) Collections.groupBy(setSupplier::get, this, keyGenerator);
    }

    @Override
    public Pair, ? extends Collection> split(Predicate predicate) {
        Map> split = groupBy(predicate::test);
        return API.Tuple(split.get(true), split.get(false));
    }

    @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 Collections.filter(this.emptySupplier().get(), this, predicate);
    }

    @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 Set orElse(final Iterable other) {
        return isEmpty() ?
                this.wrapperSupplier().apply(com.jongsoft.lang.Collections.Iterator(other).toNativeArray())
                : this;
    }

    @Override
    public Set orElse(final Supplier> supplier) {
        return isEmpty() ?
                this.wrapperSupplier().apply(com.jongsoft.lang.Collections.Iterator(supplier.get()).toNativeArray())
                : this;
    }

    @Override
    public Pipeline pipeline() {
        return new PipeCommand<>(this);
    }

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

    @Override
    @SuppressWarnings("unchecked")
    public Iterator iterator() {
        return com.jongsoft.lang.Collections.Iterator((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 Collections.>filter(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 = com.jongsoft.lang.Collections.Set(iterable)
                .map(com.jongsoft.lang.Collections::Set)
                .foldLeft(x -> true, (x, xs) -> x.and(xs::contains));

        return Collections.filter(this.emptySupplier().get(), this, operation);
    }

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

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

        return Collections.filter(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));
        }
    }

    protected abstract  Supplier> emptySupplier();
    protected abstract Function> wrapperSupplier();

    @Override
    @SuppressWarnings({"unchecked", "rawtypes"})
    public boolean equals(final Object obj) {
        if (obj instanceof Set) {
            Set casted = (Set) obj;

            return casted.size() == size()
                    && casted.intersect(this).size() == size();
        }

        return false;
    }

    @Override
    public int hashCode() {
        return foldLeft(21, (left, right) -> left + (right != null ? right.hashCode() : 0));
    }
}