package com.googlecode.objectify.impl; import com.googlecode.objectify.Key; import com.googlecode.objectify.ObjectifyFactory; import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.Subclass; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; /** * <p>Maintains information about registered entity classes<p> * * <p>There logic here is convoluted by polymorphic hierarchies. Entity classes can * be registered in any particular order, requiring some considerable care.</p> * * @author Jeff Schnitzer <jeff@infohazard.org> */ public class Registrar { /** */ @SuppressWarnings("unused") private static final Logger log = Logger.getLogger(Registrar.class.getName()); /** Needed to obtain the converters */ protected ObjectifyFactory fact; /** This maps kind to EntityMetadata */ protected Map<String, EntityMetadata<?>> byKind = new HashMap<>(); /** True if any @Cached entities have been registered */ protected boolean cacheEnabled; /** @return true if any entities are cacheable */ public boolean isCacheEnabled() { return this.cacheEnabled; } /** * @param fact is so that the translations can be obtained */ public Registrar(ObjectifyFactory fact) { this.fact = fact; } /** * <p>All @Entity and @Subclass classes (for both entity and embedded classes) * must be registered before using Objectify to load or save data. This method * must be called in a single-threaded mode sometime around application initialization.</p> * * <p>Re-registering a class has no effect.</p> * * @param clazz must be annotated with either @Entity or @Subclass */ public <T> void register(Class<T> clazz) { // There are two possible cases // 1) This might be a simple class with @Entity // 2) This might be a class annotated with @Subclass // @Entity is inherited, but we only create entity metadata for the class with the @Entity declaration if (TypeUtils.isDeclaredAnnotationPresent(clazz, Entity.class)) { String kind = Key.getKind(clazz); // If we are already registered, ignore if (this.byKind.containsKey(kind)) return; EntityMetadata<T> cmeta = new EntityMetadata<>(this.fact, clazz); this.byKind.put(kind, cmeta); if (cmeta.getCacheExpirySeconds() != null) this.cacheEnabled = true; } else if (clazz.isAnnotationPresent(Subclass.class)) { // We just need to make sure that a translator was created fact.getTranslators().getRoot(clazz); } else { throw new IllegalArgumentException(clazz + " must be annotated with either @Entity or @Subclass"); } } /** * @return the metadata for the specified kind, or null if there was nothing appropriate registered */ @SuppressWarnings("unchecked") public <T> EntityMetadata<T> getMetadata(String kind) { return (EntityMetadata<T>)this.byKind.get(kind); } /** * @return the metadata for the specified class, or null if there was nothing appropriate registered */ @SuppressWarnings("unchecked") public <T> EntityMetadata<T> getMetadata(Class<T> clazz) { return getMetadata(Key.getKind(clazz)); } /** * Gets metadata for the specified kind * @throws IllegalArgumentException if the kind has not been registered */ public <T> EntityMetadata<T> getMetadataSafe(String kind) throws IllegalArgumentException { EntityMetadata<T> metadata = this.getMetadata(kind); if (metadata == null) throw new IllegalArgumentException("No entity class has been registered which matches kind '" + kind + "'"); else return metadata; } /** * @return the metadata for a kind of typed object * @throws IllegalArgumentException if the kind has not been registered */ public <T> EntityMetadata<T> getMetadataSafe(Class<T> clazz) throws IllegalArgumentException { EntityMetadata<T> metadata = this.getMetadata(clazz); if (metadata == null) throw new IllegalArgumentException("No class '" + clazz.getName() + "' was registered"); else return metadata; } }