package com.googlecode.objectify; import com.google.appengine.api.datastore.KeyFactory; import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.impl.TypeUtils; import java.io.Serializable; /** * <p>A typesafe wrapper for the datastore Key object.</p> * * @author Jeff Schnitzer <jeff@infohazard.org> * @author Scott Hernandez */ public class Key<T> implements Serializable, Comparable<Key<?>> { private static final long serialVersionUID = 2L; /** Key.create(key) is easier to type than new Key<Blah>(key) */ public static <T> Key<T> create(com.google.appengine.api.datastore.Key raw) { if (raw == null) throw new NullPointerException("Cannot create a Key<?> from a null datastore Key"); return new Key<>(raw); } /** Key.create(Blah.class, id) is easier to type than new Key<Blah>(Blah.class, id) */ public static <T> Key<T> create(Class<? extends T> kindClass, long id) { return new Key<>(kindClass, id); } /** Key.create(Blah.class, name) is easier to type than new Key<Blah>(Blah.class, name) */ public static <T> Key<T> create(Class<? extends T> kindClass, String name) { return new Key<>(kindClass, name); } /** Key.create(parent, Blah.class, id) is easier to type than new Key<Blah>(parent, Blah.class, id) */ public static <T> Key<T> create(Key<?> parent, Class<? extends T> kindClass, long id) { return new Key<>(parent, kindClass, id); } /** Key.create(parent, Blah.class, name) is easier to type than new Key<Blah>(parent, Blah.class, name) */ public static <T> Key<T> create(Key<?> parent, Class<? extends T> kindClass, String name) { return new Key<>(parent, kindClass, name); } /** Key.create(webSafeString) is easier to type than new Key<Blah>(webSafeString) */ public static <T> Key<T> create(String webSafeString) { if (webSafeString == null) throw new NullPointerException("Cannot create a Key<?> from a null String"); return new Key<>(webSafeString); } /** This is an alias for Key.create(String) which exists for JAX-RS compliance. */ public static <T> Key<T> valueOf(String webSafeString) { return Key.create(webSafeString); } /** Create a key from a registered POJO entity. */ public static <T> Key<T> create(T pojo) { return ObjectifyService.factory().keys().keyOf(pojo); } /** */ protected com.google.appengine.api.datastore.Key raw; /** Cache the instance of the parent wrapper to avoid unnecessary garbage */ transient protected Key<?> parent; /** For GWT serialization */ private Key() {} /** Wrap a raw Key */ private Key(com.google.appengine.api.datastore.Key raw) { this.raw = raw; } /** Create a key with a long id */ private Key(Class<? extends T> kindClass, long id) { this(null, kindClass, id); } /** Create a key with a String name */ private Key(Class<? extends T> kindClass, String name) { this(null, kindClass, name); } /** Create a key with a parent and a long id */ private Key(Key<?> parent, Class<? extends T> kindClass, long id) { this.raw = KeyFactory.createKey(key(parent), getKind(kindClass), id); this.parent = parent; } /** Create a key with a parent and a String name */ private Key(Key<?> parent, Class<? extends T> kindClass, String name) { this.raw = KeyFactory.createKey(key(parent), getKind(kindClass), name); this.parent = parent; } /** * Reconstitute a Key from a web safe string. This can be generated with getString()/toWebSafeString() * or KeyFactory.stringToKey(). */ private Key(String webSafe) { this(KeyFactory.stringToKey(webSafe)); } /** * @return the raw datastore version of this key */ public com.google.appengine.api.datastore.Key getRaw() { return this.raw; } /** * @return the id associated with this key, or 0 if this key has a name. */ public long getId() { return this.raw.getId(); } /** * @return the name associated with this key, or null if this key has an id */ public String getName() { return this.raw.getName(); } /** * @return the low-level datastore kind associated with this Key */ public String getKind() { return this.raw.getKind(); } /** * @return the parent key, or null if there is no parent. Note that * the parent could potentially have any type. */ @SuppressWarnings("unchecked") public <V> Key<V> getParent() { if (this.parent == null && this.raw.getParent() != null) this.parent = new Key<V>(this.raw.getParent()); return (Key<V>)this.parent; } /** * Gets the root of a parent graph of keys. If a Key has no parent, it is the root. * * @return the topmost parent key, or this object itself if it is the root. * Note that the root key could potentially have any type. */ @SuppressWarnings("unchecked") public <V> Key<V> getRoot() { if (this.getParent() == null) return (Key<V>)this; else return this.getParent().getRoot(); } /** * <p>Compares based on comparison of the raw key</p> */ @Override public int compareTo(Key<?> other) { return this.raw.compareTo(other.raw); } /** */ @Override public boolean equals(Object obj) { if (obj == null) return false; if (!(obj instanceof Key<?>)) return false; return this.compareTo((Key<?>)obj) == 0; } /** A type-safe equivalence comparison */ public boolean equivalent(Key<T> other) { return equals(other); } /** A type-safe equivalence comparison */ public boolean equivalent(Ref<T> other) { return (other != null) && equals(other.key()); } /** */ @Override public int hashCode() { return this.raw.hashCode(); } /** Creates a human-readable version of this key */ @Override public String toString() { return "Key<?>(" + this.raw + ")"; } /** * Call KeyFactory.keyToString() on the underlying Key. You can reconstitute a Key<?> using the * constructor that takes a websafe string. This is a javabeans-style alias for toWebSafeString(). */ public String getString() { return toWebSafeString(); } /** * Call KeyFactory.keyToString() on the underlying Key. You can reconstitute a Key<?> using the * constructor that takes a websafe string. Note that toString() is only useful for debugging; * it cannot be used to create a key with Key.create(String). */ public String toWebSafeString() { return KeyFactory.keyToString(this.raw); } /** * Easy null-safe conversion of the raw key. */ public static <V> Key<V> key(com.google.appengine.api.datastore.Key raw) { if (raw == null) return null; else return new Key<>(raw); } /** * Easy null-safe conversion of the typed key. */ public static com.google.appengine.api.datastore.Key key(Key<?> typed) { if (typed == null) return null; else return typed.getRaw(); } /** * <p>Determines the kind for a Class, as understood by the datastore. The first class in a * hierarchy that has @Entity defines the kind (either explicitly or as that class' simplename).</p> * * <p>If no @Entity annotation is found, just uses the simplename as is.</p> */ public static String getKind(Class<?> clazz) { String kind = getKindRecursive(clazz); if (kind == null) return clazz.getSimpleName(); else return kind; } /** * <p>Recursively looks for the @Entity annotation.</p> * * @return null if kind cannot be found */ private static String getKindRecursive(Class<?> clazz) { if (clazz == Object.class) return null; String kind = getKindHere(clazz); if (kind != null) return kind; else return getKindRecursive(clazz.getSuperclass()); } /** * Get the kind from the class if the class has an @Entity annotation, otherwise return null. */ private static String getKindHere(Class<?> clazz) { // @Entity is inherited so we have to be explicit about the declared annotations Entity ourAnn = TypeUtils.getDeclaredAnnotation(clazz, Entity.class); if (ourAnn != null) if (ourAnn.name() != null && ourAnn.name().length() != 0) return ourAnn.name(); else return clazz.getSimpleName(); return null; } }