-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature] Non-boxing 'addAll' Methods for Primitive Types #2389
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
type: feature | ||
feature: | ||
description: '[Feature] Non-boxing ''addAll'' Methods for Primitive Types' | ||
links: | ||
- https://github.com/palantir/conjure-java/pull/2389 |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -671,6 +671,26 @@ private MethodSpec createCollectionSetter(String prefix, EnrichedField enriched, | |
.build(); | ||
} | ||
|
||
private MethodSpec createPrimitiveCollectionSetter(EnrichedField enriched, boolean override) { | ||
FieldSpec field = enriched.poetSpec(); | ||
Type type = enriched.conjureDef().getType(); | ||
|
||
CollectionType collectionType = getCollectionType(type); | ||
return BeanBuilderAuxiliarySettersUtils.createPrimitiveCollectionSetterBuilder( | ||
enriched, typeMapper, builderClass, safetyEvaluator) | ||
.addAnnotations(ConjureAnnotations.override(override)) | ||
.addCode(verifyNotBuilt()) | ||
.addCode(CodeBlocks.statement( | ||
"$1T.addAllTo$2L(this.$3N, $4L)", | ||
ConjureCollections.class, | ||
collectionType.getConjureCollectionType().getCollectionName(), | ||
field.name(), | ||
Expressions.requireNonNull( | ||
field.name(), enriched.fieldName().get() + " cannot be null"))) | ||
.addStatement("return this") | ||
.build(); | ||
} | ||
|
||
@SuppressWarnings("checkstyle:CyclomaticComplexity") | ||
private CodeBlock typeAwareAssignment(EnrichedField enriched, Type type, boolean shouldClearFirst) { | ||
FieldSpec spec = enriched.poetSpec(); | ||
|
@@ -767,10 +787,19 @@ private List<MethodSpec> createAuxiliarySetters(EnrichedField enriched, boolean | |
Type type = enriched.conjureDef().getType(); | ||
Optional<LogSafety> safety = safetyEvaluator.getUsageTimeSafety(enriched.conjureDef()); | ||
|
||
ImmutableList.Builder<MethodSpec> builder = ImmutableList.builder(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Made this a builder so I could optionally add another method and not have to duplicate logic to return lists directly. |
||
|
||
if (type.accept(TypeVisitor.IS_LIST)) { | ||
return ImmutableList.of( | ||
CollectionType collectionType = getCollectionType(type); | ||
if (collectionType.getConjureCollectionType().isPrimitiveCollection() | ||
&& collectionType.useNonNullFactory()) { | ||
builder.add(createPrimitiveCollectionSetter(enriched, override)); | ||
} | ||
|
||
builder.add( | ||
createCollectionSetter("addAll", enriched, override), | ||
createItemSetter(enriched, type.accept(TypeVisitor.LIST).getItemType(), override, safety)); | ||
return builder.build(); | ||
} | ||
|
||
if (type.accept(TypeVisitor.IS_SET)) { | ||
|
@@ -822,7 +851,9 @@ private CodeBlock optionalAssignmentStatement(EnrichedField enriched, OptionalTy | |
private static final EnumSet<PrimitiveType.Value> OPTIONAL_PRIMITIVES = | ||
EnumSet.of(PrimitiveType.Value.INTEGER, PrimitiveType.Value.DOUBLE, PrimitiveType.Value.BOOLEAN); | ||
|
||
/** Check if the optionalType contains a primitive boolean, double or integer. */ | ||
/** | ||
* Check if the optionalType contains a primitive boolean, double or integer. | ||
*/ | ||
private boolean isPrimitiveOptional(OptionalType optionalType) { | ||
return optionalType.getItemType().accept(TypeVisitor.IS_PRIMITIVE) | ||
&& OPTIONAL_PRIMITIVES.contains( | ||
|
@@ -1041,25 +1072,31 @@ public boolean useNonNullFactory() { | |
} | ||
|
||
private enum ConjureCollectionType { | ||
LIST("List"), | ||
DOUBLE_LIST("DoubleList"), | ||
INTEGER_LIST("IntegerList"), | ||
LIST("List", false), | ||
DOUBLE_LIST("DoubleList", true), | ||
INTEGER_LIST("IntegerList", true), | ||
// Eclipse has a BooleanList type, but this use case implies | ||
// bit mask and it doesn't serialize efficiently as a collection | ||
// so let's just use the "naive" boxed collection | ||
BOOLEAN_LIST("List"), | ||
SAFE_LONG_LIST("SafeLongList"), | ||
SET("Set"); | ||
BOOLEAN_LIST("List", false), | ||
SAFE_LONG_LIST("SafeLongList", true), | ||
SET("Set", false); | ||
|
||
private final String collectionName; | ||
private final Boolean primitiveCollection; | ||
|
||
ConjureCollectionType(String collectionName) { | ||
ConjureCollectionType(String collectionName, Boolean primitiveCollection) { | ||
this.collectionName = collectionName; | ||
this.primitiveCollection = primitiveCollection; | ||
} | ||
|
||
public String getCollectionName() { | ||
return collectionName; | ||
} | ||
|
||
public Boolean isPrimitiveCollection() { | ||
return primitiveCollection; | ||
} | ||
} | ||
|
||
private enum ConjureCollectionNullHandlingMode { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,7 +40,7 @@ public long longValue() { | |
return longValue; | ||
} | ||
|
||
private static long check(long value) { | ||
public static long check(long value) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I increased the visibility on this, because the logic is simple enough that to satisfy it is tantamount to copying the implementation detail (which felt bad). But also the logic is so simple that we aren't overly exposing some internals. Besides having more places rely on this is likely to prevent skew between implementations (IMO). |
||
Preconditions.checkArgument( | ||
MIN_SAFE_VALUE <= value && value <= MAX_SAFE_VALUE, | ||
"number must be safely representable in javascript i.e. " | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,7 @@ | |
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; | ||
|
@@ -170,8 +171,12 @@ public static List<Double> newNonNullDoubleList(Iterable<Double> iterable) { | |
|
||
// 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) { | ||
for (double el : elementsToAdd) { | ||
addTo.add(el); | ||
if (addTo instanceof ConjureDoubleList) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An inelegant solution to preserve the feedback on #2307 (comment), but still capture the optimization we added. Also has some prior art in |
||
((ConjureDoubleList) addTo).addAll(elementsToAdd); | ||
} else { | ||
for (double el : elementsToAdd) { | ||
addTo.add(el); | ||
} | ||
} | ||
} | ||
|
||
|
@@ -195,8 +200,12 @@ public static List<Integer> newNonNullIntegerList(Iterable<Integer> iterable) { | |
|
||
// 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) { | ||
for (int el : elementsToAdd) { | ||
addTo.add(el); | ||
if (addTo instanceof ConjureIntegerList) { | ||
((ConjureIntegerList) addTo).addAll(elementsToAdd); | ||
} else { | ||
for (int el : elementsToAdd) { | ||
addTo.add(el); | ||
} | ||
} | ||
} | ||
|
||
|
@@ -235,8 +244,12 @@ public static List<SafeLong> newNonNullSafeLongList(Iterable<SafeLong> iterable) | |
|
||
// 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) { | ||
for (long el : elementsToAdd) { | ||
addTo.add(SafeLong.of(el)); | ||
if (addTo instanceof ConjureSafeLongList) { | ||
((ConjureSafeLongList) addTo).addAll(elementsToAdd); | ||
} else { | ||
for (long el : elementsToAdd) { | ||
addTo.add(SafeLong.of(el)); | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,6 +56,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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment is a little too specific and doesn't explain how the underlying type holds longs and those must adhere to the abstraction. |
||
SafeLong.check(value); | ||
} | ||
this.delegate.addAll(source); | ||
} | ||
|
||
@Override | ||
public SafeLong remove(int index) { | ||
return SafeLong.of(delegate.removeAtIndex(index)); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can extract this into a separate PR, but I noticed that we were overly exposing these methods which in turn was overly exposing the
EnrichedField
type.