Skip to content

Commit

Permalink
Landing all conjure-lib API changes ahead of codegen adoption
Browse files Browse the repository at this point in the history
Includes:
  New conjure collections API for:
    - unmodifiableList
    - addAll
  Optimized add and serialize methods on native containers
  • Loading branch information
bmarcaur committed Nov 22, 2024
1 parent 07678a0 commit 33e2bf9
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
import com.palantir.conjure.java.lib.SafeLong;
import com.palantir.logsafe.Preconditions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
Expand All @@ -37,6 +39,35 @@ private ConjureCollections() {
// cannot instantiate
}

/*
* This is bizarre. Allow me to explain...
*
* We do _not_ want to expose the Conjure*List types externally
* but we also want the optimizations they provide to make it thru
* to jackson for serialization. So the runtime type needs to be
* preserved while also not exposing the type :phew:.
*
* To achieve this we have to do some gymnastics surrounding the type
* system. We need this to return the type of the list given, but also
* return specific Conjure types when detected. This requires that we
* erase the type info, but we know this is safe because we are directly
* returning the same type which is by definition the identity function.
* Therefore the input List<T> is the same types as the output List<T>.
*/
public static <T> List<T> unmodifiableList(List<T> list) {
// Return the unmodifiable version of the Eclipse types
if (list instanceof ConjureIntegerList) {
return (List<T>) ((ConjureIntegerList) list).asUnmodifiable();
} else if (list instanceof ConjureDoubleList) {
return (List<T>) ((ConjureDoubleList) list).asUnmodifiable();
} else if (list instanceof ConjureSafeLongList) {
return (List<T>) ((ConjureSafeLongList) list).asUnmodifiable();
} else {
// Otherwise use the JDK types
return Collections.unmodifiableList(list);
}
}

