package com.tinkerpop.frames.modules.typedgraph;
import com.tinkerpop.blueprints.Element;
import com.tinkerpop.frames.FramedGraph;
import com.tinkerpop.frames.util.Validate;
import java.util.HashMap;
import java.util.Map;
/**
* A TypeRegistry for a {@link FramedGraph}. With a {@link TypeRegistry} the
* {@link FramedGraph} is able to store type-information on edges,
* and use stored type-information to construct vertices/edges based on
* type-information stored in the graph (runtime).
*
* @see TypeField
* @see TypeValue
* @see FramedGraph
*/
public class TypeRegistry {
protected Map<Class<?>, Class<?>> typeFields = new HashMap<Class<?>, Class<?>>();
protected Map<TypeDiscriminator, Class<?>> typeDiscriminators = new HashMap<TypeDiscriminator, Class<?>>();
/**
* @return The interface that has the {@link TypeField} annotation for this
* class. (Either the class itself, or a base class if the class was
* registered).
*/
public Class<?> getTypeHoldingTypeField(Class<?> type) {
if (type.getAnnotation(TypeField.class) != null)
return type;
Class<?> result = typeFields.get(type);
return result;
}
/**
* @param typeHoldingTypeField
* The type that has the {@link TypeField} annotation for the
* Proxy class to be constructed.
* @param typeValue
* The actual persisted value for a type field on an
* {@link Element} in the graph
* @return The type that needs to be constructed, or null if there is no
* registered class that matches the input values.
*/
public Class<?> getType(Class<?> typeHoldingTypeField, String typeValue) {
Class<?> result = typeDiscriminators.get(new TypeDiscriminator(typeHoldingTypeField, typeValue));
return result;
}
protected static final class TypeDiscriminator {
public Class<?> typeHoldingTypeField;
public String value;
public TypeDiscriminator(Class<?> typeHoldingTypeField, String value) {
Validate.assertNotNull(typeHoldingTypeField, value);
this.typeHoldingTypeField = typeHoldingTypeField;
this.value = value;
}
@Override
public int hashCode() {
return 31 * (31 + typeHoldingTypeField.hashCode()) + value.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof TypeDiscriminator) {
TypeDiscriminator other = (TypeDiscriminator) obj;
// fields are never null:
return typeHoldingTypeField.equals(other.typeHoldingTypeField) && value.equals(other.value);
}
return false;
}
}
/**
* @param Add
* the interface to the registry. The interface should have a
* {@link TypeValue} annotation, and there should be a
* {@link TypeField} annotation
* on the interface or its parents.
*/
public TypeRegistry add(Class<?> type) {
Validate.assertArgument(type.isInterface(), "Not an interface: %s", type.getName());
if (!typeFields.containsKey(type)) {
Class<?> typeHoldingTypeField = findTypeHoldingTypeField(type);
Validate.assertArgument(typeHoldingTypeField != null,
"The type and its supertypes don't have a @TypeField annotation: %s", type.getName());
typeFields.put(type, typeHoldingTypeField);
registerTypeValue(type, typeHoldingTypeField);
}
return this;
}
protected Class<?> findTypeHoldingTypeField(Class<?> type) {
Class<?> typeHoldingTypeField = type.getAnnotation(TypeField.class) == null ? null : type;
for (Class<?> parentType : type.getInterfaces()) {
Class<?> parentTypeHoldingTypeField = findTypeHoldingTypeField(parentType);
Validate.assertArgument(parentTypeHoldingTypeField == null || typeHoldingTypeField == null
|| parentTypeHoldingTypeField == typeHoldingTypeField,
"You have multiple TypeField annotations in your class-hierarchy for %s", type.getName());
if (typeHoldingTypeField == null)
typeHoldingTypeField = parentTypeHoldingTypeField;
}
return typeHoldingTypeField;
}
protected void registerTypeValue(Class<?> type, Class<?> typeHoldingTypeField) {
TypeValue typeValue = type.getAnnotation(TypeValue.class);
Validate.assertArgument(typeValue != null, "The type does not have a @TypeValue annotation: %s", type.getName());
typeDiscriminators.put(new TypeRegistry.TypeDiscriminator(typeHoldingTypeField, typeValue.value()), type);
}
}