Created a pair class to assist in upcoming features

This commit is contained in:
Obique
2017-11-05 17:26:51 -06:00
parent ca38185da4
commit 7f9017395f

View File

@@ -0,0 +1,192 @@
package com.projectswg.common.data;
import java.util.Objects;
import com.projectswg.common.debug.Assert;
import com.projectswg.common.debug.Log;
import com.projectswg.common.encoding.CachedEncode;
import com.projectswg.common.encoding.Encodable;
import com.projectswg.common.encoding.Encoder;
import com.projectswg.common.encoding.StringType;
import com.projectswg.common.network.NetBuffer;
import com.projectswg.common.network.NetBufferStream;
import com.projectswg.common.persistable.Persistable;
public class Pair<T, S> implements Encodable, Persistable {
private final StringType leftType;
private final StringType rightType;
private final Class<T> leftClass;
private final Class<S> rightClass;
private final CachedEncode cache;
private T left;
private S right;
private Pair(T left, S right, Class<T> leftClass, Class<S> rightClass, StringType leftType, StringType rightType) {
// Final checks - has to be either one or the other. These ensure other assumptions in the class will succeed
Assert.test((leftType == null && !(left instanceof String) && !leftClass.equals(String.class)) ^ (leftType != null && (left instanceof String) && leftClass.equals(String.class)));
Assert.test((rightType == null && !(right instanceof String) && !rightClass.equals(String.class)) ^ (rightType != null && (right instanceof String) && rightClass.equals(String.class)));
this.leftClass = leftClass;
this.rightClass = rightClass;
this.leftType = leftType;
this.rightType = rightType;
this.cache = new CachedEncode(this::encodeImpl);
setLeft(left);
setRight(right);
}
public T getLeft() {
return left;
}
public S getRight() {
return right;
}
public void setLeft(T val1) {
this.left = val1;
cache.clearCached();
}
public void setRight(S val2) {
this.right = val2;
cache.clearCached();
}
@Override
public void decode(NetBuffer data) {
setLeft(smartDecode(data, leftClass, leftType));
setRight(smartDecode(data, rightClass, rightType));
}
@Override
public byte[] encode() {
Objects.requireNonNull(left, "left is null in encode()");
Objects.requireNonNull(right, "right is null in encode()");
return cache.encode();
}
@Override
public int getLength() {
return cache.encode().length;
}
@Override
public void save(NetBufferStream stream) {
smartSave(stream, left, leftType);
smartSave(stream, right, rightType);
}
@Override
public void read(NetBufferStream stream) {
smartRead(stream, left, leftClass, leftType);
smartRead(stream, right, rightClass, rightType);
}
private byte [] encodeImpl() {
if (left == null || right == null)
return new byte[0];
byte [] left = smartEncode(this.left, leftType);
byte [] right = smartEncode(this.right, rightType);
byte [] combined = new byte[left.length + right.length];
System.arraycopy(left, 0, combined, 0, left.length);
System.arraycopy(right, 0, combined, left.length, right.length);
return combined;
}
@SuppressWarnings("unchecked") // should succeed based on checks in constructor
private static <U> U smartDecode(NetBuffer data, Class<U> klass, StringType strType) {
if (strType != null)
return (U) data.getString(strType);
return (U) data.getGeneric(klass);
}
private static byte [] smartEncode(Object obj, StringType strType) {
if (strType != null)
return Encoder.encode(obj, strType);
return Encoder.encode(obj);
}
@SuppressWarnings("unchecked") // should succeed based on checks in constructor
private static <U> U smartRead(NetBufferStream data, U obj, Class<U> klass, StringType strType) {
if (strType != null)
return (U) data.getString(strType);
// Try making our own persistable
if (obj == null && Persistable.class.isAssignableFrom(klass)) {
try {
obj = klass.newInstance();
} catch (Exception e) {
Log.e(e);
}
}
// If we succeeded, read it - if not, try grabbing some generic
if (obj instanceof Persistable) {
((Persistable) obj).read(data);
} else {
obj = (U) data.getGeneric(klass);
}
return obj;
}
private static void smartSave(NetBufferStream data, Object obj, StringType strType) {
if (strType != null) {
data.addString((String) obj, strType);
} else if (obj instanceof Persistable) {
((Persistable) obj).save(data);
} else {
data.write(Encoder.encode(obj));
}
}
@SuppressWarnings("unchecked") // it's pretty obvious T.getClass() should be Class<T>
public static <T, S> Pair<T, S> createPair(T left, S right) {
Objects.requireNonNull(left, "Left cannot be null in this method! Instead call createPair(left, right, leftClass, rightClass)");
Objects.requireNonNull(right, "Right cannot be null in this method! Instead call createPair(left, right, leftClass, rightClass)");
return createPair(left, right, (Class<T>) left.getClass(), (Class<S>) right.getClass());
}
public static <T, S> Pair<T, S> createPair(T left, S right, Class<T> leftClass, Class<S> rightClass) {
Assert.test(!(left instanceof String));
Assert.test(!(right instanceof String));
return new Pair<>(left, right, leftClass, rightClass, null, null);
}
@SuppressWarnings("unchecked") // it's pretty obvious T.getClass() should be Class<T>
public static <T> Pair<String, T> createPair(String left, T right, StringType type) {
Objects.requireNonNull(right, "Right cannot be null in this method! Instead call createPair(left, right, type, rightClass)");
return createPair(left, right, type, (Class<T>) right.getClass());
}
public static <T> Pair<String, T> createPair(String left, T right, StringType type, Class<T> rightClass) {
Assert.test(!(right instanceof String));
return new Pair<>(left, right, String.class, rightClass, type, null);
}
@SuppressWarnings("unchecked") // it's pretty obvious T.getClass() should be Class<T>
public static <T> Pair<T, String> createPair(T left, String right, StringType type) {
Objects.requireNonNull(left, "Left cannot be null in this method! Instead call createPair(left, right, leftClass, type)");
return createPair(left, right, (Class<T>) left.getClass(), type);
}
public static <T> Pair<T, String> createPair(T left, String right, Class<T> leftClass, StringType type) {
Assert.test(!(left instanceof String));
return new Pair<>(left, right, leftClass, String.class, null, type);
}
/**
* Creates a pair with two strings
* @param left the left string, can be null
* @param right the right string, can be null
* @param leftType the type of the left string: ASCII/UNICODE
* @param rightType the type of the right string: ASCII/UNICODE
* @return the corresponding pair
*/
public static Pair<String, String> createPair(String left, String right, StringType leftType, StringType rightType) {
return new Pair<>(left, right, String.class, String.class, leftType, rightType);
}
}