Skip to content
This repository was archived by the owner on Oct 16, 2024. It is now read-only.

Commit 28591b5

Browse files
authored
Merge pull request #218 from google/add.all.stream
Generate addAllX(Stream) for Collection properties
2 parents 8d0c623 + 8445c38 commit 28591b5

10 files changed

+892
-221
lines changed

README.md

+7-5
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,8 @@ property called 'descendants' would generate:
333333
|:------:| ----------- |
334334
| `addDescendants(String element)` | Appends `element` to the collection of descendants. If descendants is a set and the element is already present, it is ignored. Throws a NullPointerException if element is null. |
335335
| `addDescendants(String... elements)` | Appends all `elements` to the collection of descendants. If descendants is a set, any elements already present are ignored. Throws a NullPointerException if elements, or any of the values it holds, is null. |
336-
| `addAllDescendants(​Iterable<String> elements)` | Appends all `elements` to the collection of descendants. If descendants is a set, any elements already present are ignored. Throws a NullPointerException if elements, or any of the values it holds, is null. |
337-
| `mutateDescendants(​Consumer<‌.‌.‌.‌<String>> mutator)` | *Java 8+* Invokes the [Consumer] `mutator` with the collection of descendants. (The mutator takes a list, set or map as appropriate.) Throws a NullPointerException if `mutator` is null. As `mutator` is a void consumer, any value returned from a lambda will be ignored, so be careful not to call pure functions like [stream] expecting the returned collection to replace the existing collection. |
336+
| `addAllDescendants(​Iterable<String> elements)` | Appends all `elements` to the collection of descendants. If descendants is a set, any elements already present are ignored. Throws a NullPointerException if elements, or any of the values it holds, is null.<br> *Java 8+* Overloaded to also accept a [Stream] or [Spliterator]. |
337+
| `mutateDescendants(​Consumer<‌.‌.‌.‌<String>> mutator)` | *Java 8+* Invokes the [Consumer] `mutator` with the collection of descendants. (The mutator takes a list, set or map as appropriate.) Throws a NullPointerException if `mutator` is null. As `mutator` is a void consumer, any value returned from a lambda will be ignored, so be careful not to call pure functions like [stream()] expecting the returned collection to replace the existing collection. |
338338
| `clearDescendants()` | Removes all elements from the collection of descendants, leaving it empty. |
339339
| `descendants()` | Returns an unmodifiable view of the collection of descendants. Changes to the collection held by the builder will be reflected in the view. |
340340

@@ -350,7 +350,7 @@ A <code>[Map][]</code> property called 'albums' would generate:
350350
| `putAlbums(int key, String value)` | Associates `key` with `value` in albums. Throws a NullPointerException if either parameter is null. Replaces any existing entry. |
351351
| `putAllAlbums(Map<? extends Integer, ? extends String> map)` | Associates all of `map`'s keys and values in albums. Throws a NullPointerException if the map is null or contains a null key or value. Throws an IllegalArgumentException if any key is already present. |
352352
| `removeAlbums(int key)` | Removes the mapping for `key` from albums. Throws a NullPointerException if the parameter is null. Does nothing if the key is not present. |
353-
| `mutateAlbums(​Consumer<Map<Integer, String>> mutator)` | *Java 8+* Invokes the [Consumer] `mutator` with the map of albums. Throws a NullPointerException if `mutator` is null. As `mutator` is a void consumer, any value returned from a lambda will be ignored, so be careful not to call pure functions like [stream] expecting the returned map to replace the existing map. |
353+
| `mutateAlbums(​Consumer<Map<Integer, String>> mutator)` | *Java 8+* Invokes the [Consumer] `mutator` with the map of albums. Throws a NullPointerException if `mutator` is null. As `mutator` is a void consumer, any value returned from a lambda will be ignored, so be careful not to call pure functions like [stream()] expecting the returned map to replace the existing map. |
354354
| `clearAlbums()` | Removes all mappings from albums, leaving it empty. |
355355
| `albums()` | Returns an unmodifiable view of the map of albums. Changes to the map held by the builder will be reflected in this view. |
356356

