diff --git a/src/main/java/lsh/ext/gson/ITypeAdapterFactory.java b/src/main/java/lsh/ext/gson/ITypeAdapterFactory.java index 949f33b7..f4f03446 100644 --- a/src/main/java/lsh/ext/gson/ITypeAdapterFactory.java +++ b/src/main/java/lsh/ext/gson/ITypeAdapterFactory.java @@ -25,6 +25,8 @@ interface ITypeResolver { TypeAdapter getTypeAdapter(int index); + TypeAdapter getTypeAdapterForClass(Class klass); + } @SuppressWarnings("NewClassNamingConvention") @@ -58,6 +60,11 @@ public TypeAdapter getTypeAdapter(final int index) { final TypeToken o = (TypeToken) TypeToken.get(typeArgument); return gson.getAdapter(o); } + + @Override + public TypeAdapter getTypeAdapterForClass(final Class klass) { + return gson.getAdapter(klass); + } }; @SuppressWarnings("unchecked") final TypeAdapter castTypeAdapter = (TypeAdapter) createTypeAdapter.apply(typeResolver); @@ -97,6 +104,11 @@ public TypeAdapter getTypeAdapter(final int index) { final TypeToken o = (TypeToken) TypeToken.get(typeArgument); return gson.getAdapter(o); } + + @Override + public TypeAdapter getTypeAdapterForClass(final Class klass) { + return gson.getAdapter(klass); + } }; @SuppressWarnings("unchecked") final TypeAdapter castTypeAdapter = (TypeAdapter) createTypeAdapter.apply(typeResolver); diff --git a/src/main/java/lsh/ext/gson/ext/org/apache/commons/collections4/ApacheCommonsCollections4TypeAdapter.java b/src/main/java/lsh/ext/gson/ext/org/apache/commons/collections4/ApacheCommonsCollections4TypeAdapter.java index 60667af3..7b9c2f28 100644 --- a/src/main/java/lsh/ext/gson/ext/org/apache/commons/collections4/ApacheCommonsCollections4TypeAdapter.java +++ b/src/main/java/lsh/ext/gson/ext/org/apache/commons/collections4/ApacheCommonsCollections4TypeAdapter.java @@ -1,5 +1,6 @@ package lsh.ext.gson.ext.org.apache.commons.collections4; +import java.util.AbstractMap; import java.util.Map; import java.util.function.Function; import java.util.function.Supplier; @@ -12,6 +13,7 @@ import lsh.ext.gson.IBuilder2; import org.apache.commons.collections4.Bag; import org.apache.commons.collections4.BidiMap; +import org.apache.commons.collections4.IteratorUtils; import org.apache.commons.collections4.MultiSet; import org.apache.commons.collections4.MultiValuedMap; @@ -19,11 +21,37 @@ public final class ApacheCommonsCollections4TypeAdapter { public static TypeAdapter> forBag( + final TypeAdapter typeAdapter, + final Supplier>> builderFactory + ) { + return Container1TypeAdapter.getInstance(typeAdapter, Bag::iterator, builderFactory); + } + + public static TypeAdapter> forBagNCopies( + final TypeAdapter integerTypeAdapter, + final Supplier>> builderFactory + ) { + return forBagNCopies(integerTypeAdapter, builderFactory, Function.identity(), Function.identity()); + } + + public static TypeAdapter> forBagNCopies( + final TypeAdapter integerTypeAdapter, final Supplier>> builderFactory, final Function toName, final Function fromName ) { - return BagTypeAdapter.getInstance(builderFactory, toName, fromName); + return Container2TypeAdapter., Map.Entry>getInstance( + integerTypeAdapter, + es -> IteratorUtils.transformedIterator( + es.uniqueSet().iterator(), + e -> new AbstractMap.SimpleImmutableEntry<>(toName.apply(e), es.getCount(e)) + ), + e -> fromName.apply(e.getKey()), + Map.Entry::getValue, + toName, + fromName, + builderFactory + ); } public static TypeAdapter> forBidiMap( diff --git a/src/main/java/lsh/ext/gson/ext/org/apache/commons/collections4/ApacheCommonsCollections4TypeAdapterFactory.java b/src/main/java/lsh/ext/gson/ext/org/apache/commons/collections4/ApacheCommonsCollections4TypeAdapterFactory.java index a6d19a59..83b68789 100644 --- a/src/main/java/lsh/ext/gson/ext/org/apache/commons/collections4/ApacheCommonsCollections4TypeAdapterFactory.java +++ b/src/main/java/lsh/ext/gson/ext/org/apache/commons/collections4/ApacheCommonsCollections4TypeAdapterFactory.java @@ -4,6 +4,7 @@ import java.util.function.Supplier; import com.google.common.collect.BiMap; +import com.google.gson.TypeAdapter; import com.google.gson.reflect.TypeToken; import lombok.experimental.UtilityClass; import lsh.ext.gson.IBuilder1; @@ -36,7 +37,18 @@ public static ITypeAdapterFactory> forBag( final Function toKey, final Function fromKey ) { - return ITypeAdapterFactory.forClassHierarchy(Bag.class, typeResolver -> ApacheCommonsCollections4TypeAdapter.forBag(lookup.lookup(typeResolver.getTypeToken()), toKey, fromKey)); + return ITypeAdapterFactory.forClassHierarchy( + Bag.class, + typeResolver -> { + final TypeAdapter integerTypeAdapter = typeResolver.getTypeAdapterForClass(int.class); + return ApacheCommonsCollections4TypeAdapter.forBagNCopies( + integerTypeAdapter, + lookup.lookup(typeResolver.getTypeToken()), + toKey, + fromKey + ); + } + ); } // TODO handle all known implementations @@ -44,9 +56,9 @@ public static Supplier>> defaultBuilderForBag(f @SuppressWarnings("unchecked") final Class> rawType = (Class>) typeToken.getRawType(); if ( HashBag.class.isAssignableFrom(rawType) ) { - return () -> Builder.forBag(new HashBag<>()); + return () -> Builder.forBagNCopies(HashBag::new); } - return () -> Builder.forBag(new HashBag<>()); + return () -> Builder.forBagNCopies(HashBag::new); } public static final ITypeAdapterFactory> defaultForBidiMap = forBidiMap(ApacheCommonsCollections4TypeAdapterFactory::defaultBuilderForBidiMap); @@ -117,12 +129,12 @@ public static Supplier>> defa @SuppressWarnings("unchecked") final Class> rawType = (Class>) typeToken.getRawType(); if ( ArrayListValuedHashMap.class.isAssignableFrom(rawType) ) { - return () -> Builder.forMultiValuedMap(new ArrayListValuedHashMap<>()); + return () -> Builder.forMultiValuedMap(ArrayListValuedHashMap::new); } if ( HashSetValuedHashMap.class.isAssignableFrom(rawType) ) { - return () -> Builder.forMultiValuedMap(new HashSetValuedHashMap<>()); + return () -> Builder.forMultiValuedMap(HashSetValuedHashMap::new); } - return () -> Builder.forMultiValuedMap(new ArrayListValuedHashMap<>()); + return () -> Builder.forMultiValuedMap(ArrayListValuedHashMap::new); } } diff --git a/src/main/java/lsh/ext/gson/ext/org/apache/commons/collections4/BagTypeAdapter.java b/src/main/java/lsh/ext/gson/ext/org/apache/commons/collections4/BagTypeAdapter.java deleted file mode 100644 index 33d2aade..00000000 --- a/src/main/java/lsh/ext/gson/ext/org/apache/commons/collections4/BagTypeAdapter.java +++ /dev/null @@ -1,55 +0,0 @@ -package lsh.ext.gson.ext.org.apache.commons.collections4; - -import java.io.IOException; -import java.util.function.Function; -import java.util.function.Supplier; - -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lsh.ext.gson.IBuilder2; -import org.apache.commons.collections4.Bag; - -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) -final class BagTypeAdapter - extends TypeAdapter> { - - private final Supplier>> builderFactory; - private final Function toKey; - private final Function fromKey; - - static TypeAdapter> getInstance( - final Supplier>> builderFactory, - final Function toKey, - final Function fromKey - ) { - return new BagTypeAdapter<>(builderFactory, toKey, fromKey) - .nullSafe(); - } - - @Override - public void write(final JsonWriter out, final Bag bag) - throws IOException { - out.beginObject(); - for ( final E e : bag.uniqueSet() ) { - out.name(toKey.apply(e)); - out.value(bag.getCount(e)); - } - out.endObject(); - } - - @Override - public Bag read(final JsonReader in) - throws IOException { - in.beginObject(); - final IBuilder2> builder = builderFactory.get(); - while ( in.hasNext() ) { - builder.accept(fromKey.apply(in.nextName()), in.nextInt()); - } - in.endObject(); - return builder.build(); - } - -} diff --git a/src/main/java/lsh/ext/gson/ext/org/apache/commons/collections4/Builder.java b/src/main/java/lsh/ext/gson/ext/org/apache/commons/collections4/Builder.java index 4fefd19f..4ef51047 100644 --- a/src/main/java/lsh/ext/gson/ext/org/apache/commons/collections4/Builder.java +++ b/src/main/java/lsh/ext/gson/ext/org/apache/commons/collections4/Builder.java @@ -1,6 +1,9 @@ package lsh.ext.gson.ext.org.apache.commons.collections4; +import java.util.function.Supplier; + import lombok.experimental.UtilityClass; +import lsh.ext.gson.IBuilder1; import lsh.ext.gson.IBuilder2; import org.apache.commons.collections4.Bag; import org.apache.commons.collections4.MultiValuedMap; @@ -8,32 +11,16 @@ @UtilityClass public final class Builder { - public static > IBuilder2 forBag(final B bag) { - return new IBuilder2<>() { - @Override - public void accept(final E e, final Integer n) { - bag.add(e, n); - } - - @Override - public B build() { - return bag; - } - }; + public static > IBuilder1 forBag(final Supplier create) { + return IBuilder1.of(create, (e, bag) -> bag.add(e)); } - public static > IBuilder2 forMultiValuedMap(final M multiValuedMap) { - return new IBuilder2<>() { - @Override - public void accept(final K k, final V v) { - multiValuedMap.put(k, v); - } + public static > IBuilder2 forBagNCopies(final Supplier create) { + return IBuilder2.of(create, (e, nCopies, bag) -> bag.add(e, nCopies)); + } - @Override - public M build() { - return multiValuedMap; - } - }; + public static > IBuilder2 forMultiValuedMap(final Supplier create) { + return IBuilder2.of(create, (k, v, map) -> map.put(k, v)); } } diff --git a/src/test/java/lsh/ext/gson/ext/org/apache/commons/collections4/ApacheCommonsCollections4TypeAdapterTest.java b/src/test/java/lsh/ext/gson/ext/org/apache/commons/collections4/ApacheCommonsCollections4TypeAdapterTest.java new file mode 100644 index 00000000..3456a8d9 --- /dev/null +++ b/src/test/java/lsh/ext/gson/ext/org/apache/commons/collections4/ApacheCommonsCollections4TypeAdapterTest.java @@ -0,0 +1,57 @@ +package lsh.ext.gson.ext.org.apache.commons.collections4; + +import java.io.IOException; +import java.util.function.Function; + +import com.google.gson.TypeAdapter; +import lsh.ext.gson.IBuilder2; +import lsh.ext.gson.test.TestTypeAdapters; +import org.apache.commons.collections4.Bag; +import org.apache.commons.collections4.bag.TreeBag; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public final class ApacheCommonsCollections4TypeAdapterTest { + + @Test + public void testForBagRoundTrip() + throws IOException { + final TypeAdapter> unit = ApacheCommonsCollections4TypeAdapter.forBag( + TestTypeAdapters.stringTypeAdapter, + () -> Builder.forBag(TreeBag::new) + ); + final Bag before = new TreeBag<>(); + before.add("foo"); + before.add("bar"); + before.add("bar"); + before.add("baz"); + before.add("baz"); + before.add("baz"); + final String json = unit.toJson(before); + Assertions.assertEquals("[\"bar\",\"bar\",\"baz\",\"baz\",\"baz\",\"foo\"]", json); + final Bag actual = unit.fromJson(json); + Assertions.assertIterableEquals(before, actual); + } + + @Test + public void testForBagNCopiesForRoundTrip() + throws IOException { + @SuppressWarnings("RedundantTypeArguments") + final TypeAdapter> unit = ApacheCommonsCollections4TypeAdapter.forBagNCopies( + TestTypeAdapters.integerTypeAdapter, + () -> IBuilder2.of(TreeBag::new, (e, nCopies, strings) -> strings.add(e, nCopies), Function.identity()) + ); + final Bag before = new TreeBag<>(); + before.add("foo"); + before.add("bar"); + before.add("bar"); + before.add("baz"); + before.add("baz"); + before.add("baz"); + final String json = unit.toJson(before); + Assertions.assertEquals("{\"bar\":2,\"baz\":3,\"foo\":1}", json); + final Bag actual = unit.fromJson(json); + Assertions.assertIterableEquals(before, actual); + } + +}