package org.eclipse.collections.impl.block.factory;
import java.util.Objects;
import org.eclipse.collections.api.block.HashingStrategy;
import org.eclipse.collections.api.block.function.Function;
import org.eclipse.collections.api.block.function.primitive.BooleanFunction;
import org.eclipse.collections.api.block.function.primitive.ByteFunction;
import org.eclipse.collections.api.block.function.primitive.CharFunction;
import org.eclipse.collections.api.block.function.primitive.DoubleFunction;
import org.eclipse.collections.api.block.function.primitive.FloatFunction;
import org.eclipse.collections.api.block.function.primitive.IntFunction;
import org.eclipse.collections.api.block.function.primitive.LongFunction;
import org.eclipse.collections.api.block.function.primitive.ShortFunction;
public final class HashingStrategies
{
private static final HashingStrategy<Object> DEFAULT_HASHING_STRATEGY = new DefaultStrategy();
private static final HashingStrategy<Object> IDENTITY_HASHING_STRATEGY = new IdentityHashingStrategy();
private HashingStrategies()
{
throw new AssertionError("Suppress default constructor for noninstantiability");
}
public static <T> HashingStrategy<T> defaultStrategy()
{
return (HashingStrategy<T>) DEFAULT_HASHING_STRATEGY;
}
public static <T> HashingStrategy<T> nullSafeHashingStrategy(HashingStrategy<T> nonNullSafeStrategy)
{
return new NullSafeHashingStrategy<>(nonNullSafeStrategy);
}
public static <T, V> HashingStrategy<T> nullSafeFromFunction(Function<? super T, ? extends V> function)
{
return new NullSafeFunctionHashingStrategy<>(function);
}
public static <T, V> HashingStrategy<T> fromFunction(Function<? super T, ? extends V> function)
{
return new FunctionHashingStrategy<>(function);
}
public static HashingStrategy<Object> identityStrategy()
{
return IDENTITY_HASHING_STRATEGY;
}
public static <T> HashingStrategy<T> chain(HashingStrategy<T>... hashingStrategies)
{
if (hashingStrategies.length == 0)
{
throw new IllegalArgumentException("Nothing to chain");
}
return new ChainedHashingStrategy<>(hashingStrategies);
}
public static <T, V1, V2> HashingStrategy<T> fromFunctions(Function<? super T, ? extends V1> one, Function<? super T, ? extends V2> two)
{
return HashingStrategies.chain(
HashingStrategies.fromFunction(one),
HashingStrategies.fromFunction(two));
}
public static <T, V1, V2, V3> HashingStrategy<T> fromFunctions(Function<? super T, ? extends V1> one, Function<? super T, ? extends V2> two, Function<? super T, ? extends V3> three)
{
return HashingStrategies.chain(
HashingStrategies.fromFunction(one),
HashingStrategies.fromFunction(two),
HashingStrategies.fromFunction(three));
}
public static <T> HashingStrategy<T> fromBooleanFunction(BooleanFunction<? super T> function)
{
return new BooleanFunctionHashingStrategy<>(function);
}
public static <T> HashingStrategy<T> fromByteFunction(ByteFunction<? super T> function)
{
return new ByteFunctionHashingStrategy<>(function);
}
public static <T> HashingStrategy<T> fromCharFunction(CharFunction<? super T> function)
{
return new CharFunctionHashingStrategy<>(function);
}
public static <T> HashingStrategy<T> fromDoubleFunction(DoubleFunction<? super T> function)
{
return new DoubleFunctionHashingStrategy<>(function);
}
public static <T> HashingStrategy<T> fromFloatFunction(FloatFunction<? super T> function)
{
return new FloatFunctionHashingStrategy<>(function);
}
public static <T> HashingStrategy<T> fromIntFunction(IntFunction<? super T> function)
{
return new IntFunctionHashingStrategy<>(function);
}
public static <T> HashingStrategy<T> fromLongFunction(LongFunction<? super T> function)
{
return new LongFunctionHashingStrategy<>(function);
}
public static <T> HashingStrategy<T> fromShortFunction(ShortFunction<? super T> function)
{
return new ShortFunctionHashingStrategy<>(function);
}
private static class DefaultStrategy implements HashingStrategy<Object>
{
private static final long serialVersionUID = 1L;
@Override
public int computeHashCode(Object object)
{
return object.hashCode();
}
@Override
public boolean equals(Object object1, Object object2)
{
return object1.equals(object2);
}
}
private static final class NullSafeHashingStrategy<T> implements HashingStrategy<T>
{
private static final long serialVersionUID = 1L;
private final HashingStrategy<T> nonNullSafeStrategy;
private NullSafeHashingStrategy(HashingStrategy<T> nonNullSafeStrategy)
{
this.nonNullSafeStrategy = nonNullSafeStrategy;
}
@Override
public int computeHashCode(T object)
{
return object == null ? 0 : this.nonNullSafeStrategy.computeHashCode(object);
}
@Override
public boolean equals(T object1, T object2)
{
return object1 == null || object2 == null
? object1 == object2
: this.nonNullSafeStrategy.equals(object1, object2);
}
}
private static final class NullSafeFunctionHashingStrategy<T, V> implements HashingStrategy<T>
{
private static final long serialVersionUID = 1L;
private final Function<? super T, ? extends V> function;
private NullSafeFunctionHashingStrategy(Function<? super T, ? extends V> function)
{
this.function = function;
}
@Override
public int computeHashCode(T object)
{
return Objects.hashCode(this.function.valueOf(object));
}
@Override
public boolean equals(T object1, T object2)
{
return Objects.equals(
this.function.valueOf(object1),
this.function.valueOf(object2));
}
}
private static final class FunctionHashingStrategy<T, V> implements HashingStrategy<T>
{
private static final long serialVersionUID = 1L;
private final Function<? super T, ? extends V> function;
private FunctionHashingStrategy(Function<? super T, ? extends V> function)
{
this.function = function;
}
@Override
public int computeHashCode(T object)
{
return this.function.valueOf(object).hashCode();
}
@Override
public boolean equals(T object1, T object2)
{
return this.function.valueOf(object1).equals(this.function.valueOf(object2));
}
}
private static final class BooleanFunctionHashingStrategy<T> implements HashingStrategy<T>
{
private static final long serialVersionUID = 1L;
private final BooleanFunction<? super T> function;
private BooleanFunctionHashingStrategy(BooleanFunction<? super T> function)
{
this.function = function;
}
@Override
public int computeHashCode(T object)
{
return this.function.booleanValueOf(object) ? Boolean.TRUE.hashCode() : Boolean.FALSE.hashCode();
}
@Override
public boolean equals(T object1, T object2)
{
return this.function.booleanValueOf(object1) == this.function.booleanValueOf(object2);
}
}
private static final class ByteFunctionHashingStrategy<T> implements HashingStrategy<T>
{
private static final long serialVersionUID = 1L;
private final ByteFunction<? super T> function;
private ByteFunctionHashingStrategy(ByteFunction<? super T> function)
{
this.function = function;
}
@Override
public int computeHashCode(T object)
{
return this.function.byteValueOf(object);
}
@Override
public boolean equals(T object1, T object2)
{
return this.function.byteValueOf(object1) == this.function.byteValueOf(object2);
}
}
private static final class CharFunctionHashingStrategy<T> implements HashingStrategy<T>
{
private static final long serialVersionUID = 1L;
private final CharFunction<? super T> function;
private CharFunctionHashingStrategy(CharFunction<? super T> function)
{
this.function = function;
}
@Override
public int computeHashCode(T object)
{
return this.function.charValueOf(object);
}
@Override
public boolean equals(T object1, T object2)
{
return this.function.charValueOf(object1) == this.function.charValueOf(object2);
}
}
private static final class DoubleFunctionHashingStrategy<T> implements HashingStrategy<T>
{
private static final long serialVersionUID = 1L;
private final DoubleFunction<? super T> function;
private DoubleFunctionHashingStrategy(DoubleFunction<? super T> function)
{
this.function = function;
}
@Override
public int computeHashCode(T object)
{
return Long.hashCode(Double.doubleToLongBits(this.function.doubleValueOf(object)));
}
@Override
public boolean equals(T object1, T object2)
{
return Double.compare(this.function.doubleValueOf(object1), this.function.doubleValueOf(object2)) == 0;
}
}
private static final class FloatFunctionHashingStrategy<T> implements HashingStrategy<T>
{
private static final long serialVersionUID = 1L;
private final FloatFunction<? super T> function;
private FloatFunctionHashingStrategy(FloatFunction<? super T> function)
{
this.function = function;
}
@Override
public int computeHashCode(T object)
{
return Float.floatToIntBits(this.function.floatValueOf(object));
}
@Override
public boolean equals(T object1, T object2)
{
return Float.compare(this.function.floatValueOf(object1), this.function.floatValueOf(object2)) == 0;
}
}
private static final class IntFunctionHashingStrategy<T> implements HashingStrategy<T>
{
private static final long serialVersionUID = 1L;
private final IntFunction<? super T> function;
private IntFunctionHashingStrategy(IntFunction<? super T> function)
{
this.function = function;
}
@Override
public int computeHashCode(T object)
{
return this.function.intValueOf(object);
}
@Override
public boolean equals(T object1, T object2)
{
return this.function.intValueOf(object1) == this.function.intValueOf(object2);
}
}
private static final class LongFunctionHashingStrategy<T> implements HashingStrategy<T>
{
private static final long serialVersionUID = 1L;
private final LongFunction<? super T> function;
private LongFunctionHashingStrategy(LongFunction<? super T> function)
{
this.function = function;
}
@Override
public int computeHashCode(T object)
{
return Long.hashCode(this.function.longValueOf(object));
}
@Override
public boolean equals(T object1, T object2)
{
return this.function.longValueOf(object1) == this.function.longValueOf(object2);
}
}
private static final class ShortFunctionHashingStrategy<T> implements HashingStrategy<T>
{
private static final long serialVersionUID = 1L;
private final ShortFunction<? super T> function;
private ShortFunctionHashingStrategy(ShortFunction<? super T> function)
{
this.function = function;
}
@Override
public int computeHashCode(T object)
{
return this.function.shortValueOf(object);
}
@Override
public boolean equals(T object1, T object2)
{
return this.function.shortValueOf(object1) == this.function.shortValueOf(object2);
}
}
private static final class IdentityHashingStrategy implements HashingStrategy<Object>
{
private static final long serialVersionUID = 1L;
@Override
public int computeHashCode(Object object)
{
return System.identityHashCode(object);
}
@Override
public boolean equals(Object object1, Object object2)
{
return object1 == object2;
}
}
private static final class ChainedHashingStrategy<T> implements HashingStrategy<T>
{
private static final long serialVersionUID = 1L;
private final HashingStrategy<T>[] hashingStrategies;
private ChainedHashingStrategy(HashingStrategy<T>... hashingStrategies)
{
this.hashingStrategies = hashingStrategies;
}
@Override
public int computeHashCode(T object)
{
int hashCode = this.hashingStrategies[0].computeHashCode(object);
for (int i = 1; i < this.hashingStrategies.length; i++)
{
hashCode = hashCode * 31 + this.hashingStrategies[i].computeHashCode(object);
}
return hashCode;
}
@Override
public boolean equals(T object1, T object2)
{
for (HashingStrategy<T> hashingStrategy : this.hashingStrategies)
{
if (!hashingStrategy.equals(object1, object2))
{
return false;
}
}
return true;
}
}
}