/*
* =============================================================================
*
* Copyright (c) 2010, The JAVATUPLES team (http://www.javatuples.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* =============================================================================
*/
package de.sanandrew.core.manpack.util.javatuples;
import de.sanandrew.core.manpack.init.ManPackLoadingPlugin;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.handler.codec.serialization.ObjectDecoderInputStream;
import io.netty.handler.codec.serialization.ObjectEncoderOutputStream;
import org.apache.logging.log4j.Level;
import java.io.IOException;
import java.io.Serializable;
import java.util.*;
/**
* <p>
* Abstract base class for all tuple classes.
* </p>
*
* @author Daniel Fernández
* @since 1.0
*/
public abstract class Tuple
implements Iterable<Object>, Serializable, Comparable<Tuple>
{
private static final long serialVersionUID = 5431085632328343101L;
private final Object[] valueArray;
private final List<Object> valueList;
protected Tuple(final Object... values) {
this.valueArray = values;
this.valueList = Arrays.asList(values);
}
/**
* <p>
* Return the size of the tuple.
* </p>
*
* @return the size of the tuple.
*/
public abstract int getSize();
/**
* <p>
* Get the value at a specific position in the tuple. This method
* has to return object, so using it you will lose the type-safety you
* get with the <tt>getValueX()</tt> methods.
* </p>
*
* @param pos the position of the value to be retrieved.
* @return the value
*/
public final Object getValue(final int pos) {
if( pos >= getSize() ) {
throw new IllegalArgumentException(String.format("Cannot retrieve value index %1$d in %2$s. Indices of %2$s range from 0 to %3$d",
pos, this.getClass().getSimpleName(), (getSize() - 1)
)
);
}
return this.valueArray[pos];
}
@Override
public final Iterator<Object> iterator() {
return this.valueList.iterator();
}
@Override
public final String toString() {
return this.valueList.toString();
}
public final boolean contains(final Object value) {
for( final Object val : this.valueList ) {
if( val == null ) {
if( value == null ) {
return true;
}
} else {
if( val.equals(value) ) {
return true;
}
}
}
return false;
}
public final boolean containsAll(final Collection<?> collection) {
for( final Object value : collection ) {
if( !this.contains(value) ) {
return false;
}
}
return true;
}
public final boolean containsAll(final Object... values) {
if( values == null ) {
throw new IllegalArgumentException("containsAll needs at least 1 parameter or array cannot be null.");
}
for( final Object value : values ) {
if( !contains(value) ) {
return false;
}
}
return true;
}
public final int indexOf(final Object value) {
int i = 0;
for( final Object val : this.valueList ) {
if( val == null ) {
if( value == null ) {
return i;
}
} else {
if( val.equals(value) ) {
return i;
}
}
i++;
}
return -1;
}
public final int lastIndexOf(final Object value) {
for( int i = getSize() - 1; i >= 0; i-- ) {
final Object val = this.valueList.get(i);
if( val == null ) {
if( value == null ) {
return i;
}
} else {
if( val.equals(value) ) {
return i;
}
}
}
return -1;
}
public final List<Object> toList() {
return Collections.unmodifiableList(new ArrayList<>(this.valueList));
}
public final Object[] toArray() {
return this.valueArray.clone();
}
@Override
public final int hashCode() {
return 31 + this.valueList.hashCode();
}
@Override
public final boolean equals(final Object obj) {
if( this == obj ) {
return true;
}
if( obj == null ) {
return false;
}
if( getClass() != obj.getClass() ) {
return false;
}
final Tuple other = (Tuple) obj;
return this.valueList.equals(other.valueList);
}
@SuppressWarnings( { "rawtypes", "unchecked" } )
@Override
public int compareTo(final Tuple o) {
final int tLen = this.valueArray.length;
final Object[] oValues = o.valueArray;
final int oLen = oValues.length;
for( int i = 0; i < tLen && i < oLen; i++ ) {
final Comparable tElement = (Comparable) this.valueArray[i];
final Comparable oElement = (Comparable) oValues[i];
final int comparison = tElement.compareTo(oElement);
if( comparison != 0 ) {
return comparison;
}
}
return (Integer.valueOf(tLen)).compareTo(oLen);
}
public static Tuple readFromByteBufStream(ByteBufInputStream stream) {
try( ObjectDecoderInputStream odis = new ObjectDecoderInputStream(stream) ) {
return (Tuple) odis.readObject();
} catch( IOException | ClassNotFoundException ex ) {
ManPackLoadingPlugin.MOD_LOG.log(Level.ERROR, "Cannot deserialize Tuple!", ex);
}
return null;
}
public static void writeToByteBufStream(Tuple tuple, ByteBufOutputStream stream) {
try( ObjectEncoderOutputStream oeos = new ObjectEncoderOutputStream(stream) ) {
oeos.writeObject(tuple);
} catch( IOException ex ) {
ManPackLoadingPlugin.MOD_LOG.log(Level.ERROR, "Cannot serialize Tuple!", ex);
}
}
public static Tuple from(Object... values) {
switch( values.length ) {
case 1:
return Unit.fromArray(values);
case 2:
return Pair.fromArray(values);
case 3:
return Triplet.fromArray(values);
case 4:
return Quartet.fromArray(values);
case 5:
return Quintet.fromArray(values);
case 6:
return Sextet.fromArray(values);
case 7:
return Septet.fromArray(values);
case 8:
return Octet.fromArray(values);
case 9:
return Ennead.fromArray(values);
case 10:
return Decade.fromArray(values);
default:
throw new RuntimeException(String.format("Cannot create Tuple with size %d!", values.length));
}
}
}