package com.googlecode.objectify;
import com.googlecode.objectify.impl.ref.LiveRef;
import java.io.Serializable;
/**
* <p>Ref<?> is a Key<?> which allows the entity value to be fetched directly.</p>
*
* <p>Note that the methods might or might not throw runtime exceptions related to datastore operations;
* ConcurrentModificationException, DatastoreTimeoutException, DatastoreFailureException, and DatastoreNeedIndexException.
* Some Refs hide datastore operations that could throw these exceptions.</p>
*
* @author Jeff Schnitzer <jeff@infohazard.org>
*/
abstract public class Ref<T> implements Serializable, Comparable<Ref<T>>
{
private static final long serialVersionUID = 1L;
/** Key.create(Blah.class, id) is easier to type than new Key<Blah>(Blah.class, id) */
public static <T> Ref<T> create(Key<T> key) {
if (key == null)
throw new NullPointerException("Cannot create a Ref from a null key");
return new LiveRef<>(key);
}
/** Creates a Ref from a registered pojo entity */
public static <T> Ref<T> create(T value) {
Key<T> key = Key.create(value);
return create(key);
}
/** The key associated with this ref */
protected Key<T> key;
/** For GWT serialization */
protected Ref() {}
/**
* Create a Ref based on the key, with the specified session
*/
protected Ref(Key<T> key) {
if (key == null)
throw new NullPointerException("Cannot create a Ref for a null key");
this.key = key;
}
/**
* @return the key associated with this Ref
*/
public Key<T> key() {
return key;
}
/**
* Obtain the entity value associated with the key. Will pull from session if present, otherwise will
* fetch from the datastore.
*
* @return the entity referenced, or null if the entity was not found
*/
abstract public T get();
/**
* If an entity has been loaded into the session or is otherwise available, this will return true.
* Calls to get() will not require a trip to backing store.
* Note that even when loaded, get() can still return null if there is no entity which corresponds to the key.
*
* @return true if the value is in the session or otherwise immediately available; false if get() will
* require a trip to the datastore or memcache.
*/
abstract public boolean isLoaded();
/**
* This method exists to facilitate serialization via javabeans conventions. Unlike get(),
* it will return null if isLoaded() is false.
*
* @return the entity referenced, or null if either the entity was not found or isLoaded() is false.
*/
final public T getValue() {
return isLoaded() ? get() : null;
}
/**
* Same as key() but conforms to JavaBeans conventions in case this is being processed by a JSON
* converter or expression language.
*/
final public Key<T> getKey() {
return key();
}
/**
* Obtain the entity value, throwing an exception if the entity was not found.
*
* @return the entity referenced. Never returns null.
* @throws NotFoundException if the specified entity was not found
*/
final public T safe() throws NotFoundException {
T t = this.get();
if (t == null)
throw new NotFoundException(key());
else
return t;
}
/** Comparison is based on key */
@Override
public int compareTo(Ref<T> o) {
return this.key().compareTo(o.key());
}
/** Equality comparison is based on key equivalence */
@Override
public boolean equals(Object obj) {
return obj != null && obj instanceof Ref && key().equals(((Ref<?>)obj).key());
}
/** Type-safe comparison for key equivalence */
public boolean equivalent(Ref<T> other) {
return equals(other);
}
/** Type safe comparison for key equivalence */
public boolean equivalent(Key<T> other) {
return key().equivalent(other);
}
/** Hash code is simply that of key */
@Override
public int hashCode() {
return key().hashCode();
}
/** Renders some info about the key */
@Override
public String toString() {
return this.getClass().getSimpleName() + "(" + key() + ")";
}
}