From 89bec13aa2458e6010faeb549babf7a0856cafd6 Mon Sep 17 00:00:00 2001 From: Ray Greenwell Date: Thu, 30 Sep 2010 21:30:59 +0000 Subject: [PATCH] Added Transformers that will always return immutable collections, interned Strings, or both. I may rework this into attributes that can be specified for any @Transformer annotation, and leave it up to each Transformer whether they are honored (and how). --- .../com/samskivert/depot/Transformers.java | 177 ++++++++++++++---- 1 file changed, 141 insertions(+), 36 deletions(-) diff --git a/src/main/java/com/samskivert/depot/Transformers.java b/src/main/java/com/samskivert/depot/Transformers.java index 16412e7..7d91fb8 100644 --- a/src/main/java/com/samskivert/depot/Transformers.java +++ b/src/main/java/com/samskivert/depot/Transformers.java @@ -38,6 +38,10 @@ import com.samskivert.depot.annotation.Column; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Interner; +import com.google.common.collect.Interners; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @@ -62,14 +66,23 @@ public class Transformers */ public static class StringArray extends StringBase { - public String toPersistent (String[] value) + protected Iterable toIterable (String[] value) { - return (value == null) ? null : encode(Arrays.asList(value)); + return Arrays.asList(value); } - public String[] fromPersistent (Type ftype, String encoded) + protected Builder createBuilder (Type ftype, String encoded) { - return (encoded == null) ? null : Iterables.toArray(decode(encoded), String.class); + final String[] result = new String[countElements(encoded)]; + return new Builder() { + public void add (String s) { + result[idx++] = s; + } + public String[] build () { + return result; + } + protected int idx = 0; + }; } } @@ -79,36 +92,85 @@ public class Transformers * backslash, newlines will be encoded as "\n", and null elements will be encoded as "\0" (but * not terminated by a newline). */ - public static class StringIterable extends StringBase> + public static class StringIterable extends StringIterableBase { - public String toPersistent (Iterable value) + protected Builder> createBuilder (Type ftype, String encoded) { - return (value == null) ? null : encode(value); - } - - public Iterable fromPersistent (Type ftype, String encoded) - { - if (encoded == null) { - return null; - } - - ArrayList value = decode(encoded); Type fclass = (ftype instanceof ParameterizedType) ? ((ParameterizedType)ftype).getRawType() : ftype; - if (fclass == ArrayList.class || fclass == List.class || - fclass == Collection.class || fclass == Iterable.class) { - return value; - } - if (fclass == LinkedList.class) { - return Lists.newLinkedList(value); - } - if (fclass == HashSet.class || fclass == Set.class) { - return Sets.newHashSet(value); - } - // else: reflection? See if it's a collection, call the 0-arg constructor, add all - // and return? Something? - return value; + final Collection collection = createCollection(fclass, encoded); + return new Builder>() { + public void add (String s) { + collection.add(s); + } + public Iterable build () { + return collection; + } + }; } + + protected Collection createCollection (Type fclass, String encoded) + { + // TODO: TreeSet, etc, etc + if (fclass == HashSet.class || fclass == Set.class) { + return Sets.newHashSet(); + + } else if (fclass == LinkedList.class) { + return Lists.newLinkedList(); + + } else { + return Lists.newArrayList(); + } + } + } + + public static class ImmutableStringIterable extends StringIterableBase + { + protected Builder> createBuilder (Type ftype, String encoded) + { + Type fclass = (ftype instanceof ParameterizedType) ? + ((ParameterizedType)ftype).getRawType() : ftype; + // TODO: SortedSet + if (fclass == Set.class) { + return new Builder>() { + public void add (String s) { + _builder.add(s); + } + public Iterable build () { + return _builder.build(); + } + protected ImmutableSet.Builder _builder = ImmutableSet.builder(); + }; + + } else { //if (fclass == List.class) + return new Builder>() { + public void add (String s) { + _builder.add(s); + } + public Iterable build () { + return _builder.build(); + } + protected ImmutableList.Builder _builder = ImmutableList.builder(); + }; + } + } + } + + public static class InternedImmutableStringIterable extends ImmutableStringIterable + { + @Override + public Iterable fromPersistent (Type ftype, String encoded) + { + Iterable result = super.fromPersistent(ftype, encoded); + return (result == null) ? result : INTERNER.intern(result); + } + + @Override protected boolean doInterning () + { + return true; + } + + protected static final Interner> INTERNER = Interners.newWeakInterner(); } public static class ByteEnumSet & ByteEnum> @@ -132,10 +194,14 @@ public class Transformers protected abstract static class StringBase implements Transformer { - protected static String encode (Iterable value) + public String toPersistent (F value) { + if (value == null) { + return null; + } + StringBuilder buf = new StringBuilder(); - for (String s : value) { + for (String s : toIterable(value)) { if (s == null) { buf.append("\\\n"); // encode nulls as slash followed by the terminator } else { @@ -147,15 +213,21 @@ public class Transformers return buf.toString(); } - protected static ArrayList decode (String encoded) + public F fromPersistent (Type ftype, String encoded) { - ArrayList value = Lists.newArrayList(); + if (encoded == null) { + return null; + } + + Builder builder = createBuilder(ftype, encoded); + boolean intern = doInterning(); StringBuilder buf = new StringBuilder(encoded.length()); for (int ii = 0, nn = encoded.length(); ii < nn; ii++) { char c = encoded.charAt(ii); switch (c) { case '\n': - value.add(buf.toString()); // TODO: intern? + String s = buf.toString(); + builder.add(intern ? s.intern() : s); buf.setLength(0); break; @@ -165,7 +237,7 @@ public class Transformers switch (slashed) { case '\n': // turn back into a null element Preconditions.checkArgument(buf.length() == 0, "Invalid encoded string"); - value.add(null); + builder.add(null); break; case 'n': // turn \n back into a newline buf.append('\n'); @@ -181,9 +253,42 @@ public class Transformers break; } } - // make sure the last element was terminated Preconditions.checkArgument(buf.length() == 0, "Invalid encoded string"); + return builder.build(); + } + + protected boolean doInterning () + { + return false; + } + + protected abstract Iterable toIterable (F value); + + protected abstract Builder createBuilder (Type ftype, String encoded); + + /** + * Count the number of elements in the encoded non-null string. + */ + protected static int countElements (String encoded) + { + int count = 0; + for (int pos = 0; 0 != (pos = 1 + encoded.indexOf('\n', pos)); count++) {} + return count; + } + + protected interface Builder + { + void add (String s); + + F build (); + } + } + + protected static abstract class StringIterableBase extends StringBase> + { + protected Iterable toIterable (Iterable value) + { return value; } }