From b32c6753c114a830c4f1be5e7c217f65f4d718f9 Mon Sep 17 00:00:00 2001 From: Ray Greenwell Date: Mon, 17 Jan 2011 22:20:25 +0000 Subject: [PATCH] Added EnumIterable, which can be used to transform an Iterable containing Enums. It extends StringBase, so the enums are stored similarly to how a Set is stored, and not based on ordinal. --- .../com/samskivert/depot/Transformers.java | 202 +++++++++++++----- 1 file changed, 144 insertions(+), 58 deletions(-) diff --git a/src/main/java/com/samskivert/depot/Transformers.java b/src/main/java/com/samskivert/depot/Transformers.java index a00ac9f..0306cd1 100644 --- a/src/main/java/com/samskivert/depot/Transformers.java +++ b/src/main/java/com/samskivert/depot/Transformers.java @@ -26,14 +26,17 @@ import java.lang.reflect.Type; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.EnumSet; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; +import com.google.common.base.Function; import com.google.common.base.Preconditions; 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; @@ -43,6 +46,8 @@ import com.samskivert.util.ByteEnumUtil; import com.samskivert.depot.annotation.Column; import com.samskivert.depot.annotation.Transform; +import static com.samskivert.depot.Log.log; + /** * Contains various generally useful {@link Transformer} implementations. To use a transformer, you * specify it via a {@link Column} annotation. For example: @@ -97,11 +102,11 @@ public class Transformers return Arrays.asList(value); } - @Override protected Builder createBuilder (String encoded) + @Override protected Builder createBuilder (String encoded) { // jog through and count the elements so that we can populate the array directly final String[] result = new String[countElements(encoded)]; - return new Builder() { + return new Builder() { public void add (String s) { result[idx++] = s; } @@ -126,53 +131,138 @@ public class Transformers return value; } - @Override protected Builder> createBuilder (String encoded) + @Override protected Builder> createBuilder (String encoded) { - Collection adder; - Collection retval = null; - Type clazz = (_ftype instanceof ParameterizedType) ? - ((ParameterizedType)_ftype).getRawType() : _ftype; - // TODO: fill out the collection types - if (clazz == HashSet.class || clazz == Set.class) { - Set set = Sets.newHashSet(); - adder = set; - if (_immutable && (clazz == Set.class)) { - retval = Collections.unmodifiableSet(set); - } - - } else if (clazz == LinkedList.class) { - adder = Lists.newLinkedList(); - - } else { - List list = Lists.newArrayList(); - adder = list; - if (_immutable && - ((clazz == List.class) || - (clazz == Iterable.class) || - (clazz == Collection.class))) { - retval = Collections.unmodifiableList(list); - } - } - return createBuilder(adder, (retval == null) ? adder : retval); - } - - protected Builder> createBuilder ( - final Collection adder, final Collection retval) - { - Preconditions.checkNotNull(adder); - return new Builder>() { - public void add (String s) { - adder.add(s); - } - public Iterable build () { - return (_immutable && _intern && (adder != retval)) - ? INTERNER.intern(retval) - : retval; - } - }; + return createCollectionBuilder(_ftype, String.class, _immutable, _intern); } } + /** + * A Transformer for anything that is an Iterable. + * Often used for Enum sets. + */ + public static class EnumIterable> extends StringBase> + { + @Override @SuppressWarnings("unchecked") + public void init (Type fieldType, Transform annotation) + { + super.init(fieldType, annotation); + _eclass = (Class) ((ParameterizedType) fieldType).getActualTypeArguments()[0]; + _internStrings = false; // don't waste time interning strings + } + + @Override protected Iterable asIterable (Iterable value) + { + return Iterables.transform(value, new Function() { + public String apply (E val) { + return (val == null) ? null : val.name(); + } + }); + } + + @Override protected Builder> createBuilder (String encoded) + { + // create a Builder for our field type + final Builder> ebuilder = createCollectionBuilder( + _ftype, _eclass, _immutable, _intern); + // wrap that builder in one that accepts String elements + return new Builder>() { + public void add (String s) { + if (s == null) { + ebuilder.add(null); + return; + } + E value; + try { + value = Enum.valueOf(_eclass, s); + } catch (IllegalArgumentException iae) { + log.warning("Invalid enum cannot be unpersisted", "e", s, iae); + return; + } + ebuilder.add(value); + } + public Iterable build () { + return ebuilder.build(); + } + }; + } + + protected Class _eclass; + } + + /** + * An interface used by some of these transformers to build their result. + */ + protected interface Builder + { + void add (E element); + + B build (); + } + + /** + * Create a builder that populates a collection. + */ + protected static Builder> createCollectionBuilder ( + Type fieldType, Class elementType, boolean immutable, boolean intern) + { + Collection adder; + Collection retval = null; + Type clazz = (fieldType instanceof ParameterizedType) ? + ((ParameterizedType)fieldType).getRawType() : fieldType; + // TODO: fill out the collection types + if (clazz == HashSet.class || clazz == Set.class || clazz == EnumSet.class) { + Set set; + if (clazz == HashSet.class || !elementType.isEnum()) { + set = Sets.newHashSet(); + } else { + @SuppressWarnings("unchecked") + Class eclazz = (Class)elementType; + @SuppressWarnings("unchecked") + Set eSet = (Set)EnumSet.noneOf(eclazz); + set = eSet; + } + adder = set; + if (immutable && (clazz == Set.class)) { + retval = Collections.unmodifiableSet(set); + } + + } else if (clazz == LinkedList.class) { + adder = Lists.newLinkedList(); + + } else { + List list = Lists.newArrayList(); + adder = list; + if (immutable && + ((clazz == List.class) || + (clazz == Iterable.class) || + (clazz == Collection.class))) { + retval = Collections.unmodifiableList(list); + } + } + + // ok, now we're ready + final Collection fadder = adder; + final Collection fretval = (retval == null) ? adder : retval; + final boolean fintern = (retval != null) && immutable && intern; + return new Builder>() { + public void add (E element) { + fadder.add(element); + } + public Iterable build () { + if (fintern) { + @SuppressWarnings("unchecked") + Iterable built = (Iterable) INTERNER.intern(fretval); + return built; + } + return fretval; + } + }; + } + + /** + * Building-block used to create other Transformers. + */ protected abstract static class StringBase extends Transformer { @Override @@ -180,7 +270,7 @@ public class Transformers { _ftype = fieldType; _immutable = annotation.immutable(); - _intern = annotation.intern(); + _internStrings = _intern = annotation.intern(); } @Override @@ -210,14 +300,14 @@ public class Transformers return null; } - Builder builder = createBuilder(encoded); + Builder builder = createBuilder(encoded); 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': String s = buf.toString(); - builder.add(_intern ? s.intern() : s); + builder.add(_internStrings ? s.intern() : s); buf.setLength(0); break; @@ -250,7 +340,7 @@ public class Transformers protected abstract Iterable asIterable (F value); - protected abstract Builder createBuilder (String encoded); + protected abstract Builder createBuilder (String encoded); /** * Utility tount the number of elements in the encoded non-null string. @@ -262,13 +352,6 @@ public class Transformers return count; } - protected interface Builder - { - void add (String s); - - F build (); - } - protected Type _ftype; /** Immutable hint. */ @@ -277,7 +360,10 @@ public class Transformers /** Interning hint.*/ protected boolean _intern; - /** The interner we use for immutable values. */ - protected static final Interner> INTERNER = Interners.newWeakInterner(); + /** Do we intern the actual strings? */ + protected boolean _internStrings; } + + /** The interner we use for immutable values. */ + protected static final Interner INTERNER = Interners.newWeakInterner(); }