TupleImpl.java
package sprouts.impl;
import org.jspecify.annotations.Nullable;
import sprouts.SequenceChange;
import sprouts.Tuple;
import java.util.*;
import static sprouts.impl.ArrayUtil.*;
public final class TupleImpl<T extends @Nullable Object> implements Tuple<T>, SequenceDiffOwner
{
private final boolean _allowsNull;
private final Class<T> _type;
private final Object _data;
private final SequenceDiff _diffToPrevious;
@SuppressWarnings("NullAway")
public TupleImpl(
boolean allowsNull,
Class<T> type,
List<T> items
) {
this(allowsNull, type, _createArrayFromList(type, allowsNull, items), null);
}
@SuppressWarnings("NullAway")
public TupleImpl(
boolean allowsNull,
Class<T> type,
@Nullable Object items,
@Nullable SequenceDiff diffToPrevious
) {
Objects.requireNonNull(type);
_allowsNull = allowsNull;
_type = type;
_data = ( items == null ? _createArray(type, allowsNull, 0) : _tryFlatten(items,type,allowsNull) );
_diffToPrevious = ( diffToPrevious == null ? SequenceDiff.initial() : diffToPrevious );
if ( !allowsNull ) {
_each(_data, type, item -> {
if ( item == null )
throw new NullPointerException();
});
}
}
@Override
public Class<T> type() {
return _type;
}
@Override
public int size() {
return _length(_data);
}
@Override
@SuppressWarnings("NullAway")
public T get( int index ) {
T item = _getAt(index, _data, _type);
if ( !_allowsNull && item == null )
throw new NullPointerException();
return item;
}
@Override
public boolean allowsNull() {
return _allowsNull;
}
@Override
public Tuple<T> slice( int from, int to ) {
if ( from < 0 || to > _length(_data) )
throw new IndexOutOfBoundsException();
if ( from > to )
throw new IllegalArgumentException();
int newSize = (to - from);
if ( newSize == this.size() )
return this;
if ( newSize == 0 ) {
SequenceDiff diff = SequenceDiff.of(this, SequenceChange.RETAIN, -1, 0);
Object newItems = _createArray(_type, _allowsNull, 0);
return new TupleImpl<>(_allowsNull, _type, newItems, diff);
}
Object newItems = _createArray(_type, _allowsNull, newSize);
System.arraycopy(_data, from, newItems, 0, newSize);
SequenceDiff diff = SequenceDiff.of(this, SequenceChange.RETAIN, from, newSize);
return new TupleImpl<>(_allowsNull, _type, newItems, diff);
}
@Override
public Tuple<T> removeRange( int from, int to ) {
if ( from < 0 || to > _length(_data) )
throw new IndexOutOfBoundsException();
if ( from > to )
throw new IllegalArgumentException();
int numberOfItemsToRemove = to - from;
if ( numberOfItemsToRemove == 0 )
return this;
if ( numberOfItemsToRemove == this.size() ) {
SequenceDiff diff = SequenceDiff.of(this, SequenceChange.REMOVE, 0, this.size());
Object newItems = _createArray(_type, _allowsNull, 0);
return new TupleImpl<>(_allowsNull, _type, newItems, diff);
}
Object newItems = _withRemoveRange(from, to, _data, _type, _allowsNull);
SequenceDiff diff = SequenceDiff.of(this, SequenceChange.REMOVE, from, numberOfItemsToRemove);
return new TupleImpl<>(_allowsNull, _type, newItems, diff);
}
@Override
public Tuple<T> removeAll( Tuple<T> properties ) {
if ( properties.isEmpty() )
return this;
int[] indicesOfThingsToKeep = new int[this.size()];
int newSize = 0;
for ( int i = 0; i < this.size(); i++ ) {
int index = properties.firstIndexOf( _getAt(i, _data, _type) );
if ( index == -1 ) {
indicesOfThingsToKeep[newSize] = i;
newSize++;
} else {
indicesOfThingsToKeep[newSize] = -1;
}
}
if ( newSize == this.size() )
return this;
Object newItems = _createArray(_type, _allowsNull, newSize);
for ( int i = 0; i < newSize; i++ ) {
int index = indicesOfThingsToKeep[i];
if ( index != -1 )
_setAt(i, _getAt(index, _data, _type), newItems);
}
SequenceDiff diff = SequenceDiff.of(this, SequenceChange.REMOVE, -1, this.size() - newSize);
return new TupleImpl<>(_allowsNull, _type, newItems, diff);
}
@Override
public Tuple<T> addAt( int index, T item ) {
if ( !this.allowsNull() && item == null )
throw new NullPointerException();
if ( index < 0 || index > _length(_data) )
throw new IndexOutOfBoundsException();
Object newItems = _withAddAt(index, item, _data, _type, _allowsNull);
SequenceDiff diff = SequenceDiff.of(this, SequenceChange.ADD, index, 1);
return new TupleImpl<>(_allowsNull, _type, newItems, diff);
}
@Override
public Tuple<T> setAt( int index, T item ) {
if ( index < 0 || index >= _length(_data) )
throw new IndexOutOfBoundsException();
if ( Objects.equals(item, get(index)) )
return this;
Object newItems = _withSetAt(index, item, _data, _type, _allowsNull);
SequenceDiff diff = SequenceDiff.of(this, SequenceChange.SET, index, 1);
return new TupleImpl<>(_allowsNull, _type, newItems, diff);
}
@Override
public Tuple<T> addAllAt( int index, Tuple<T> tuple ) {
Objects.requireNonNull(tuple);
if ( tuple.isEmpty() )
return this; // nothing to do
if ( !this.allowsNull() && tuple.allowsNull() )
throw new NullPointerException();
if ( index < 0 || index > _length(_data) )
throw new IndexOutOfBoundsException();
Object newItems = _withAddAllAt(index, tuple, _data, _type, _allowsNull);
SequenceDiff diff = SequenceDiff.of(this, SequenceChange.ADD, index, tuple.size());
return new TupleImpl<>(_allowsNull, _type, newItems, diff);
}
@Override
public Tuple<T> setAllAt( int index, Tuple<T> tuple ) {
if ( !this.allowsNull() && tuple.allowsNull() )
throw new NullPointerException();
if ( index < 0 || index + tuple.size() > _length(_data) )
throw new IndexOutOfBoundsException();
if ( tuple.isEmpty() )
return this; // nothing to do
boolean isAlreadyTheSame = true;
for (int i = 0; i < tuple.size() && isAlreadyTheSame; i++ ) {
if ( !Objects.equals(this.get(i+index), tuple.get(i)) )
isAlreadyTheSame = false;
}
if ( isAlreadyTheSame )
return this;
Object newItems = _clone(_data, _type, _allowsNull);
for (int i = 0; i < tuple.size(); i++ )
_setAt(index + i, tuple.get(i), newItems);
SequenceDiff diff = SequenceDiff.of(this, SequenceChange.SET, index, tuple.size());
return new TupleImpl<>(_allowsNull, _type, newItems, diff);
}
@Override
public Tuple<T> retainAll(Tuple<T> tuple) {
if ( tuple.isEmpty() ) {
SequenceDiff diff = SequenceDiff.of(this, SequenceChange.RETAIN, -1, 0);
Object newItems = _createArray(_type, _allowsNull, 0);
return new TupleImpl<>(_allowsNull, _type, newItems, diff);
}
int[] indicesOfThingsToKeep = new int[this.size()];
int newSize = 0;
int singleSequenceIndex = size() > 0 ? -2 : -1;
int retainSequenceSize = 0;
for ( int i = 0; i < this.size(); i++ ) {
int index = tuple.firstIndexOf( _getAt(i, _data, _type) );
if ( index != -1 ) {
indicesOfThingsToKeep[newSize] = i;
newSize++;
if ( singleSequenceIndex != -1 ) {
if ( singleSequenceIndex == -2 )
singleSequenceIndex = i;
else if ( i > singleSequenceIndex + retainSequenceSize )
singleSequenceIndex = -1;
}
if ( singleSequenceIndex >= 0 )
retainSequenceSize++;
} else {
indicesOfThingsToKeep[newSize] = -1;
}
}
if ( newSize == this.size() )
return this;
Object newItems = _createArray(_type, _allowsNull, newSize);
for ( int i = 0; i < newSize; i++ ) {
int index = indicesOfThingsToKeep[i];
if ( index != -1 )
_setAt(i, _getAt(index, _data, _type), newItems);
}
SequenceDiff diff = SequenceDiff.of(this, SequenceChange.RETAIN, singleSequenceIndex, newSize);
return new TupleImpl<>(_allowsNull, _type, newItems, diff);
}
@Override
public Tuple<T> clear() {
SequenceDiff diff = SequenceDiff.of(this, SequenceChange.CLEAR, 0, _length(_data));
return new TupleImpl<>(_allowsNull, _type, null, diff);
}
@Override
public Tuple<T> sort(Comparator<T> comparator ) {
Object newItems = _clone(_data, _type, _allowsNull);
_sort(newItems, comparator);
SequenceDiff diff = SequenceDiff.of(this, SequenceChange.SORT, -1, _length(_data));
return new TupleImpl<>(_allowsNull, _type, newItems, diff);
}
@Override
public Tuple<T> makeDistinct() {
int newSize = 0;
Object newItems = _createArray(_type, _allowsNull, _length(_data));
for ( int i = 0; i < _length(_data); i++ ) {
T item = _getAt(i, _data, _type);
if ( firstIndexOf(item) == newSize ) {
_setAt(newSize, item, newItems);
newSize++;
}
}
if ( newSize == _length(_data) )
return this;
Object distinctItems = _createArray(_type, _allowsNull, newSize);
System.arraycopy(newItems, 0, distinctItems, 0, newSize);
SequenceDiff diff = SequenceDiff.of(this, SequenceChange.DISTINCT, -1, _length(_data) - newSize);
return new TupleImpl<>(_allowsNull, _type, distinctItems, diff);
}
@Override
public Tuple<T> reversed() {
if ( _length(_data) < 2 )
return this;
Object newItems = _withReversed(_data, _type, _allowsNull);
SequenceDiff diff = SequenceDiff.of(this, SequenceChange.REVERSE, -1, _length(_data));
return new TupleImpl<>(_allowsNull, _type, newItems, diff);
}
@Override
@SuppressWarnings("NullAway")
public Iterator<T> iterator() {
return new Iterator<T>() {
private int _index = 0;
@Override
public boolean hasNext() {
return _index < _length(_data);
}
@Override
public T next() {
return _getAt(_index++, _data, _type);
}
};
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Tuple<");
sb.append(_type.getSimpleName());
if ( allowsNull() )
sb.append("?");
sb.append(">[");
for ( int i = 0; i < _length(_data); i++ ) {
sb.append(_getAt(i, _data, _type));
if ( i < _length(_data) - 1 )
sb.append(", ");
}
sb.append("]");
return sb.toString();
}
@Override
public boolean equals( Object obj ) {
if ( obj == this )
return true;
if ( !(obj instanceof Tuple) )
return false;
Tuple<?> other = (Tuple<?>) obj;
if ( other.allowsNull() != this.allowsNull() )
return false;
if ( other.size() != this.size() )
return false;
if ( !other.type().equals(_type) )
return false;
for ( int i = 0; i < this.size(); i++ ) {
if ( !Objects.equals( this.get(i), other.get(i) ) )
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = _type.hashCode() ^ _length(_data);
for ( int i = 0; i < _length(_data); i++ ) {
T item = _getAt(i, _data, _type);
hash = 31 * hash + (item == null ? 0 : item.hashCode());
}
return hash ^ (_allowsNull ? 1 : 0);
}
@Override
public Optional<SequenceDiff> differenceFromPrevious() {
return Optional.ofNullable(_diffToPrevious);
}
}