package com.googlecode.objectify;
import java.io.Serializable;
/**
* <p>This is a typesafe version of the Key object. It is also Serializable
* and GWT-safe, enabling your entity objects to be used for GWT RPC should
* you so desire.</p>
*
* <p>You may use normal Key objects as relationships in your entities if you
* desire neither type safety nor GWTability.</p>
*
* @author Jeff Schnitzer <jeff@infohazard.org>
* @author Scott Hernandez
*/
public class Key<T> implements Serializable, Comparable<Key<?>>
{
private static final long serialVersionUID = 1L;
/**
* The name of the class which represents the kind. As much as
* we'd like to use the normal String kind value here, translating
* back to a Class for getKind() would then require a link to the
* OFactory, making this object non-serializable.
*/
protected String kindClassName;
/** Null if there is no parent */
protected Key<?> parent;
/** Either id or name will be valid */
protected long id;
/** Either id or name will be valid */
protected String name;
/** For GWT serialization */
protected Key() {}
/** Create a key with a long id */
public Key(Class<? extends T> kind, long id)
{
this(null, kind, id);
}
/** Create a key with a String name */
public Key(Class<? extends T> kind, String name)
{
this(null, kind, name);
}
/** Create a key with a parent and a long id */
public Key(Key<?> parent, Class<? extends T> kind, long id)
{
this.parent = parent;
this.kindClassName = kind.getName();
this.id = id;
}
/** Create a key with a parent and a String name */
public Key(Key<?> parent, Class<? extends T> kind, String name)
{
this.parent = parent;
this.kindClassName = kind.getName();
this.name = name;
}
/**
* @return the id associated with this key, or 0 if this key has a name.
*/
public long getId()
{
return this.id;
}
/**
* @return the name associated with this key, or null if this key has an id
*/
public String getName()
{
return this.name;
}
/**
* @return the name of the Class associated with this key.
*/
public String getKindClassName()
{
return this.kindClassName;
}
/**
* @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()
{
return (Key<V>)this.parent;
}
/**
* <p>Compares based on the following traits, in order:</p>
* <ol>
* <li>kind</li>
* <li>parent</li>
* <li>id or name</li>
* </ol>
*/
@Override
public int compareTo(Key<?> other)
{
// First kind
int cmp = this.kindClassName.compareTo(other.kindClassName);
if (cmp != 0)
return cmp;
// Then parent
cmp = compareNullable(this.parent, other.parent);
if (cmp != 0)
return cmp;
// Then either id or name, whichever exists - but they might be different
cmp = compareNullable(this.name, other.name);
if (cmp != 0)
return cmp;
if (this.id < other.id)
return -1;
else if (this.id > other.id)
return 1;
else
return 0;
}
/** */
@Override
public boolean equals(Object obj)
{
if (obj == null)
return false;
if (!(obj instanceof Key<?>))
return false;
return this.compareTo((Key<?>)obj) == 0;
}
/** */
@Override
public int hashCode()
{
if (this.name != null)
return this.name.hashCode();
else
return (int)this.id;
}
/** Creates a human-readable version of this key */
@Override
public String toString()
{
StringBuilder bld = new StringBuilder();
bld.append("Key{kindClassName=");
bld.append(this.kindClassName);
bld.append(", parent=");
bld.append(this.parent);
if (this.name != null)
{
bld.append(", name=");
bld.append(this.name);
}
else
{
bld.append(", id=");
bld.append(this.id);
}
bld.append("}");
return bld.toString();
}
/** */
@SuppressWarnings("unchecked")
private static int compareNullable(Comparable o1, Comparable o2)
{
if (o1 == null && o2 == null)
return 0;
if (o1 == null && o2 != null)
return -1;
else if (o1 != null && o2 == null)
return 1;
else
return o1.compareTo(o2);
}
}