@@ -368,7 +368,7 @@ A <code>[Multimap][]</code> property called 'awards' would generate:
368368
| `putAllAwards(Map<? extends Integer, ? extends String> map)` | Associates all of `map`'s keys and values in awards. Throws a NullPointerException if the map is null or contains a null key or value. If awards is a map, an IllegalArgumentException will be thrown if any key is already present. |
369369
| `removeAwards(int key, String value)` | Removes the single pair `key`-`value` from awards. If multiple pairs match, which is removed is unspecified. Throws a NullPointerException if either parameter is null. |
370370
| `removeAllAwards(int key)` | Removes all values associated with `key` from awards. Throws a NullPointerException if the key is null. |
371-
| `mutateAwards(​Consumer<Map<Integer, String>> mutator)` | *Java 8+* Invokes the [Consumer] `mutator` with the multimap of awards. Throws a NullPointerException if `mutator` is null. As `mutator` is a void consumer, any value returned from a lambda will be ignored, so be careful not to call pure functions like [stream] expecting the returned multimap to replace the existing multimap. |
371+
| `mutateAwards(​Consumer<Map<Integer, String>> mutator)` | *Java 8+* Invokes the [Consumer] `mutator` with the multimap of awards. Throws a NullPointerException if `mutator` is null. As `mutator` is a void consumer, any value returned from a lambda will be ignored, so be careful not to call pure functions like [stream()] expecting the returned multimap to replace the existing multimap. |
372372
| `clearAwards()` | Removes all mappings from awards, leaving it empty. |
373373
| `awards()` | Returns an unmodifiable view of the multimap of awards. Changes to the multimap held by the builder will be reflected in this view. |
374374

@@ -386,11 +386,13 @@ personBuilder
386386

387387
[List]: http://docs.oracle.com/javase/tutorial/collections/interfaces/list.html
388388
[Set]: http://docs.oracle.com/javase/tutorial/collections/interfaces/set.html
389+
[Spliterator]: https://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.html
390+
[Stream]: https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
389391
[Multiset]: https://github.com/google/guava/wiki/NewCollectionTypesExplained#multiset
390392
[Map]: http://docs.oracle.com/javase/tutorial/collections/interfaces/map.html
391393
[Multimap]: https://github.com/google/guava/wiki/NewCollectionTypesExplained#multimap
392394
[sort]: http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#sort-java.util.List-
393-
[stream]: https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#stream--
395+
[stream()]: https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#stream--
394396
[subList]: http://docs.oracle.com/javase/8/docs/api/java/util/List.html#subList-int-int-
395397

396398

src/main/java/org/inferred/freebuilder/processor/ListPropertyFactory.java

+83-16
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import static org.inferred.freebuilder.processor.util.StaticExcerpt.Type.METHOD;
3131
import static org.inferred.freebuilder.processor.util.feature.FunctionPackage.FUNCTION_PACKAGE;
3232
import static org.inferred.freebuilder.processor.util.feature.GuavaLibrary.GUAVA;
33+
import static org.inferred.freebuilder.processor.util.feature.SourceLevel.SOURCE_LEVEL;
3334
import static org.inferred.freebuilder.processor.util.feature.SourceLevel.diamondOperator;
3435

