package com.googlecode.objectify.impl.translate; import com.google.appengine.api.datastore.PropertyContainer; import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.Subclass; import com.googlecode.objectify.impl.KeyMetadata; import com.googlecode.objectify.impl.Path; import java.util.HashMap; import java.util.Map; /** * <p>Translator which maps classes, both normal embedded classes and Entity classes.</p> * * <p>Entity classes are just like any other class except they have @Id and @Parent fields and a kind. * When translating to native datastore structure (Entity for top level, EmbeddedEntity for an embedded field) * then these attributes are stored in the Key structure, not as properties.</p> * * <p>An entity class is any class which has the @Entity annotation anywhere in its superclass * hierarchy.</p> * * <p>Note that entities can be embedded in other objects; they are still entities. The * difference between an embedded class and an embedded entity is that the entity has a Key.</p> * * <p>One noteworthy issue is that we must ensure there is only one classtranslator for any * given class. Normally the discovery process creates a separate translator for each set of * annotations, however, this screws up the subclass registration process, which needs to * register at each parent class translator. Since field annotations are actually irrelevant to * the internal function of a ClassTranslator, we can just cache class translators here * in the factory. There will never be more than one translator for a given class, even * though many TypeKeys may point at it.</p> * * @author Jeff Schnitzer <jeff@infohazard.org> */ public class ClassTranslatorFactory<P> implements TranslatorFactory<P, PropertyContainer> { /** Cache of existing translators, see the class javadoc */ private Map<Class<P>, ClassTranslator<P>> translators = new HashMap<>(); @Override public ClassTranslator<P> create(TypeKey<P> tk, CreateContext ctx, Path path) { Class<P> clazz = tk.getTypeAsClass(); ClassTranslator<P> classTranslator = translators.get(clazz); if (classTranslator == null) { // Entity is an inherited annotation; this checks up the hierarchy classTranslator = (clazz.isAnnotationPresent(Entity.class)) ? createEntityClassTranslator(clazz, ctx, path) : createEmbeddedClassTranslator(clazz, ctx, path); translators.put(clazz, classTranslator); if (clazz.isAnnotationPresent(Subclass.class)) registerSubclass(classTranslator, new TypeKey<>(clazz.getSuperclass(), tk), ctx, path); } return classTranslator; } /** */ public static <P> ClassTranslator<P> createEntityClassTranslator(Class<P> clazz, CreateContext ctx, Path path) { KeyMetadata<P> keyMetadata = new KeyMetadata<>(clazz, ctx, path); Creator<P> creator = new EntityCreator<>(clazz, ctx.getFactory(), keyMetadata); Populator<P> populator = new ClassPopulator<>(clazz, ctx, path); return new ClassTranslator<>(clazz, path, creator, populator); } /** */ public static <P> ClassTranslator<P> createEmbeddedClassTranslator(Class<P> clazz, CreateContext ctx, Path path) { Creator<P> creator = new EmbeddedCreator<>(clazz, ctx.getFactory()); Populator<P> populator = new ClassPopulator<>(clazz, ctx, path); return new ClassTranslator<>(clazz, path, creator, populator); } /** * Recursively register this subclass with all the superclass translators. This works because we cache * translators uniquely in the factory. */ private void registerSubclass(ClassTranslator<P> translator, TypeKey<? super P> superclassTypeKey, CreateContext ctx, Path path) { if (superclassTypeKey.getTypeAsClass() == Object.class) return; @SuppressWarnings("unchecked") ClassTranslator<? super P> superTranslator = create((TypeKey)superclassTypeKey, ctx, path); superTranslator.registerSubclass(translator); registerSubclass(translator, new TypeKey<>(superclassTypeKey.getTypeAsClass().getSuperclass()), ctx, path); } }