Skip to content

Commit

Permalink
Apache Collections default builders probably should only support thei…
Browse files Browse the repository at this point in the history
…r interfaces and immutable implementations
  • Loading branch information
lyubomyr-shaydariv committed Nov 1, 2024
1 parent be4380f commit 0785f39
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import java.util.function.Function;
import java.util.function.Supplier;

import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import lombok.experimental.UtilityClass;
import lsh.ext.gson.IBuilder1;
Expand All @@ -18,12 +17,19 @@
import org.apache.commons.collections4.MultiSet;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.bag.HashBag;
import org.apache.commons.collections4.bag.UnmodifiableBag;
import org.apache.commons.collections4.bidimap.DualHashBidiMap;
import org.apache.commons.collections4.bidimap.DualLinkedHashBidiMap;
import org.apache.commons.collections4.bidimap.UnmodifiableBidiMap;
import org.apache.commons.collections4.collection.UnmodifiableBoundedCollection;
import org.apache.commons.collections4.keyvalue.DefaultKeyValue;
import org.apache.commons.collections4.keyvalue.UnmodifiableMapEntry;
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
import org.apache.commons.collections4.map.HashedMap;
import org.apache.commons.collections4.map.UnmodifiableMap;
import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
import org.apache.commons.collections4.multimap.UnmodifiableMultiValuedMap;
import org.apache.commons.collections4.multiset.HashMultiSet;
import org.apache.commons.collections4.multiset.UnmodifiableMultiSet;
import org.apache.commons.collections4.queue.CircularFifoQueue;