@SuppressWarnings("unchecked")
public static <T> void addAll(Collection<T> addTo, Iterable<? extends T> elementsToAdd) {
Preconditions.checkNotNull(elementsToAdd, "elementsToAdd cannot be null");
Expand Down Expand Up @@ -139,6 +170,12 @@ public static <T> Set<T> newNonNullSet(Iterable<? extends T> iterable) {
return set;
}

/**
* The following Conjure boxed list wrappers for the eclipse-collections [type]ArrayList are temporary (except
* ConjureSafeLongList). In eclipse-collections 12, a BoxedMutable[type]List will be released. Once available,
* Conjure[type]List should be replaced with that.
*/

// This method returns a list that can't handle nulls. Do not use this unless the nonNullCollections flag is set
public static List<Double> newNonNullDoubleList() {
return new ConjureDoubleList(new DoubleArrayList());
Expand All @@ -157,11 +194,14 @@ public static List<Double> newNonNullDoubleList(Iterable<Double> iterable) {
return doubleList;
}

/**
* The following Conjure boxed list wrappers for the eclipse-collections [type]ArrayList are temporary (except
* ConjureSafeLongList). In eclipse-collections 12, a BoxedMutable[type]List will be released. Once available,
* Conjure[type]List should be replaced with that.
*/
// This method modifies a list that can't handle nulls. Do not use this unless the nonNullCollections flag is set
public static void addAllToDoubleList(Collection<Double> addTo, double[] elementsToAdd) {
if (addTo instanceof ConjureDoubleList) {
((ConjureDoubleList) addTo).addAll(elementsToAdd);
} else {
addAll(addTo, () -> Arrays.stream(elementsToAdd).iterator());
}
}

// This method returns a list that can't handle nulls. Do not use this unless the nonNullCollections flag is set
public static List<Integer> newNonNullIntegerList() {
Expand All @@ -181,9 +221,19 @@ public static List<Integer> newNonNullIntegerList(Iterable<Integer> iterable) {
return integerList;
}

// This method modifies a list that can't handle nulls. Do not use this unless the nonNullCollections flag is set
public static void addAllToIntegerList(Collection<Integer> addTo, int[] elementsToAdd) {
if (addTo instanceof ConjureIntegerList) {
((ConjureIntegerList) addTo).addAll(elementsToAdd);
} else {
addAll(addTo, () -> Arrays.stream(elementsToAdd).iterator());
}
}

/**
* Deprecated, this should only ever be called by a previously generated conjure internal implementation.
*/
// This method returns a list that can't handle nulls. Do not use this unless the nonNullCollections flag is set
public static List<Boolean> newNonNullBooleanList() {
return newNonNullList();
}
Expand Down Expand Up @@ -212,4 +262,13 @@ public static List<SafeLong> newNonNullSafeLongList(Iterable<SafeLong> iterable)

return safeLongList;
}

// This method modifies a list that can't handle nulls. Do not use this unless the nonNullCollections flag is set
public static void addAllToSafeLongList(Collection<SafeLong> addTo, long[] elementsToAdd) {
if (addTo instanceof ConjureSafeLongList) {
((ConjureSafeLongList) addTo).addAll(elementsToAdd);
} else {
addAll(addTo, Arrays.stream(elementsToAdd).boxed().map(SafeLong::of).toList());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,21 @@

package com.palantir.conjure.java.lib.internal;

import com.fasterxml.jackson.annotation.JsonValue;
import java.util.AbstractList;
import java.util.Collection;
import java.util.RandomAccess;
import org.eclipse.collections.impl.list.mutable.primitive.DoubleArrayList;
import org.eclipse.collections.api.list.primitive.MutableDoubleList;
import org.eclipse.collections.impl.utility.Iterate;

/**
* ConjureDoubleList is a boxed list wrapper for the eclipse-collections DoubleArrayList. In eclipse-collections 12,
* a BoxedMutableDoubleList will be released. Once available, ConjureDoubleList should be replaced with that.
*/
final class ConjureDoubleList extends AbstractList<Double> implements RandomAccess {
private final DoubleArrayList delegate;
private final MutableDoubleList delegate;

ConjureDoubleList(DoubleArrayList delegate) {
ConjureDoubleList(MutableDoubleList delegate) {
this.delegate = delegate;
}

Expand All @@ -55,6 +56,10 @@ public boolean addAll(int index, Collection<? extends Double> collection) {
return delegate.addAllAtIndex(index, target);
}

public void addAll(double... source) {
this.delegate.addAll(source);
}

@Override
public Double remove(int index) {
return delegate.removeAtIndex(index);
Expand All @@ -69,4 +74,15 @@ public void clear() {
public Double set(int index, Double element) {
return delegate.set(index, element);
}

public ConjureDoubleList asUnmodifiable() {
return new ConjureDoubleList(delegate.asUnmodifiable());
}

// Cannot be named 'toArray' as that conflicts with the #toArray in AbstractList
// This is a serialization optimization that avoids boxing, but does copy
@JsonValue
double[] jacksonSerialize() {
return delegate.toArray();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,21 @@

package com.palantir.conjure.java.lib.internal;

import com.fasterxml.jackson.annotation.JsonValue;
import java.util.AbstractList;
import java.util.Collection;
import java.util.RandomAccess;
import org.eclipse.collections.impl.list.mutable.primitive.IntArrayList;
import org.eclipse.collections.api.list.primitive.MutableIntList;
import org.eclipse.collections.impl.utility.Iterate;

/**
* ConjureIntegerList is a boxed list wrapper for the eclipse-collections IntArrayList. In eclipse-collections 12,
* a BoxedMutableIntList will be released. Once available, ConjureIntegerList should be replaced with that.
*/
final class ConjureIntegerList extends AbstractList<Integer> implements RandomAccess {
private final IntArrayList delegate;
private final MutableIntList delegate;

ConjureIntegerList(IntArrayList delegate) {
ConjureIntegerList(MutableIntList delegate) {
this.delegate = delegate;
}

Expand All @@ -55,6 +56,10 @@ public boolean addAll(int index, Collection<? extends Integer> collection) {
return delegate.addAllAtIndex(index, target);
}

public void addAll(int... source) {
this.delegate.addAll(source);
}

@Override
public Integer remove(int index) {
return delegate.removeAtIndex(index);
Expand All @@ -69,4 +74,15 @@ public void clear() {
public Integer set(int index, Integer element) {
return delegate.set(index, element);
}

public ConjureIntegerList asUnmodifiable() {
return new ConjureIntegerList(delegate.asUnmodifiable());
}

// Cannot be named 'toArray' as that conflicts with the #toArray in AbstractList
// This is a serialization optimization that avoids boxing, but does copy
@JsonValue
int[] jacksonSerialize() {
return delegate.toArray();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,22 @@

package com.palantir.conjure.java.lib.internal;

import com.fasterxml.jackson.annotation.JsonValue;
import com.palantir.conjure.java.lib.SafeLong;
import java.util.AbstractList;
import java.util.Collection;
import java.util.RandomAccess;
import org.eclipse.collections.impl.list.mutable.primitive.LongArrayList;
import org.eclipse.collections.api.list.primitive.MutableLongList;
import org.eclipse.collections.impl.utility.Iterate;

/**
* ConjureSafeLongList is a boxed list wrapper for the eclipse-collections LongArrayList. This handles boxing/unboxing
* with SafeLongs.
*/
final class ConjureSafeLongList extends AbstractList<SafeLong> implements RandomAccess {
private final LongArrayList delegate;
private final MutableLongList delegate;

ConjureSafeLongList(LongArrayList delegate) {
ConjureSafeLongList(MutableLongList delegate) {
this.delegate = delegate;
}

Expand All @@ -56,6 +57,14 @@ public boolean addAll(int index, Collection<? extends SafeLong> collection) {
return delegate.addAllAtIndex(index, target);
}

public void addAll(long... source) {
for (long value : source) {
// Doesn't use SafeLong creation because this causes unnecessary boxing
SafeLong.check(value);
}
this.delegate.addAll(source);
}

@Override
public SafeLong remove(int index) {
return SafeLong.of(delegate.removeAtIndex(index));
Expand All @@ -70,4 +79,15 @@ public void clear() {
public SafeLong set(int index, SafeLong element) {
return SafeLong.of(delegate.set(index, element.longValue()));
}

public ConjureSafeLongList asUnmodifiable() {
return new ConjureSafeLongList(delegate.asUnmodifiable());
}

// Cannot be named 'toArray' as that conflicts with the #toArray in AbstractList
// This is a serialization optimization that avoids boxing, but does copy
@JsonValue
long[] jacksonSerialize() {
return delegate.toArray();
}
}

0 comments on commit 33e2bf9

Please sign in to comment.