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); } }