diff --git a/src/java/com/samskivert/depot/Transformers.java b/src/java/com/samskivert/depot/Transformers.java index 45c3ac4..e24d9a9 100644 --- a/src/java/com/samskivert/depot/Transformers.java +++ b/src/java/com/samskivert/depot/Transformers.java @@ -20,8 +20,24 @@ package com.samskivert.depot; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + import com.samskivert.depot.annotation.Column; +//import com.google.common.annotations.Beta; + +import com.google.common.base.Preconditions; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + /** * Contains various generally useful {@link Transformer} implementations. To use a transformer, you * specify it via a {@link Column} annotation. For example: @@ -57,4 +73,109 @@ public class Transformers return values; } } + + //@Beta + public static class StringArray implements Transformer + { + public String toPersistent (String[] value) + { + if (value == null) { + return null; + } + return StringIterable.toPersistent0(Arrays.asList(value)); + } + + public String[] fromPersistent (Class ftype, String encoded) + { + if (encoded == null) { + return null; + } + return Iterables.toArray(StringIterable.fromPersistent0(encoded), String.class); + } + } + + //@Beta + public static class StringIterable implements Transformer, String> + { + public String toPersistent (Iterable value) + { + return toPersistent0(value); + } + + public Iterable fromPersistent (Class ftype, String encoded) + { + ArrayList value = fromPersistent0(encoded); + if (value == null || + ftype == ArrayList.class || ftype == List.class || ftype == Collection.class || + ftype == Iterable.class) { + return value; + } + if (ftype == LinkedList.class) { + return Lists.newLinkedList(value); + } + if (ftype == HashSet.class || ftype == 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; + } + + protected static String toPersistent0 (Iterable value) + { + if (value == null) { + return null; + } + StringBuilder buf = new StringBuilder(); + for (String s : value) { + if (s == null) { + buf.append('\uFFFC'); + } else { + s = s.replace("\\", "\\\\"); // turn \ into \\ + s = s.replace("\n", "\\n"); // turn newline into \n + s = s.replace("\uFFFC", "\\\uFFFC"); // preslash the nullmarker too + buf.append(s).append('\n'); + } + } + return buf.toString(); + } + + protected static ArrayList fromPersistent0 (String encoded) + { + if (encoded == null) { + return null; + } + ArrayList value = Lists.newArrayList(); + StringBuilder buf = new StringBuilder(encoded.length()); + for (int ii = 0, nn = encoded.length(); ii < nn; ii++) { + char c = encoded.charAt(ii); + switch (c) { + case '\uFFFC': + Preconditions.checkArgument(buf.length() == 0, "Invalid encoded string"); + value.add(null); + break; + + case '\n': + value.add(buf.toString()); // TODO: intern? + buf.setLength(0); + break; + + case '\\': + try { + buf.append(encoded.charAt(++ii)); // take the next char at face value + } catch (IndexOutOfBoundsException e) { + throw new IllegalArgumentException("Invalid encoded string", e); + } + break; + + default: + buf.append(c); + break; + } + } + // make sure there are no left-over characters + Preconditions.checkArgument(buf.length() == 0, "Invalid encoded string"); + return value; + } + } }