@UtilityClass
public final class ApacheCommonsCollections4TypeAdapterFactory {
Expand All @@ -41,28 +47,18 @@ public static <E> ITypeAdapterFactory<Bag<E>> forBag(
final Function<? super E, String> toKey,
final Function<? super String, ? extends E> fromKey
) {
return ITypeAdapterFactory.forClassHierarchy(
Bag.class,
provider -> {
final TypeAdapter<Integer> integerTypeAdapter = provider.getTypeAdapterForClass(int.class);
return ApacheCommonsCollections4TypeAdapter.forBagNCopies(
integerTypeAdapter,
lookup.lookup(provider.getTypeToken()),
toKey,
fromKey
);
}
);
return ITypeAdapterFactory.forClassHierarchy(Bag.class, provider -> ApacheCommonsCollections4TypeAdapter.forBagNCopies(provider.getTypeAdapterForClass(int.class), lookup.lookup(provider.getTypeToken()), toKey, fromKey));
}

// TODO handle all known implementations
public static <E> Supplier<IBuilder2<E, Integer, Bag<E>>> defaultBuilderForBag(final TypeToken<? super Bag<E>> typeToken) {
@SuppressWarnings("unchecked")
final Class<? super Bag<?>> rawType = (Class<? super Bag<?>>) typeToken.getRawType();
if ( HashBag.class.isAssignableFrom(rawType) ) {
return () -> Builder.forBagNCopies(HashBag::new);
if ( rawType == Bag.class || UnmodifiableBag.class.isAssignableFrom(rawType) ) {
return () -> Builder.forBagNCopies(HashBag::new, UnmodifiableBag::unmodifiableBag);
}
return () -> Builder.forBagNCopies(HashBag::new);
return () -> {
throw new UnsupportedOperationException(String.format("The default builder for %s does not support %s", Bag.class, typeToken));
};
}

public static final ITypeAdapterFactory<BidiMap<String, Object>> defaultForBidiMap = forBidiMap(ApacheCommonsCollections4TypeAdapterFactory::defaultBuilderForBidiMap);
Expand All @@ -81,17 +77,15 @@ public static <K, V> ITypeAdapterFactory<BidiMap<K, V>> forBidiMap(
return ITypeAdapterFactory.forClassHierarchy(BidiMap.class, provider -> ApacheCommonsCollections4TypeAdapter.forBidiMap(provider.getTypeAdapter(1), lookup.lookup(provider.getTypeToken()), toKey, fromKey));
}

// TODO handle all known implementations
public static <V> Supplier<IBuilder2<String, V, BidiMap<String, V>>> defaultBuilderForBidiMap(final TypeToken<? super BidiMap<String, V>> typeToken) {
@SuppressWarnings("unchecked")
final Class<? extends BidiMap<?, ?>> rawType = (Class<? extends BidiMap<?, ?>>) typeToken.getRawType();
if ( DualHashBidiMap.class.isAssignableFrom(rawType) ) {
return () -> IBuilder2.fromMap(new DualHashBidiMap<>());
@SuppressWarnings({ "rawtypes", "unchecked" })
final Class<? extends BidiMap> rawType = (Class<? extends BidiMap>) typeToken.getRawType();
if ( rawType == BidiMap.class || UnmodifiableBidiMap.class.isAssignableFrom(rawType) ) {
return () -> Builder.forBidiMap(DualHashBidiMap::new, UnmodifiableBidiMap::unmodifiableBidiMap);
}
if ( DualLinkedHashBidiMap.class.isAssignableFrom(rawType) ) {
return () -> IBuilder2.fromMap(new DualLinkedHashBidiMap<>());
}
return () -> IBuilder2.fromMap(new DualHashBidiMap<>());
return () -> {
throw new UnsupportedOperationException(String.format("The default builder for %s does not support %s", BidiMap.class, typeToken));
};
}

public static ITypeAdapterFactory<BoundedCollection<Object>> defaultForBoundedCollection = forBoundedCollection(ApacheCommonsCollections4TypeAdapterFactory::defaultBuilderForBoundedCollection);
Expand All @@ -102,9 +96,15 @@ public static <E> ITypeAdapterFactory<BoundedCollection<E>> forBoundedCollection
return ITypeAdapterFactory.forClassHierarchy(BoundedCollection.class, provider -> ApacheCommonsCollections4TypeAdapter.forBoundedCollection(provider.getTypeAdapter(0), lookup.lookup(provider.getTypeToken())));
}

// TODO handle all known implementations
public static <E> Supplier<IBuilder1<E, BoundedCollection<E>>> defaultBuilderForBoundedCollection(final TypeToken<? super BoundedCollection<E>> typeToken) {
throw new UnsupportedOperationException(String.valueOf(typeToken));
@SuppressWarnings({ "rawtypes", "unchecked" })
final Class<? extends BoundedCollection> rawType = (Class<? extends BoundedCollection>) typeToken.getRawType();
if ( rawType == BoundedCollection.class || UnmodifiableBoundedCollection.class.isAssignableFrom(rawType) ) {
return () -> Builder.forBoundedCollection(CircularFifoQueue::new, UnmodifiableBoundedCollection::unmodifiableBoundedCollection);
}
return () -> {
throw new UnsupportedOperationException(String.format("The default builder for %s does not support %s", BoundedCollection.class, typeToken));
};
}

public static final ITypeAdapterFactory<IterableMap<String, Object>> defaultForIterableMap = forIterableMap(ApacheCommonsCollections4TypeAdapterFactory::defaultBuilderForIterableMap);
Expand All @@ -124,7 +124,14 @@ public static <K, V> ITypeAdapterFactory<IterableMap<K, V>> forIterableMap(
}

public static <V> Supplier<IBuilder2<String, V, IterableMap<String, V>>> defaultBuilderForIterableMap(final TypeToken<? super IterableMap<String, V>> typeToken) {
throw new UnsupportedOperationException(String.valueOf(typeToken));
@SuppressWarnings({ "unchecked", "rawtypes" })
final Class<? extends IterableMap> rawType = (Class<? extends IterableMap>) typeToken.getRawType();
if ( rawType == IterableMap.class ) {
return () -> Builder.forIterableMap(HashedMap::new, iterableMap -> (IterableMap<String, V>) UnmodifiableMap.unmodifiableMap(iterableMap));
}
return () -> {
throw new UnsupportedOperationException(String.format("The default builder for %s does not support %s", IterableMap.class, typeToken));
};
}

public static final ITypeAdapterFactory<KeyValue<String, Object>> defaultForKeyValue = forKeyValue(UnmodifiableMapEntry::new);
Expand All @@ -147,7 +154,14 @@ public static <K, V> ITypeAdapterFactory<KeyValue<K, V>> forKeyValue(
}

public static <V> Supplier<IBuilder2<String, V, KeyValue<String, V>>> defaultBuilderForKeyValue(final TypeToken<? super KeyValue<String, V>> typeToken) {
throw new UnsupportedOperationException(String.valueOf(typeToken));
@SuppressWarnings({ "unchecked", "rawtypes" })
final Class<? extends KeyValue> rawType = (Class<? extends KeyValue>) typeToken.getRawType();
if ( rawType == KeyValue.class || UnmodifiableMapEntry.class.isAssignableFrom(rawType) ) {
return () -> Builder.forKeyValue(DefaultKeyValue::new, UnmodifiableMapEntry::new);
}
return () -> {
throw new UnsupportedOperationException(String.format("The default builder for %s does not support %s", KeyValue.class, typeToken));
};
}

public static ITypeAdapterFactory<MultiSet<Object>> defaultForMultiSet = forMultiSet(ApacheCommonsCollections4TypeAdapterFactory::defaultBuilderForMultiSet);
Expand All @@ -158,14 +172,15 @@ public static <E> ITypeAdapterFactory<MultiSet<E>> forMultiSet(
return ITypeAdapterFactory.forClassHierarchy(MultiSet.class, provider -> ApacheCommonsCollections4TypeAdapter.forMultiSet(provider.getTypeAdapter(0), lookup.lookup(provider.getTypeToken())));
}

// TODO handle all known implementations
public static <E> Supplier<IBuilder1<E, MultiSet<E>>> defaultBuilderForMultiSet(final TypeToken<? super MultiSet<E>> typeToken) {
@SuppressWarnings("unchecked")
final Class<? extends MultiSet<?>> rawType = (Class<? extends MultiSet<?>>) typeToken.getRawType();
if ( HashMultiSet.class.isAssignableFrom(rawType) ) {
return () -> Builder.forMultiSet(HashMultiSet::new);
@SuppressWarnings({ "unchecked", "rawtypes" })
final Class<? extends MultiSet> rawType = (Class<? extends MultiSet>) typeToken.getRawType();
if ( rawType == MultiSet.class || UnmodifiableMultiSet.class.isAssignableFrom(rawType) ) {
return () -> Builder.forMultiSet(HashMultiSet::new, UnmodifiableMultiSet::unmodifiableMultiSet);
}
return () -> Builder.forMultiSet(HashMultiSet::new);
return () -> {
throw new UnsupportedOperationException(String.format("The default builder for %s does not support %s", MultiSet.class, typeToken));
};
}

public static final ITypeAdapterFactory<MultiValuedMap<String, Object>> defaultForMultiValueMap = forMultiValueMap(ApacheCommonsCollections4TypeAdapterFactory::defaultBuilderForMultiValuedMap);
Expand All @@ -184,17 +199,15 @@ public static <K, V> ITypeAdapterFactory<MultiValuedMap<K, V>> forMultiValueMap(
return ITypeAdapterFactory.forClassHierarchy(MultiValuedMap.class, provider -> ApacheCommonsCollections4TypeAdapter.forMultiValuedMap(provider.getTypeAdapter(1), builderLookup.lookup(provider.getTypeToken()), encodeKey, decodeKey));
}

// TODO handle all known implementations
public static <V> Supplier<IBuilder2<String, V, MultiValuedMap<String, V>>> defaultBuilderForMultiValuedMap(final TypeToken<? super MultiValuedMap<String, V>> typeToken) {
@SuppressWarnings("unchecked")
final Class<? extends MultiValuedMap<?, ?>> rawType = (Class<? extends MultiValuedMap<?, ?>>) typeToken.getRawType();
if ( ArrayListValuedHashMap.class.isAssignableFrom(rawType) ) {
return () -> Builder.forMultiValuedMap(ArrayListValuedHashMap::new);
}
if ( HashSetValuedHashMap.class.isAssignableFrom(rawType) ) {
return () -> Builder.forMultiValuedMap(HashSetValuedHashMap::new);
@SuppressWarnings({ "unchecked", "rawtypes" })
final Class<? extends MultiValuedMap> rawType = (Class<? extends MultiValuedMap>) typeToken.getRawType();
if ( rawType == MultiValuedMap.class || UnmodifiableMultiValuedMap.class.isAssignableFrom(rawType) ) {
return () -> Builder.forMultiValuedMap(HashSetValuedHashMap::new, UnmodifiableMultiValuedMap::unmodifiableMultiValuedMap);
}
return () -> Builder.forMultiValuedMap(ArrayListValuedHashMap::new);
return () -> {
throw new UnsupportedOperationException(String.format("The default builder for %s does not support %s", MultiValuedMap.class, typeToken));
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@

import java.util.Collection;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.annotation.Nullable;

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.BidiMap;
import org.apache.commons.collections4.BoundedCollection;
import org.apache.commons.collections4.IterableMap;
import org.apache.commons.collections4.KeyValue;
import org.apache.commons.collections4.MultiSet;
import org.apache.commons.collections4.MultiValuedMap;

Expand All @@ -20,24 +25,49 @@ public static <E, B extends Bag<E>> IBuilder1<E, B> forBag(final Supplier<? exte
return IBuilder1.of(create, Bag::add);
}

public static <E, B extends Bag<E>> IBuilder2<E, Integer, B> forBagNCopies(final Supplier<? extends B> create) {
return IBuilder2.of(create, Bag::add);
public static <E, BI extends Bag<E>, BO extends Bag<E>> IBuilder2<E, Integer, BO> forBagNCopies(final Supplier<? extends BI> create, final Function<? super BI, ? extends BO> build) {
return IBuilder2.of(create, Bag::add, build);
}

public static <E, M extends MultiSet<E>> IBuilder1<E, M> forMultiSet(final Supplier<? extends M> create) {
return IBuilder1.of(create, MultiSet::add);
public static <K, V, MI extends BidiMap<K, V>, MO extends BidiMap<K, V>> IBuilder2<K, V, MO> forBidiMap(final Supplier<? extends MI> create, final Function<? super MI, ? extends MO> build) {
return IBuilder2.of(create, BidiMap::put, build);
}

public static <E, C extends BoundedCollection<E>> IBuilder1<E, C> forBoundedCollection(final Supplier<? extends C> create) {
return IBuilder1.of(create, Collection::add);
public static <E, CI extends BoundedCollection<E>, CO extends BoundedCollection<E>> IBuilder1<E, CO> forBoundedCollection(final Supplier<? extends CI> create, final Function<? super CI, ? extends CO> build) {
return IBuilder1.of(create, Collection::add, build);
}

public static <K, V, M extends IterableMap<K, V>> IBuilder2<K, V, M> forIterableMap(final Supplier<? extends M> create) {
return IBuilder2.of(create, Map::put);
public static <K, V, MI extends IterableMap<K, V>, MO extends IterableMap<K, V>> IBuilder2<K, V, MO> forIterableMap(final Supplier<? extends MI> create, final Function<? super MI, ? extends MO> build) {
return IBuilder2.of(create, Map::put, build);
}

public static <K, V, M extends MultiValuedMap<K, V>> IBuilder2<K, V, M> forMultiValuedMap(final Supplier<? extends M> create) {
return IBuilder2.of(create, MultiValuedMap::put);
public static <K, V, EI extends KeyValue<K, V>, EO extends KeyValue<K, V>> IBuilder2<K, V, EO> forKeyValue(final BiFunction<? super K, ? super V, ? extends EI> create, final Function<? super EI, ? extends EO> build) {
return new IBuilder2<>() {
@Nullable
private EI keyValue;

@Override
public void accept(final K k, final V v) {
keyValue = create.apply(k, v);
}

@Override
@Nullable
public EO build() {
if ( keyValue == null ) {
return null;
}
return build.apply(keyValue);
}
};
}

public static <E, MI extends MultiSet<E>, MO extends MultiSet<E>> IBuilder1<E, MO> forMultiSet(final Supplier<? extends MI> create, final Function<? super MI, ? extends MO> build) {
return IBuilder1.of(create, MultiSet::add, build);
}

public static <K, V, MI extends MultiValuedMap<K, V>, MO extends MultiValuedMap<K, V>> IBuilder2<K, V, MO> forMultiValuedMap(final Supplier<? extends MI> create, final Function<? super MI, ? extends MO> build) {
return IBuilder2.of(create, MultiValuedMap::put, build);
}

}

0 comments on commit 0785f39

Please sign in to comment.