3536
import com.google.common.annotations.VisibleForTesting;
@@ -134,7 +135,7 @@ public void addBuilderFieldDeclaration(SourceBuilder code) {
134135
public void addBuilderFieldAccessors(SourceBuilder code) {
135136
addAdd(code, metadata);
136137
addVarargsAdd(code, metadata);
137-
addAddAll(code, metadata);
138+
addAddAllMethods(code, metadata);
138139
addMutate(code, metadata);
139140
addClear(code, metadata);
140141
addGetter(code, metadata);
@@ -201,24 +202,26 @@ private void addVarargsAdd(SourceBuilder code, Metadata metadata) {
201202
code.addLine("}");
202203
}
203204

204-
private void addAddAll(SourceBuilder code, Metadata metadata) {
205-
code.addLine("")
206-
.addLine("/**")
207-
.addLine(" * Adds each element of {@code elements} to the list to be returned from")
208-
.addLine(" * %s.", metadata.getType().javadocNoArgMethodLink(property.getGetterName()))
209-
.addLine(" *")
210-
.addLine(" * @return this {@code %s} object", metadata.getBuilder().getSimpleName())
211-
.addLine(" * @throws NullPointerException if {@code elements} is null or contains a")
212-
.addLine(" * null element")
213-
.addLine(" */");
205+
private void addAddAllMethods(SourceBuilder code, Metadata metadata) {
206+
if (code.feature(SOURCE_LEVEL).stream().isPresent()) {
207+
addSpliteratorAddAll(code, metadata);
208+
addStreamAddAll(code, metadata);
209+
addIterableAddAll(code, metadata);
210+
} else {
211+
addPreStreamsAddAll(code, metadata);
212+
}
213+
}
214+
215+
private void addPreStreamsAddAll(SourceBuilder code, Metadata metadata) {
216+
addJavadocForAddAll(code, metadata);
214217
addAccessorAnnotations(code);
215218
code.addLine("public %s %s(%s<? extends %s> elements) {",
216-
metadata.getBuilder(),
217-
addAllMethod(property),
218-
Iterable.class,
219-
elementType);
219+
metadata.getBuilder(),
220+
addAllMethod(property),
221+
Iterable.class,
222+
elementType);
220223
code.addLine(" if (elements instanceof %s) {", Collection.class)
221-
.addLine(" int elementsSize = ((%s<?>) elements).size();", Collection.class);
224+
.addLine(" int elementsSize = ((%s<?>) elements).size();", Collection.class);
222225
if (code.feature(GUAVA).isAvailable()) {
223226
code.addLine(" if (elementsSize != 0) {")
224227
.addLine(" if (%s instanceof %s) {", property.getName(), ImmutableList.class)
@@ -239,6 +242,70 @@ private void addAddAll(SourceBuilder code, Metadata metadata) {
239242
.addLine("}");
240243
}
241244

245+
private void addSpliteratorAddAll(SourceBuilder code, Metadata metadata) {
246+
QualifiedName spliterator = code.feature(SOURCE_LEVEL).spliterator().get();
247+
addJavadocForAddAll(code, metadata);
248+
code.addLine("public %s %s(%s<? extends %s> elements) {",
249+
metadata.getBuilder(),
250+
addAllMethod(property),
251+
spliterator,
252+
elementType);
253+
code.addLine(" if ((elements.characteristics() & %s.SIZED) != 0) {", spliterator)
254+
.addLine(" long elementsSize = elements.estimateSize();")
255+
.addLine(" if (elementsSize > 0 && elementsSize <= Integer.MAX_VALUE) {");
256+
if (code.feature(GUAVA).isAvailable()) {
257+
code.addLine(" if (%s instanceof %s) {", property.getName(), ImmutableList.class)
258+
.addLine(" %1$s = new %2$s%3$s(%1$s);",
259+
property.getName(), ArrayList.class, diamondOperator(elementType))
260+
.addLine(" }")
261+
.add(" ((%s<?>) %s)", ArrayList.class, property.getName());
262+
} else {
263+
code.add(" %s", property.getName());
264+
}
265+
code.add(".ensureCapacity(%s.size() + (int) elementsSize);%n", property.getName())
266+
.addLine(" }")
267+
.addLine(" }")
268+
.addLine(" elements.forEachRemaining(this::%s);", addMethod(property))
269+
.addLine(" return (%s) this;", metadata.getBuilder())
270+
.addLine("}");
271+
}
272+
273+
private void addIterableAddAll(SourceBuilder code, Metadata metadata) {
274+
addJavadocForAddAll(code, metadata);
275+
addAccessorAnnotations(code);
276+
code.addLine("public %s %s(%s<? extends %s> elements) {",
277+
metadata.getBuilder(),
278+
addAllMethod(property),
279+
Iterable.class,
280+
elementType)
281+
.addLine(" return %s(elements.spliterator());", addAllMethod(property))
282+
.addLine("}");
283+
}
284+
285+
private void addStreamAddAll(SourceBuilder code, Metadata metadata) {
286+
QualifiedName baseStream = code.feature(SOURCE_LEVEL).baseStream().get();
287+
addJavadocForAddAll(code, metadata);
288+
code.addLine("public %s %s(%s<? extends %s, ?> elements) {",
289+
metadata.getBuilder(),
290+
addAllMethod(property),
291+
baseStream,
292+
elementType)
293+
.addLine(" return %s(elements.spliterator());", addAllMethod(property))
294+
.addLine("}");
295+
}
296+
297+
private void addJavadocForAddAll(SourceBuilder code, Metadata metadata) {
298+
code.addLine("")
299+
.addLine("/**")
300+
.addLine(" * Adds each element of {@code elements} to the list to be returned from")
301+
.addLine(" * %s.", metadata.getType().javadocNoArgMethodLink(property.getGetterName()))
302+
.addLine(" *")
303+
.addLine(" * @return this {@code %s} object", metadata.getBuilder().getSimpleName())
304+
.addLine(" * @throws NullPointerException if {@code elements} is null or contains a")
305+
.addLine(" * null element")
306+
.addLine(" */");
307+
}
308+
242309
private void addMutate(SourceBuilder code, Metadata metadata) {
243310
ParameterizedType consumer = code.feature(FUNCTION_PACKAGE).consumer().orNull();
244311
if (consumer == null) {

src/main/java/org/inferred/freebuilder/processor/MultisetPropertyFactory.java

+65-11
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import static org.inferred.freebuilder.processor.util.ModelUtils.maybeUnbox;
2929
import static org.inferred.freebuilder.processor.util.ModelUtils.overrides;
3030
import static org.inferred.freebuilder.processor.util.feature.FunctionPackage.FUNCTION_PACKAGE;
31+
import static org.inferred.freebuilder.processor.util.feature.SourceLevel.SOURCE_LEVEL;
3132

3233
import com.google.common.base.Optional;
3334
import com.google.common.base.Preconditions;
@@ -118,7 +119,7 @@ public void addBuilderFieldDeclaration(SourceBuilder code) {
118119
public void addBuilderFieldAccessors(SourceBuilder code) {
119120
addAdd(code, metadata);
120121
addVarargsAdd(code, metadata);
121-
addAddAll(code, metadata);
122+
addAddAllMethods(code, metadata);
122123
addAddCopiesTo(code, metadata);
123124
addMutate(code, metadata);
124125
addClear(code, metadata);
@@ -169,16 +170,57 @@ private void addVarargsAdd(SourceBuilder code, Metadata metadata) {
169170
.addLine("}");
170171
}
171172

172-
private void addAddAll(SourceBuilder code, Metadata metadata) {
173-
code.addLine("")
174-
.addLine("/**")
175-
.addLine(" * Adds each element of {@code elements} to the multiset to be returned from")
176-
.addLine(" * %s.", metadata.getType().javadocNoArgMethodLink(property.getGetterName()))
177-
.addLine(" *")
178-
.addLine(" * @return this {@code %s} object", metadata.getBuilder().getSimpleName())
179-
.addLine(" * @throws NullPointerException if {@code elements} is null or contains a")
180-
.addLine(" * null element")
181-
.addLine(" */");
173+
private void addAddAllMethods(SourceBuilder code, Metadata metadata) {
174+
if (code.feature(SOURCE_LEVEL).stream().isPresent()) {
175+
addSpliteratorAddAll(code, metadata);
176+
addStreamAddAll(code, metadata);
177+
addIterableAddAll(code, metadata);
178+
} else {
179+
addPreStreamsAddAll(code, metadata);
180+
}
181+
}
182+
183+
private void addSpliteratorAddAll(SourceBuilder code, Metadata metadata) {
184+
QualifiedName spliterator = code.feature(SOURCE_LEVEL).spliterator().get();
185+
addJavadocForAddAll(code, metadata);
186+
code.addLine("public %s %s(%s<? extends %s> elements) {",
187+
metadata.getBuilder(),
188+
addAllMethod(property),
189+
spliterator,
190+
elementType)
191+
.addLine(" elements.forEachRemaining(element -> {")
192+
.addLine(" %s(element, 1);", addCopiesMethod(property))
193+
.addLine(" });")
194+
.addLine(" return (%s) this;", metadata.getBuilder())
195+
.addLine("}");
196+
}
197+
198+
private void addStreamAddAll(SourceBuilder code, Metadata metadata) {
199+
QualifiedName baseStream = code.feature(SOURCE_LEVEL).baseStream().get();
200+
addJavadocForAddAll(code, metadata);
201+
code.addLine("public %s %s(%s<? extends %s, ?> elements) {",
202+
metadata.getBuilder(),
203+
addAllMethod(property),
204+
baseStream,
205+
elementType)
206+
.addLine(" return %s(elements.spliterator());", addAllMethod(property))
207+
.addLine("}");
208+
}
209+
210+
private void addIterableAddAll(SourceBuilder code, Metadata metadata) {
211+
addJavadocForAddAll(code, metadata);
212+
addAccessorAnnotations(code);
213+
code.addLine("public %s %s(%s<? extends %s> elements) {",
214+
metadata.getBuilder(),
215+
addAllMethod(property),
216+
Iterable.class,
217+
elementType)
218+
.addLine(" return %s(elements.spliterator());", addAllMethod(property))
219+
.addLine("}");
220+
}
221+
222+
private void addPreStreamsAddAll(SourceBuilder code, Metadata metadata) {
223+
addJavadocForAddAll(code, metadata);
182224
addAccessorAnnotations(code);
183225
code.addLine("public %s %s(%s<? extends %s> elements) {",
184226
metadata.getBuilder(),
@@ -192,6 +234,18 @@ private void addAddAll(SourceBuilder code, Metadata metadata) {
192234
.addLine("}");
193235
}
194236

237+
private void addJavadocForAddAll(SourceBuilder code, Metadata metadata) {
238+
code.addLine("")
239+
.addLine("/**")
240+
.addLine(" * Adds each element of {@code elements} to the multiset to be returned from")
241+
.addLine(" * %s.", metadata.getType().javadocNoArgMethodLink(property.getGetterName()))
242+
.addLine(" *")
243+
.addLine(" * @return this {@code %s} object", metadata.getBuilder().getSimpleName())
244+
.addLine(" * @throws NullPointerException if {@code elements} is null or contains a")
245+
.addLine(" * null element")
246+
.addLine(" */");
247+
}
248+
195249
private void addAddCopiesTo(SourceBuilder code, Metadata metadata) {
196250
code.addLine("")
197251
.addLine("/**")

0 commit comments

Comments
 (0)