/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache;
import net.jcip.annotations.Immutable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.util.ImmutableListCopy;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* A Fully Qualified Name (Fqn) is a list of names (typically Strings but can be any Object),
* which represent a path to a particular {@link Node} or sometimes a {@link Region} in a {@link Cache}.
* <p/>
* This name can be absolute (i.e., relative from the root node - {@link #ROOT}), or relative to any node in the cache. Reading the
* documentation on each API call that makes use of {@link org.jboss.cache.Fqn}s will tell you whether the API expects a
* relative or absolute Fqn.
* <p/>
* For instance, using this class to fetch a particular node might look like
* this. (Here data on "Joe" is kept under the "Smith" surname node, under
* the "people" tree.)
* <pre>
* Fqn<String> abc = Fqn.fromString("/people/Smith/Joe/");
* Node joesmith = Cache.getRoot().getChild(abc);
* </pre>
* Alternatively, the same Fqn could be constructed using an array:
* <pre>
* Fqn<String> abc = Fqn.fromElements("people", "Smith", "Joe");
* </pre>
* This is a bit more efficient to construct.
* <p/>
* Note that<br>
* <p/>
* <code>Fqn<String> f = new Fqn<String>("/a/b/c");</code>
* <p/>
* is <b>not</b> the same as
* <p/>
* <code>Fqn<String> f = Fqn.fromString("/a/b/c");</code>
* <p/>
* The former will result in a single Fqn, called "/a/b/c" which hangs directly under Fqn.ROOT.
* <p/>
* The latter will result in 3 Fqns, called "a", "b" and "c", where "c" is a child of "b", "b" is a child of "a", and "a" hangs off Fqn.ROOT.
* <p/>
* Another way to look at it is that the "/" separarator is only parsed when it forms
* part of a String passed in to Fqn.fromString() and not otherwise.
* <p/>
* <B>Best practices</B>: Always creating Fqns - even when using some factory methods - can be expensive in the long run,
* and as far as possible we recommend that client code holds on to their Fqn references and reuse them. E.g.:
* <code>
* // BAD!!
* for (int i=0; i<someBigNumber; i++)
* {
* cache.get(Fqn.fromString("/a/b/c"), "key" + i);
* }
* </code>
* instead, do:
* <code>
* // Much better
* Fqn f = Fqn.fromString("/a/b/c");
* for (int i=0; i<someBigNumber; i++)
* {
* cache.get(f, "key" + i);
* }
* </code>
* <p/>
* <B>NOTE</B> public constructors of this class are <b>deprectaed</b> and will be removed in JBoss Cache 3.0.0. The
* factory methods provided are the correct way get an instance of an Fqn, since these allow for numerous optimisations.
* <p/>
*
* @version $Revision: 6413 $
*/
@Immutable
public class Fqn<E> implements Cloneable, Externalizable, Comparable<Fqn<?>>
{
/**
* Separator between FQN elements.
*/
public static final String SEPARATOR = "/";
private static final long serialVersionUID = -5351930616956603651L;
private static final Log log = LogFactory.getLog(Fqn.class);
protected List<E> elements;
private transient int hash_code = 0;
protected int size = 0;
/**
* Immutable root FQN.
*/
@SuppressWarnings("unchecked")
public static final Fqn ROOT = new Fqn(true);
/**
* A cached string representation of this Fqn, used by toString to it isn't calculated again every time.
*/
protected String stringRepresentation;
// ----------------- START: Private constructors for use by factory methods only. ----------------------
// TODO: 3.0.0: Remove the unnecessary internalMarker boolean parameters to these methods once the deprecated public constructors are removed in 3.0.0.
protected Fqn(boolean internalMarker)
{
elements = Collections.emptyList();
size = 0;
}
@SuppressWarnings("unchecked")
protected Fqn(boolean internalMarker, List<?> names, boolean safe)
{
if (names != null)
{
// if not safe make a defensive copy
elements = safe ? names : new ImmutableListCopy(names);
size = elements.size();
}
else
{
elements = Collections.emptyList();
size = 0;
}
}
@SuppressWarnings("unchecked")
protected Fqn(boolean internalMarker, Fqn<?> base, List<?> relative)
{
elements = new ImmutableListCopy(base.elements, relative);
size = elements.size();
}
// ----------------- END: Private constructors for use by factory methods only. ----------------------
/**
* Constructs a root Fqn
*
* @deprecated use {@link #ROOT} instead. This constructor will be removed in 3.0.0.
*/
@Deprecated
public Fqn()
{
this(true);
}
/**
* Constructs a FQN from a list of names.
*
* @param names List of names
* @deprecated use {@link #fromList(java.util.List)} instead. This constructor will be removed in 3.0.0.
*/
@Deprecated
public Fqn(List<E> names)
{
// the list is unsafe - may be referenced externally
this(true, names, false);
}
/**
* Retrieves an Fqn that represents the list of elements passed in.
*
* @param names list of elements that comprise the Fqn
* @return an Fqn
* @since 2.2.0
*/
public static <E> Fqn<E> fromList(List<E> names)
{
return new Fqn<E>(true, names, false);
}
/**
* Retrieves an Fqn that represents the list of elements passed in.
*
* @param names list of elements that comprise the Fqn
* @param safe if true, the list passed in is not defensively copied but used directly. <b>Use with care.</b> Make sure
* you know what you are doing before you pass in a <tt>true</tt> value to <tt>safe</tt>, as it can have adverse effects on
* performance or correctness. The defensive copy of list elements is not just for safety but also for performance as
* an appropriare List implementation is used, which works well with Fqn operations.
* @return an Fqn
*/
public static <E> Fqn<E> fromList(List<E> elements, boolean safe)
{
return new Fqn<E>(true, elements, safe);
}
/**
* If safe is false, Collections.unmodifiableList() is used to wrap the list passed in. This is an optimisation so
* Fqn.fromString(), probably the most frequently used factory method, doesn't end up needing to use the unmodifiableList()
* since it creates the list internally.
*
* @param names List of names
* @param safe whether this list is referenced externally (safe = false) or not (safe = true).
* @deprecated use {@link #fromList(java.util.List)} instead. The boolean "safety" hint is calculated internally. This constructor will be removed in 3.0.0.
*/
@Deprecated
public Fqn(List<E> names, boolean safe)
{
this(true, names, safe);
}
/**
* Constructs a Fqn from an array of names.
*
* @param names Names that comprose this Fqn
* @deprecated use {@link #fromElements(Object[])} instead. This constructor will be removed in 3.0.0.
*/
@Deprecated
public Fqn(E... names)
{
// safe - the list is created here.
this(true, Arrays.asList(names), true);
}
/**
* Retrieves an Fqn that represents the array of elements passed in.
*
* @param elements array of elements that comprise the Fqn
* @return an Fqn
* @since 2.2.0
*/
public static <E> Fqn<E> fromElements(E... elements)
{
return new Fqn<E>(true, Arrays.asList(elements), true);
}
/**
* Constructs a Fqn from a base and relative Fqn.
*
* @param base parent Fqn
* @param relative Sub-Fqn relative to the parent
* @deprecated use {@link #fromRelativeFqn(Fqn, Fqn)} instead. This constructor will be removed in 3.0.0.
*/
@Deprecated
public Fqn(Fqn<E> base, Fqn<E> relative)
{
this(true, base, relative.elements);
}
/**
* Retrieves an Fqn that represents the absolute Fqn of the relative Fqn passed in.
*
* @param base base Fqn
* @param relative relative Fqn
* @return an Fqn
* @since 2.2.0
*/
public static Fqn<Object> fromRelativeFqn(Fqn<?> base, Fqn<?> relative)
{
return new Fqn<Object>(true, base, relative.elements);
}
/**
* Constructs a Fqn from a base and a list of relative names.
*
* @param base parent Fqn
* @param relative List of elements that identify the child Fqn, relative to the parent
* @deprecated use {@link #fromRelativeList(Fqn, java.util.List)} instead. This constructor will be removed in 3.0.0.
*/
@Deprecated
public Fqn(Fqn<E> base, List<E> relative)
{
this(true, base, relative);
}
/**
* Retrieves an Fqn that represents the List of elements passed in, relative to the base Fqn.
*
* @param base base Fqn
* @param relativeElements relative List of elements
* @return an Fqn
* @since 2.2.0
*/
public static Fqn<Object> fromRelativeList(Fqn<?> base, List<?> relativeElements)
{
return new Fqn<Object>(true, base, relativeElements);
}
/**
* Constructs a Fqn from a base and two relative names.
*
* @param base parent Fqn
* @param childNames elements that denote the path to the Fqn, under the parent
* @deprecated use {@link #fromRelativeElements(Fqn, Object[])} instead. This constructor will be removed in 3.0.0.
*/
@Deprecated
public Fqn(Fqn<E> base, E... childNames)
{
this(true, base, Arrays.asList(childNames));
}
/**
* Retrieves an Fqn that represents the array of elements passed in, relative to the base Fqn.
*
* @param base base Fqn
* @param relativeElements relative elements
* @return an Fqn
* @since 2.2.0
*/
public static Fqn<Object> fromRelativeElements(Fqn<?> base, Object... relativeElements)
{
return new Fqn<Object>(true, base, Arrays.asList(relativeElements));
}
/**
* Returns a new Fqn from a string, where the elements are deliminated by
* one or more separator ({@link #SEPARATOR}) characters.<br><br>
* Example use:<br>
* <pre>
* Fqn.fromString("/a/b/c/");
* </pre><br>
* is equivalent to:<br>
* <pre>
* new Fqn("a", "b", "c");
* </pre><br>
* but not<br>
* <pre>
* new Fqn("/a/b/c");
* </pre>
*
* @param stringRepresentation String representation of the Fqn
* @return an Fqn<String> constructed from the string representation passed in
* @see #Fqn(Object[])
*/
@SuppressWarnings("unchecked")
public static Fqn<String> fromString(String stringRepresentation)
{
if (stringRepresentation == null || stringRepresentation.equals(SEPARATOR) || stringRepresentation.equals(""))
return root();
String toMatch = stringRepresentation.startsWith(SEPARATOR) ? stringRepresentation.substring(1) : stringRepresentation;
Object[] el = toMatch.split("/");
return new Fqn(new ImmutableListCopy(el), true);
}
/**
* Retrieves an Fqn read from an object input stream, typically written to using {@link #writeExternal(java.io.ObjectOutput)}.
*
* @param in input stream
* @return an Fqn
* @throws IOException in the event of a problem reading the stream
* @throws ClassNotFoundException in the event of classes that comprise the element list of this Fqn not being found
* @since 2.2.0
*/
public static Fqn<Object> fromExternalStream(ObjectInput in) throws IOException, ClassNotFoundException
{
Fqn<Object> f = new Fqn<Object>(true);
f.readExternal(in);
return f;
}
/**
* Obtains an ancestor of the current Fqn. Literally performs <code>elements.subList(0, generation)</code>
* such that if
* <code>
* generation == Fqn.size()
* </code>
* then the return value is the Fqn itself (current generation), and if
* <code>
* generation == Fqn.size() - 1
* </code>
* then the return value is the same as
* <code>
* Fqn.getParent()
* </code>
* i.e., just one generation behind the current generation.
* <code>
* generation == 0
* </code>
* would return Fqn.ROOT.
*
* @param generation the generation of the ancestor to retrieve
* @return an ancestor of the current Fqn
*/
public Fqn<E> getAncestor(int generation)
{
if (generation == 0) return root();
return getSubFqn(0, generation);
}
/**
* Obtains a sub-Fqn from the given Fqn. Literally performs <code>elements.subList(startIndex, endIndex)</code>
*/
public Fqn<E> getSubFqn(int startIndex, int endIndex)
{
List el = elements.subList(startIndex, endIndex);
return new Fqn<E>(true, el, true);
}
/**
* @return the number of elements in the Fqn. The root node contains zero.
*/
public int size()
{
return size;
}
/**
* @param n index of the element to return
* @return Returns the nth element in the Fqn.
*/
public E get(int n)
{
return elements.get(n);
}
/**
* @return the last element in the Fqn.
* @see #getLastElementAsString
*/
public E getLastElement()
{
if (isRoot()) return null;
return elements.get(size - 1);
}
/**
* @param element element to find
* @return true if the Fqn contains this element, false otherwise.
*/
public boolean hasElement(E element)
{
return elements.indexOf(element) != -1;
}
/**
* Clones the Fqn.
*/
@Override
@SuppressWarnings("unchecked")
public Fqn<E> clone() throws CloneNotSupportedException
{
try
{
return (Fqn<E>) super.clone();
}
catch (CloneNotSupportedException e)
{
log.error("Unable to clone Fqn " + this, e);
return null;
}
}
/**
* Returns true if obj is a Fqn with the same elements.
*/
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (!(obj instanceof Fqn))
{
return false;
}
Fqn<?> other = (Fqn<?>) obj;
return size == other.size() && elements.equals(other.elements);
}
/**
* Returns a hash code with Fqn elements.
*/
@Override
public int hashCode()
{
if (hash_code == 0)
{
hash_code = calculateHashCode();
}
return hash_code;
}
/**
* Returns this Fqn as a string, prefixing the first element with a {@link Fqn#SEPARATOR} and
* joining each subsequent element with a {@link Fqn#SEPARATOR}.
* If this is the root Fqn, returns {@link Fqn#SEPARATOR}.
* Example:
* <pre>
* new Fqn(new Object[] { "a", "b", "c" }).toString(); // "/a/b/c"
* Fqn.ROOT.toString(); // "/"
* </pre>
*/
@Override
public String toString()
{
if (stringRepresentation == null)
{
stringRepresentation = getStringRepresentation(elements);
}
return stringRepresentation;
}
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeShort(size);
for (E element : elements)
{
out.writeObject(element);
}
}
@SuppressWarnings("unchecked")
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
size = in.readShort();
this.elements = new ArrayList<E>(size);
for (int i = 0; i < size; i++)
{
E e = (E) in.readObject();
elements.add(e);
}
}
/**
* Returns true if this Fqn is child of parentFqn.
* Example usage:
* <pre>
* Fqn<String> f1 = Fqn.fromString("/a/b");
* Fqn<String> f2 = Fqn.fromString("/a/b/c");
* assertTrue(f1.isChildOf(f2));
* assertFalse(f1.isChildOf(f1));
* assertFalse(f2.isChildOf(f1));
* </pre>
*
* @param parentFqn candidate parent to test against
* @return true if the target is a child of parentFqn
*/
public boolean isChildOf(Fqn<E> parentFqn)
{
return parentFqn.size() != size && isChildOrEquals(parentFqn);
}
/**
* Returns true if this Fqn is a <i>direct</i> child of a given Fqn.
*
* @param parentFqn parentFqn to compare with
* @return true if this is a direct child, false otherwise.
*/
public boolean isDirectChildOf(Fqn parentFqn)
{
return size == parentFqn.size() + 1 && isChildOf(parentFqn);
}
/**
* Returns true if this Fqn is equals or the child of parentFqn.
* Example usage:
* <pre>
* Fqn<String> f1 = Fqn.fromString("/a/b");
* Fqn<String> f2 = Fqn.fromString("/a/b/c");
* assertTrue(f1.isChildOrEquals(f2));
* assertTrue(f1.isChildOrEquals(f1));
* assertFalse(f2.isChildOrEquals(f1));
* </pre>
*
* @param parentFqn candidate parent to test against
* @return true if this Fqn is equals or the child of parentFqn.
*/
public boolean isChildOrEquals(Fqn<E> parentFqn)
{
List<E> parentList = parentFqn.elements;
if (parentList.size() > size)
{
return false;
}
for (int i = parentList.size() - 1; i >= 0; i--)
{
if (!parentList.get(i).equals(elements.get(i)))
{
return false;
}
}
return true;
}
/**
* Calculates a hash code by summing the hash code of all elements.
*
* @return a cached hashcode
*/
protected int calculateHashCode()
{
int hashCode = 0;
int count = 1;
Object o;
for (E element : elements)
{
o = element;
hashCode += (o == null) ? 0 : o.hashCode() * count++;
}
if (hashCode == 0)// fix degenerate case
{
hashCode = 0xFEED;
}
return hashCode;
}
protected String getStringRepresentation(List elements)
{
StringBuilder builder = new StringBuilder();
for (Object e : elements)
{
// incase user element 'e' does not implement equals() properly, don't rely on their implementation.
if (!SEPARATOR.equals(e) && !"".equals(e))
{
builder.append(SEPARATOR);
builder.append(e);
}
}
return builder.length() == 0 ? SEPARATOR : builder.toString();
}
/**
* Returns the parent of this Fqn.
* The parent of the root node is {@link #ROOT}.
* Examples:
* <pre>
* Fqn<String> f1 = Fqn.fromString("/a");
* Fqn<String> f2 = Fqn.fromString("/a/b");
* assertEquals(f1, f2.getParent());
* assertEquals(Fqn.ROOT, f1.getParent().getParent());
* assertEquals(Fqn.ROOT, Fqn.ROOT.getParent());
* </pre>
*
* @return the parent Fqn
*/
public Fqn<E> getParent()
{
switch (size)
{
case 0:
case 1:
return root();
default:
return new Fqn<E>(true, elements.subList(0, size - 1), true);
}
}
@SuppressWarnings("unchecked")
public static <T> Fqn<T> root()
{
return ROOT;
}
/**
* Returns true if this is a root Fqn.
*
* @return true if the Fqn is Fqn.ROOT.
*/
public boolean isRoot()
{
return size == 0;
}
/**
* If this is the root, returns {@link Fqn#SEPARATOR}.
*
* @return a String representation of the last element that makes up this Fqn.
*/
public String getLastElementAsString()
{
if (isRoot())
{
return SEPARATOR;
}
else
{
Object last = getLastElement();
if (last instanceof String)
return (String) last;
else
return String.valueOf(getLastElement());
}
}
/**
* Peeks into the elements that build up this Fqn. The list returned is
* read-only, to maintain the immutable nature of Fqn.
*
* @return an unmodifiable list
*/
public List<E> peekElements()
{
return elements;
}
/**
* Compares this Fqn to another using {@link FqnComparator}.
*/
public int compareTo(Fqn<?> Fqn)
{
return FqnComparator.INSTANCE.compare(this, Fqn);
}
}