-
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
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.
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,38 @@ 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), | ||
// SafeLong is unique in this list. While it is technically backed with a long | ||
// its logical limitations are captured in the boxed type SafeLong. Meaning, | ||
// you must either expose this "implementation detail" on the public API or | ||
// accept that you cannot optimize away the boxing. For now, given the focus | ||
// on doubles, let's delay this optimization and have it as a separate discussion. | ||
// Technically this type could be optimized at rest, but that would require a more | ||
// complex enum to represent this trinary. So for now this is disabled. | ||
SAFE_LONG_LIST("SafeLongList", false), | ||
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 |
---|---|---|
|
@@ -170,8 +170,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 +199,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); | ||
} | ||
} | ||
} | ||
|
||
|
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.