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<String> is stored, and not based on ordinal.
This commit is contained in:
@@ -26,14 +26,17 @@ import java.lang.reflect.Type;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.Interner;
|
import com.google.common.collect.Interner;
|
||||||
import com.google.common.collect.Interners;
|
import com.google.common.collect.Interners;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Sets;
|
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.Column;
|
||||||
import com.samskivert.depot.annotation.Transform;
|
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
|
* Contains various generally useful {@link Transformer} implementations. To use a transformer, you
|
||||||
* specify it via a {@link Column} annotation. For example:
|
* specify it via a {@link Column} annotation. For example:
|
||||||
@@ -97,11 +102,11 @@ public class Transformers
|
|||||||
return Arrays.asList(value);
|
return Arrays.asList(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected Builder<String[]> createBuilder (String encoded)
|
@Override protected Builder<String, String[]> createBuilder (String encoded)
|
||||||
{
|
{
|
||||||
// jog through and count the elements so that we can populate the array directly
|
// jog through and count the elements so that we can populate the array directly
|
||||||
final String[] result = new String[countElements(encoded)];
|
final String[] result = new String[countElements(encoded)];
|
||||||
return new Builder<String[]>() {
|
return new Builder<String, String[]>() {
|
||||||
public void add (String s) {
|
public void add (String s) {
|
||||||
result[idx++] = s;
|
result[idx++] = s;
|
||||||
}
|
}
|
||||||
@@ -126,53 +131,138 @@ public class Transformers
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected Builder<Iterable<String>> createBuilder (String encoded)
|
@Override protected Builder<String, Iterable<String>> createBuilder (String encoded)
|
||||||
{
|
{
|
||||||
Collection<String> adder;
|
return createCollectionBuilder(_ftype, String.class, _immutable, _intern);
|
||||||
Collection<String> 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<String> set = Sets.newHashSet();
|
|
||||||
adder = set;
|
|
||||||
if (_immutable && (clazz == Set.class)) {
|
|
||||||
retval = Collections.unmodifiableSet(set);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (clazz == LinkedList.class) {
|
|
||||||
adder = Lists.newLinkedList();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
List<String> 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<Iterable<String>> createBuilder (
|
|
||||||
final Collection<String> adder, final Collection<String> retval)
|
|
||||||
{
|
|
||||||
Preconditions.checkNotNull(adder);
|
|
||||||
return new Builder<Iterable<String>>() {
|
|
||||||
public void add (String s) {
|
|
||||||
adder.add(s);
|
|
||||||
}
|
|
||||||
public Iterable<String> build () {
|
|
||||||
return (_immutable && _intern && (adder != retval))
|
|
||||||
? INTERNER.intern(retval)
|
|
||||||
: retval;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Transformer for anything that is an Iterable<Enum>.
|
||||||
|
* Often used for Enum sets.
|
||||||
|
*/
|
||||||
|
public static class EnumIterable<E extends Enum<E>> extends StringBase<Iterable<E>>
|
||||||
|
{
|
||||||
|
@Override @SuppressWarnings("unchecked")
|
||||||
|
public void init (Type fieldType, Transform annotation)
|
||||||
|
{
|
||||||
|
super.init(fieldType, annotation);
|
||||||
|
_eclass = (Class<E>) ((ParameterizedType) fieldType).getActualTypeArguments()[0];
|
||||||
|
_internStrings = false; // don't waste time interning strings
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override protected Iterable<String> asIterable (Iterable<E> value)
|
||||||
|
{
|
||||||
|
return Iterables.transform(value, new Function<E, String>() {
|
||||||
|
public String apply (E val) {
|
||||||
|
return (val == null) ? null : val.name();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override protected Builder<String, Iterable<E>> createBuilder (String encoded)
|
||||||
|
{
|
||||||
|
// create a Builder for our field type
|
||||||
|
final Builder<E, Iterable<E>> ebuilder = createCollectionBuilder(
|
||||||
|
_ftype, _eclass, _immutable, _intern);
|
||||||
|
// wrap that builder in one that accepts String elements
|
||||||
|
return new Builder<String, Iterable<E>>() {
|
||||||
|
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<E> build () {
|
||||||
|
return ebuilder.build();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Class<E> _eclass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface used by some of these transformers to build their result.
|
||||||
|
*/
|
||||||
|
protected interface Builder<E, B>
|
||||||
|
{
|
||||||
|
void add (E element);
|
||||||
|
|
||||||
|
B build ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a builder that populates a collection.
|
||||||
|
*/
|
||||||
|
protected static <E> Builder<E, Iterable<E>> createCollectionBuilder (
|
||||||
|
Type fieldType, Class<E> elementType, boolean immutable, boolean intern)
|
||||||
|
{
|
||||||
|
Collection<E> adder;
|
||||||
|
Collection<E> 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<E> set;
|
||||||
|
if (clazz == HashSet.class || !elementType.isEnum()) {
|
||||||
|
set = Sets.newHashSet();
|
||||||
|
} else {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Class<Enum> eclazz = (Class<Enum>)elementType;
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Set<E> eSet = (Set<E>)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<E> 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<E> fadder = adder;
|
||||||
|
final Collection<E> fretval = (retval == null) ? adder : retval;
|
||||||
|
final boolean fintern = (retval != null) && immutable && intern;
|
||||||
|
return new Builder<E, Iterable<E>>() {
|
||||||
|
public void add (E element) {
|
||||||
|
fadder.add(element);
|
||||||
|
}
|
||||||
|
public Iterable<E> build () {
|
||||||
|
if (fintern) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Iterable<E> built = (Iterable<E>) INTERNER.intern(fretval);
|
||||||
|
return built;
|
||||||
|
}
|
||||||
|
return fretval;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Building-block used to create other Transformers.
|
||||||
|
*/
|
||||||
protected abstract static class StringBase<F> extends Transformer<F, String>
|
protected abstract static class StringBase<F> extends Transformer<F, String>
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
@@ -180,7 +270,7 @@ public class Transformers
|
|||||||
{
|
{
|
||||||
_ftype = fieldType;
|
_ftype = fieldType;
|
||||||
_immutable = annotation.immutable();
|
_immutable = annotation.immutable();
|
||||||
_intern = annotation.intern();
|
_internStrings = _intern = annotation.intern();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -210,14 +300,14 @@ public class Transformers
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Builder<F> builder = createBuilder(encoded);
|
Builder<String, F> builder = createBuilder(encoded);
|
||||||
StringBuilder buf = new StringBuilder(encoded.length());
|
StringBuilder buf = new StringBuilder(encoded.length());
|
||||||
for (int ii = 0, nn = encoded.length(); ii < nn; ii++) {
|
for (int ii = 0, nn = encoded.length(); ii < nn; ii++) {
|
||||||
char c = encoded.charAt(ii);
|
char c = encoded.charAt(ii);
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '\n':
|
case '\n':
|
||||||
String s = buf.toString();
|
String s = buf.toString();
|
||||||
builder.add(_intern ? s.intern() : s);
|
builder.add(_internStrings ? s.intern() : s);
|
||||||
buf.setLength(0);
|
buf.setLength(0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -250,7 +340,7 @@ public class Transformers
|
|||||||
|
|
||||||
protected abstract Iterable<String> asIterable (F value);
|
protected abstract Iterable<String> asIterable (F value);
|
||||||
|
|
||||||
protected abstract Builder<F> createBuilder (String encoded);
|
protected abstract Builder<String, F> createBuilder (String encoded);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility tount the number of elements in the encoded non-null string.
|
* Utility tount the number of elements in the encoded non-null string.
|
||||||
@@ -262,13 +352,6 @@ public class Transformers
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected interface Builder<F>
|
|
||||||
{
|
|
||||||
void add (String s);
|
|
||||||
|
|
||||||
F build ();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Type _ftype;
|
protected Type _ftype;
|
||||||
|
|
||||||
/** Immutable hint. */
|
/** Immutable hint. */
|
||||||
@@ -277,7 +360,10 @@ public class Transformers
|
|||||||
/** Interning hint.*/
|
/** Interning hint.*/
|
||||||
protected boolean _intern;
|
protected boolean _intern;
|
||||||
|
|
||||||
/** The interner we use for <em>immutable</em> values. */
|
/** Do we intern the actual strings? */
|
||||||
protected static final Interner<Iterable<String>> INTERNER = Interners.newWeakInterner();
|
protected boolean _internStrings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The interner we use for <em>immutable</em> values. */
|
||||||
|
protected static final Interner<Object> INTERNER = Interners.newWeakInterner();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user