package org.tmatesoft.svn.core.internal.wc17.db;
import java.util.Arrays;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class Structure<T extends Enum<T>> {
public interface TypeSafety {
Class<?> getType();
}
private static final StructuresPool globalPool = new StructuresPool();
private static final Object LONG_MARKER = Long.TYPE;
private static final Object BOOLEAN_MARKER = Boolean.TYPE;
@SuppressWarnings("unchecked")
public static <X extends Structure<T>, T extends Enum<T>> X obtain(Class<T> e, Enum<T>... fields) {
assert e != null && e.isEnum();
return (X) globalPool.obtain(e, fields);
}
@SuppressWarnings("unchecked")
public static <X extends Structure<T>, T extends Enum<T>> X obtain(Class<T> e) {
assert e != null && e.isEnum();
return (X) globalPool.obtain(e);
}
private static void release(Structure<?> e) {
if (e != null) {
globalPool.release(e);
}
}
private Class<?> enumClass;
private long requestedFields;
private Object[] nonPrimitiveValues;
private long[] longValues;
private T[] copySource;
private Structure(Class<T> enumClass, Enum<T>... fields) {
init(enumClass, fields);
}
public long lng(T e) {
assert e.getClass() == enumClass;
assertSafeType(e, Long.TYPE);
if (nonPrimitiveValues[e.ordinal()] == LONG_MARKER) {
return longValues[e.ordinal()];
}
assert false;
return 0;
}
public String text(T e) {
assert e.getClass() == enumClass;
assertSafeType(e, String.class);
return (String) nonPrimitiveValues[e.ordinal()];
}
public boolean is(T e) {
assert e.getClass() == enumClass;
assertSafeType(e, Boolean.TYPE);
if (nonPrimitiveValues[e.ordinal()] == BOOLEAN_MARKER) {
return longValues[e.ordinal()] != 0;
}
assert false;
return false;
}
public boolean hasValue(Enum<?> e) {
assert e.getClass() == enumClass;
return nonPrimitiveValues[e.ordinal()] != null;
}
@SuppressWarnings("unchecked")
public <X> X get(T e) {
assert e.getClass() == enumClass;
Class<?> expectedType = null;
if (e instanceof TypeSafety) {
expectedType = ((TypeSafety) e).getType();
}
Object value = nonPrimitiveValues[e.ordinal()];
if (value == null) {
return null;
}
assert value != LONG_MARKER && value != BOOLEAN_MARKER;
if (expectedType != null) {
assert expectedType.isAssignableFrom(value.getClass());
}
return (X) value;
}
public void set(T x, Object v) {
if (v == null) {
unset(x);
return;
}
if (x instanceof TypeSafety) {
assert v == LONG_MARKER || v == BOOLEAN_MARKER || v == null || ((TypeSafety) x).getType().isAssignableFrom(v.getClass());
}
nonPrimitiveValues[x.ordinal()] = v;
}
public void unset(T x) {
if ((requestedFields & (1 << x.ordinal())) == 0) {
return;
}
nonPrimitiveValues[x.ordinal()] = null;
longValues[x.ordinal()] = 0;
}
public void set(T x, long v) {
if (x instanceof TypeSafety) {
assert ((TypeSafety) x).getType() == Long.TYPE;
}
longValues[x.ordinal()] = v;
set(x, LONG_MARKER);
}
public void set(T x, boolean v) {
if (x instanceof TypeSafety) {
assert ((TypeSafety) x).getType() == Boolean.TYPE;
}
longValues[x.ordinal()] = v ? 1 : 0;
set(x, BOOLEAN_MARKER);
}
public boolean hasField(Enum<T> field) {
return (requestedFields & (1 << field.ordinal())) != 0;
}
public void clear() {
copySource = null;
Arrays.fill(nonPrimitiveValues, null);
Arrays.fill(longValues, 0);
}
public void release() {
release(this);
}
public Structure<T> from(T... fields) {
assert copySource == null;
assert fields != null;
copySource = fields;
return this;
}
public <X extends Enum<X>> void into(Structure<X> target, X... fields) {
assert copySource != null;
assert fields != null;
assert fields.length == copySource.length;
assert target != null;
try {
for (int i = 0; i < copySource.length; i++) {
int valueIndex = copySource[i].ordinal();
Object v = nonPrimitiveValues[copySource[i].ordinal()];
if (v == BOOLEAN_MARKER) {
target.set(fields[i], longValues[valueIndex] != 0);
} else if (v == LONG_MARKER) {
target.set(fields[i], longValues[valueIndex]);
} else if (v == null) {
target.unset(fields[i]);
} else {
target.set(fields[i], v);
}
}
} finally {
copySource = null;
}
}
public int hashCode() {
int code = enumClass.hashCode();
for (int i = 0; i < nonPrimitiveValues.length; i++) {
if (nonPrimitiveValues != null && nonPrimitiveValues[i] != null) {
code += 13*nonPrimitiveValues[i].hashCode();
}
}
for (int i = 0; i < longValues.length; i++) {
code += 17*longValues[i];
}
return code;
}
public boolean equals(Object e) {
if (e == null || e.getClass() != Structure.class) {
return false;
}
Structure<?> other = (Structure<?>) e;
if (other.enumClass == enumClass) {
return Arrays.equals(other.nonPrimitiveValues, nonPrimitiveValues) &&
Arrays.equals(longValues, other.longValues);
}
return false;
}
private void assertSafeType(T e, Class<?> c) {
if (e instanceof TypeSafety) {
assert ((TypeSafety) e).getType() == c;
}
}
private void init(Class<?> enumClass, Enum<?>... fields) {
this.enumClass = enumClass;
if (fields != null && fields.length > 0) {
requestedFields = 0;
for (Enum<?> field : fields) {
requestedFields |= (1 << field.ordinal());
}
} else {
requestedFields = ~0;
}
Object[] enumConstants = enumClass.getEnumConstants();
assert enumConstants != null;
nonPrimitiveValues = adjustArraySize(nonPrimitiveValues, enumConstants.length);
longValues = adjustArraySize(longValues, enumConstants.length);
clear();
}
private static Object[] adjustArraySize(Object[] array, int desiredSize) {
if (array == null || array.length < desiredSize) {
return new Object[desiredSize];
}
return array;
}
private static long[] adjustArraySize(long[] array, int desiredSize) {
if (array == null || array.length < desiredSize) {
return new long[desiredSize];
}
return array;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('<');
sb.append(enumClass.getSimpleName());
sb.append(">\n");
for (Object field : enumClass.getEnumConstants()) {
@SuppressWarnings("unchecked")
T e = (T) field;
Object o = nonPrimitiveValues[e.ordinal()];
if (o != null) {
sb.append(e.name());
sb.append(" = ");
if (o == LONG_MARKER) {
sb.append(Long.toString(lng(e)));
} else if (o == BOOLEAN_MARKER) {
sb.append(is(e));
} else {
sb.append(o);
}
sb.append('\n');
}
}
return sb.toString();
}
private static class StructuresPool {
private BlockingQueue<Structure<?>> objectsQueues = new LinkedBlockingQueue<Structure<?>>(23);
@SuppressWarnings({ "unchecked", "rawtypes" })
public Structure<?> obtain(Class<?> enumClass, Enum<?>... fields) {
Structure<?> t = objectsQueues.poll();
if (t == null) {
t = new Structure(enumClass, fields);
} else {
t.init(enumClass, fields);
}
return t;
}
public void release(Structure<?> t) {
if (!objectsQueues.contains(t)) {
objectsQueues.offer(t);
}
}
}
}