Author: manik.surtani(a)jboss.com
Date: 2008-04-22 10:53:02 -0400 (Tue, 22 Apr 2008)
New Revision: 5620
Added:
core/trunk/src/main/java/org/jboss/cache/StringFqn.java
Modified:
core/trunk/src/main/java/org/jboss/cache/Fqn.java
core/trunk/src/test/java/org/jboss/cache/FqnTest.java
Log:
Optimised Fqns for String representation
Modified: core/trunk/src/main/java/org/jboss/cache/Fqn.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/Fqn.java 2008-04-22 12:57:24 UTC (rev 5619)
+++ core/trunk/src/main/java/org/jboss/cache/Fqn.java 2008-04-22 14:53:02 UTC (rev 5620)
@@ -94,7 +94,7 @@
private static final Log log = LogFactory.getLog(Fqn.class);
- private List<E> elements;
+ protected List<E> elements;
private transient int hash_code = 0;
private int size = 0;
@@ -102,25 +102,25 @@
* Immutable root FQN.
*/
@SuppressWarnings("unchecked")
- public static final Fqn ROOT = new Fqn(true);
+ public static final Fqn ROOT = new StringFqn();
/**
* A cached string representation of this Fqn, used by toString to it isn't
calculated again every time.
*/
- private String cachedStringRep;
+ protected String stringRepresentation;
// ----------------- START: Private constructors for use by factory methods only.
----------------------
// TODO: Remove the unnecessary internalMarker boolean parameters to these methods
once the deprecated public constructors are removed in 3.0.0.
- private Fqn(boolean internalMarker)
+ protected Fqn(boolean internalMarker)
{
elements = Collections.emptyList();
size = 0;
}
@SuppressWarnings("unchecked")
- private Fqn(boolean internalMarker, List<?> names, boolean safe)
+ protected Fqn(boolean internalMarker, List<?> names, boolean safe)
{
if (names != null)
{
@@ -136,7 +136,7 @@
}
@SuppressWarnings("unchecked")
- private Fqn(boolean internalMarker, Fqn<?> base, List<?> relative)
+ protected Fqn(boolean internalMarker, Fqn<?> base, List<?> relative)
{
List elements = new ArrayList(base.elements.size() + relative.size());
elements.addAll(base.elements);
@@ -180,7 +180,10 @@
*/
public static <E> Fqn<E> fromList(List<E> names)
{
- return new Fqn<E>(true, names, false);
+ if (containsStrings(names))
+ return new StringFqn((List<String>) names);
+ else
+ return new Fqn<E>(true, names, false);
}
/**
@@ -221,7 +224,11 @@
*/
public static <E> Fqn<E> fromElements(E... elements)
{
- return new Fqn<E>(true, Arrays.asList(elements), true);
+ List el = Arrays.asList(elements);
+ if (containsStrings(el))
+ return new StringFqn(el);
+ else
+ return new Fqn<E>(true, el, true);
}
/**
@@ -247,7 +254,10 @@
*/
public static Fqn<Object> fromRelativeFqn(Fqn<?> base, Fqn<?>
relative)
{
- return new Fqn<Object>(true, base, relative.elements);
+ if (base.getClass().equals(StringFqn.class) &&
relative.getClass().equals(StringFqn.class))
+ return new StringFqn((StringFqn) base, (StringFqn) relative);
+ else
+ return new Fqn<Object>(true, base, relative.elements);
}
/**
@@ -273,7 +283,10 @@
*/
public static Fqn<Object> fromRelativeList(Fqn<?> base, List<?>
relativeElements)
{
- return new Fqn<Object>(true, base, relativeElements);
+ if (base.getClass().equals(StringFqn.class) &&
containsStrings(relativeElements))
+ return new StringFqn((StringFqn) base, (List<String>) relativeElements);
+ else
+ return new Fqn<Object>(true, base, relativeElements);
}
/**
@@ -299,9 +312,22 @@
*/
public static Fqn<Object> fromRelativeElements(Fqn<?> base, Object...
relativeElements)
{
- return new Fqn<Object>(true, base, Arrays.asList(relativeElements));
+ List rel = Arrays.asList(relativeElements);
+ if (base.getClass().equals(StringFqn.class) && containsStrings(rel))
+ return new StringFqn((StringFqn) base, rel);
+ else
+ return new Fqn<Object>(true, base, rel);
}
+ protected static boolean containsStrings(List l)
+ {
+ for (Object o : l)
+ {
+ if (!(o instanceof String)) return false;
+ }
+ return true;
+ }
+
/**
* Returns a new Fqn from a string, where the elements are deliminated by
* one or more separator ({@link #SEPARATOR}) characters.<br><br>
@@ -322,6 +348,7 @@
* @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)
@@ -331,7 +358,7 @@
List<String> list = new ArrayList<String>();
StringTokenizer tok = new StringTokenizer(stringRepresentation, SEPARATOR);
while (tok.hasMoreTokens()) list.add(tok.nextToken());
- return new Fqn<String>(true, list, true);
+ return new StringFqn(list);
}
/**
@@ -465,7 +492,7 @@
{
if (hash_code == 0)
{
- hash_code = _hashCode();
+ hash_code = calculateHashCode();
}
return hash_code;
}
@@ -482,11 +509,11 @@
*/
public String toString()
{
- if (cachedStringRep == null)
+ if (stringRepresentation == null)
{
if (isRoot())
{
- cachedStringRep = SEPARATOR;
+ stringRepresentation = SEPARATOR;
}
else
{
@@ -495,10 +522,10 @@
{
sb.append(SEPARATOR).append(element);
}
- cachedStringRep = sb.toString();
+ stringRepresentation = sb.toString();
}
}
- return cachedStringRep;
+ return stringRepresentation;
}
public void writeExternal(ObjectOutput out) throws IOException
@@ -578,7 +605,7 @@
*
* @return a cached hashcode
*/
- private int _hashCode()
+ protected int calculateHashCode()
{
int hashCode = 0;
int count = 1;
Added: core/trunk/src/main/java/org/jboss/cache/StringFqn.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/StringFqn.java (rev
0)
+++ core/trunk/src/main/java/org/jboss/cache/StringFqn.java 2008-04-22 14:53:02 UTC (rev
5620)
@@ -0,0 +1,86 @@
+package org.jboss.cache;
+
+import java.util.List;
+
+/**
+ * An optimisation of Fqn that does more efficient equals() and hashcode() computations.
This is returned by default when
+ * the factory method {@link Fqn#fromString(String)} is used, or when any of the other
factory methods on {@link Fqn} are
+ * passed only String elements.
+ *
+ * @author Manik Surtani (<a
href="mailto:manik@jboss.org">manik@jboss.org</a>)
+ * @since 2.2.0
+ */
+public class StringFqn extends Fqn
+{
+ protected StringFqn()
+ {
+ super(true);
+ stringRepresentation = SEPARATOR;
+ }
+
+ protected StringFqn(StringFqn base, List<String> elements)
+ {
+ super(true, base, elements);
+ String elementStringRep = getStringRepresentation(elements);
+ stringRepresentation = base.isRoot() ? elementStringRep : base.stringRepresentation
+ elementStringRep;
+ }
+
+ protected StringFqn(StringFqn base, StringFqn relative)
+ {
+ super(true, base, relative.elements);
+ stringRepresentation = base.isRoot() ? relative.stringRepresentation :
base.stringRepresentation + relative.stringRepresentation;
+ }
+
+ protected StringFqn(List<String> elements)
+ {
+ super(true, elements, false);
+ stringRepresentation = getStringRepresentation(elements);
+ }
+
+ private String getStringRepresentation(List<String> elements)
+ {
+ StringBuilder builder = new StringBuilder();
+ for (String e : elements)
+ {
+ builder.append(SEPARATOR);
+ // escape special chars.
+ builder.append(e.replace("\\",
"\\\\").replace("/", "\\/"));
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public boolean equals(Object other)
+ {
+ if (other == this) return true;
+ if (other == null) return false;
+ if (other.getClass().equals(StringFqn.class))
+ {
+ return stringRepresentation.equals(((StringFqn) other).stringRepresentation);
+ }
+ else
+ {
+ return super.equals(other);
+ }
+ }
+
+ @Override
+ protected int calculateHashCode()
+ {
+ return stringRepresentation.hashCode();
+ }
+
+ @Override
+ public boolean isChildOrEquals(Fqn parentFqn)
+ {
+ if (parentFqn.getClass().equals(StringFqn.class))
+ {
+ StringFqn stringParentFqn = (StringFqn) parentFqn;
+ return stringRepresentation.startsWith(stringParentFqn.stringRepresentation);
+ }
+ else
+ {
+ return super.isChildOrEquals(parentFqn);
+ }
+ }
+}
Modified: core/trunk/src/test/java/org/jboss/cache/FqnTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/FqnTest.java 2008-04-22 12:57:24 UTC (rev
5619)
+++ core/trunk/src/test/java/org/jboss/cache/FqnTest.java 2008-04-22 14:53:02 UTC (rev
5620)
@@ -8,11 +8,16 @@
import org.jboss.cache.config.Configuration;
-import org.jgroups.util.Util;
+import org.jboss.cache.marshall.CacheMarshaller210;
+import org.jboss.cache.marshall.Marshaller;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.util.HashMap;
/**
@@ -25,11 +30,13 @@
public class FqnTest
{
private Cache<Object, Object> cache;
+ private Marshaller marshaller;
@BeforeTest
protected void setUp()
{
cache = null;
+ marshaller = new CacheMarshaller210();
}
@AfterTest
@@ -341,8 +348,13 @@
@SuppressWarnings("unchecked")
<T> Fqn<T> marshalAndUnmarshal(Fqn<T> fqn) throws Exception
{
- byte[] buf = Util.objectToByteBuffer(fqn);
- return (Fqn<T>) Util.objectFromByteBuffer(buf);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream out = new ObjectOutputStream(baos);
+ marshaller.objectToObjectStream(fqn, out);
+ out.close();
+ baos.close();
+ ObjectInputStream in = new ObjectInputStream(new
ByteArrayInputStream(baos.toByteArray()));
+ return (Fqn<T>) marshaller.objectFromObjectStream(in);
}
// testing generics
@@ -393,6 +405,7 @@
// }
//
+
public void testGenerations()
{
Fqn<Integer> f = Fqn.fromElements(1, 2, 3, 4, 5, 6, 7);
@@ -433,4 +446,24 @@
{
System.out.println("-- " + msg);
}
+
+ public void testUnescapedString()
+ {
+ Fqn f1 = Fqn.fromString("/a/b/c/d"); // an Fqn containing elements
"a", "b", "c", "d".
+ Fqn f2 = Fqn.fromElements("a", "b", "c",
"d");
+
+ assert f1.equals(f2);
+ assert f2.equals(f1);
+
+ Fqn f3 = Fqn.fromElements("a", "b/c", "d");
+
+ assert !f1.equals(f3);
+ assert !f2.equals(f3);
+ assert !f3.equals(f1);
+ assert !f3.equals(f2);
+
+ System.out.println("F1: " + f1);
+ System.out.println("F2: " + f2);
+ System.out.println("F3: " + f3);
+ }
}