Skip to content

Commit fc6d935

Browse files
committed
First pass at add and put methods for building collections and maps within beans (JodaOrg#100).
1 parent 1730967 commit fc6d935

File tree

9 files changed

+423
-6
lines changed

9 files changed

+423
-6
lines changed

Diff for: pom.xml

+4
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@
7272
<name>Martynas Sateika</name>
7373
<url>https://github.com/martynassateika</url>
7474
</contributor>
75+
<contributor>
76+
<name>Ashley Heath</name>
77+
<url>https://github.com/ashleyheath</url>
78+
</contributor>
7579
</contributors>
7680

7781
<!-- ==================================================================== -->

Diff for: src/main/java/org/joda/beans/gen/BeanGen.java

+18
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,8 @@ private void generateBuilderClass() {
12491249
}
12501250
generateIndentedSeparator();
12511251
generateBuilderPropertySetMethods();
1252+
generateBuilderPropertyAddMethods();
1253+
generateBuilderPropertyPutMethods();
12521254
generateIndentedSeparator();
12531255
generateBuilderToString();
12541256
addLine(1, "}");
@@ -1395,6 +1397,22 @@ private void generateBuilderPropertySetMethods() {
13951397
}
13961398
}
13971399

1400+
private void generateBuilderPropertyAddMethods() {
1401+
if (data.isEffectiveBuilderScopeVisible()) {
1402+
for (PropertyGen prop : nonDerivedProperties()) {
1403+
addLines(prop.generateBuilderAddMethod());
1404+
}
1405+
}
1406+
}
1407+
1408+
private void generateBuilderPropertyPutMethods() {
1409+
if (data.isEffectiveBuilderScopeVisible()) {
1410+
for (PropertyGen prop : nonDerivedProperties()) {
1411+
addLines(prop.generateBuilderPutMethod());
1412+
}
1413+
}
1414+
}
1415+
13981416
private void generateBuilderToString() {
13991417
List<PropertyGen> nonDerived = toStringProperties();
14001418
if (data.isImmutable() && data.isTypeFinal()) {

Diff for: src/main/java/org/joda/beans/gen/BeanGenConfig.java

+41-3
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ public final class BeanGenConfig {
5050
* The builder generators.
5151
*/
5252
private final Map<String, BuilderGen> builderGenerators;
53+
/**
54+
* The collection generators.
55+
*/
56+
private final Map<String, CollectionGen> collectionGenerators;
5357
/**
5458
* The invalid immutable types.
5559
*/
@@ -96,7 +100,7 @@ public static BeanGenConfig parse(String resourceLocator) {
96100
if (loader == null) {
97101
throw new IllegalArgumentException("ClassLoader was null: " + fullFile);
98102
}
99-
URL url = loader.getResource(fullFile);
103+
URL url = BeanGenConfig.class.getResource("guava.ini");
100104
if (url == null) {
101105
throw new IllegalArgumentException("Configuration file not found: " + fullFile);
102106
}
@@ -131,6 +135,7 @@ private static BeanGenConfig parse(List<String> lines) {
131135
Map<String, String> immutableGetClones = new HashMap<>();
132136
Map<String, String> immutableVarArgs = new HashMap<>();
133137
Map<String, String> builderInits = new HashMap<>();
138+
Map<String, String> builderAdds = new HashMap<>();
134139
Map<String, String> builderTypes = new HashMap<>();
135140
Set<String> invalidImmutableTypes = new HashSet<>();
136141
for (ListIterator<String> iterator = lines.listIterator(); iterator.hasNext(); ) {
@@ -238,6 +243,21 @@ private static BeanGenConfig parse(List<String> lines) {
238243
String value = line.substring(pos + 1).trim();
239244
builderInits.put(key, value);
240245
}
246+
} else if (line.equals("[immutable.builder.add]")){
247+
while (iterator.hasNext()) {
248+
line = iterator.next().trim();
249+
if (line.startsWith("[")) {
250+
iterator.previous();
251+
break;
252+
}
253+
int pos = line.indexOf('=');
254+
if (pos <= 0) {
255+
throw new IllegalArgumentException("Invalid ini file line: " + line);
256+
}
257+
String key = line.substring(0, pos).trim();
258+
String value = line.substring(pos + 1).trim();
259+
builderAdds.put(key, value);
260+
}
241261
} else {
242262
throw new IllegalArgumentException("Invalid ini file section: " + line);
243263
}
@@ -261,7 +281,13 @@ private static BeanGenConfig parse(List<String> lines) {
261281
}
262282
copyGenerators.put(fieldType, new CopyGen.PatternCopyGen(immutableCopier, mutableCopier));
263283
}
264-
return new BeanGenConfig(copyGenerators, builderGenerators, builderTypes, invalidImmutableTypes, immutableVarArgs, immutableGetClones);
284+
Map<String, CollectionGen> collectionGenerators = new HashMap<>();
285+
for (Entry<String, String> entry : builderAdds.entrySet()) {
286+
String fieldType = entry.getKey();
287+
String adder = entry.getValue();
288+
collectionGenerators.put(fieldType, new CollectionGen.PatternCollectionGen(adder));
289+
}
290+
return new BeanGenConfig(copyGenerators, builderGenerators, collectionGenerators, builderTypes, invalidImmutableTypes, immutableVarArgs, immutableGetClones);
265291
}
266292

267293
//-----------------------------------------------------------------------
@@ -270,6 +296,7 @@ private static BeanGenConfig parse(List<String> lines) {
270296
*
271297
* @param copyGenerators the copy generators, not null
272298
* @param builderGenerators the builder generators, not null
299+
* @param collectionGenerators the collection generators, not null
273300
* @param builderTypes the builder types, not null
274301
* @param invalidImmutableTypes the invalid immutable types, not null
275302
* @param immutableVarArgs the varargs code
@@ -278,12 +305,14 @@ private static BeanGenConfig parse(List<String> lines) {
278305
private BeanGenConfig(
279306
Map<String, CopyGen> copyGenerators,
280307
Map<String, BuilderGen> builderGenerators,
308+
Map<String, CollectionGen> collectionGenerators,
281309
Map<String, String> builderTypes,
282310
Set<String> invalidImmutableTypes,
283311
Map<String, String> immutableVarArgs,
284312
Map<String, String> immutableGetClones) {
285313
this.copyGenerators = copyGenerators;
286314
this.builderGenerators = builderGenerators;
315+
this.collectionGenerators = collectionGenerators;
287316
this.builderTypes = builderTypes;
288317
this.invalidImmutableTypes = invalidImmutableTypes;
289318
this.immutableVarArgs = immutableVarArgs;
@@ -302,13 +331,22 @@ public Map<String, CopyGen> getCopyGenerators() {
302331

303332
/**
304333
* The builder generators.
305-
*
334+
*
306335
* @return the generators, not null
307336
*/
308337
public Map<String, BuilderGen> getBuilderGenerators() {
309338
return builderGenerators;
310339
}
311340

341+
/**
342+
* The collection generators.
343+
*
344+
* @return the generators, not null
345+
*/
346+
public Map<String, CollectionGen> getCollectionGenerators() {
347+
return collectionGenerators;
348+
}
349+
312350
/**
313351
* The builder types.
314352
*

Diff for: src/main/java/org/joda/beans/gen/CollectionGen.java

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2019-present Stephen Colebourne
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.joda.beans.gen;
17+
18+
/**
19+
* A generator of collection code.
20+
*/
21+
abstract class CollectionGen {
22+
23+
/**
24+
* Generates code to add an element to a collection.
25+
*
26+
* @param existing the name of an existing collection or an expression evaluating to an instance of a collection, not null
27+
* @param collectionTypeParams the type params of the collection, may be empty
28+
* @param value the name of the value to add to the collection or an expression evaluating to an instance of a collection, not null
29+
* @return the generated code, not null
30+
*/
31+
abstract String generateAddToCollection(String existing, String collectionTypeParams, String value);
32+
33+
/**
34+
* Generates code to add a new key/value pair to a map.
35+
*
36+
* @param existing the name of an existing map or an expression evaluating to an instance of a map, not null
37+
* @param mapTypeParams the type params of the map, may be empty
38+
* @param key the name of the key to add to the map or an expression evaluating to an instance of the key type, not null
39+
* @param value the name of the value to add to the map or an expression evaluating to an instance of the value type, not null
40+
* @return the generated code, not null
41+
*/
42+
abstract String generateAddToMap(String existing, String mapTypeParams, String key, String value);
43+
44+
static class PatternCollectionGen extends CollectionGen {
45+
private final String pattern;
46+
47+
PatternCollectionGen(String pattern) {
48+
this.pattern = pattern;
49+
}
50+
51+
@Override
52+
String generateAddToCollection(String existing, String collectionTypeParams, String value) {
53+
return generateAddToMap(existing, collectionTypeParams, "", value);
54+
}
55+
56+
@Override
57+
String generateAddToMap(String existing, String mapTypeParams, String key, String value) {
58+
return pattern.replaceAll("\\$existing", existing).replaceAll("\\$params", mapTypeParams).replaceAll("\\$value", value).replaceAll("\\$key", key);
59+
}
60+
}
61+
}

Diff for: src/main/java/org/joda/beans/gen/PropertyData.java

+71-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.io.File;
2121
import java.util.ArrayList;
2222
import java.util.Arrays;
23+
import java.util.Collections;
2324
import java.util.HashSet;
2425
import java.util.List;
2526
import java.util.Set;
@@ -119,6 +120,8 @@ class PropertyData {
119120
private CopyGen copyGen;
120121
/** The builder generator. */
121122
private BuilderGen builderGen;
123+
/** The collection generator. */
124+
private CollectionGen collectionGen;
122125
/** The config. */
123126
private BeanGenConfig config;
124127

@@ -658,6 +661,19 @@ public String getGenericParamType() {
658661
return type.substring(pos + 1, type.length() - 1);
659662
}
660663

664+
/**
665+
* Gets the parameterisations of the property.
666+
* {@code Map<String, Object>} will return a list of {@code String} and {@code Object}.
667+
* @return the generic types, or an empty list if not generic, not null
668+
*/
669+
public List<String> getGenericParamTypes() {
670+
String params = getGenericParamType();
671+
if (params.isEmpty()) {
672+
return Collections.emptyList();
673+
}
674+
return separateGenericParamTypes(params);
675+
}
676+
661677
/**
662678
* Checks if the type is the generic type of the bean.
663679
* For example, if the property is of type T or T[] in a bean of Foo[T].
@@ -984,7 +1000,7 @@ public CopyGen getCopyGen() {
9841000

9851001
//-----------------------------------------------------------------------
9861002
/**
987-
* Resolves the copy generator.
1003+
* Resolves the builder generator.
9881004
*/
9891005
public void resolveBuilderGen() {
9901006
if (getBean().isMutable()) {
@@ -1012,6 +1028,29 @@ public BuilderGen getBuilderGen() {
10121028
return builderGen;
10131029
}
10141030

1031+
//-----------------------------------------------------------------------
1032+
/**
1033+
* Resolves the collection generator.
1034+
*/
1035+
public void resolveCollectionGen() {
1036+
if (getBean().isMutable()) {
1037+
if (!getBean().isBuilderScopeVisible() && !getBean().isBeanStyleLightOrMinimal()) {
1038+
return; // no builder
1039+
}
1040+
}
1041+
if (!isDerived()) {
1042+
collectionGen = config.getCollectionGenerators().get(getFieldTypeRaw());
1043+
}
1044+
}
1045+
1046+
/**
1047+
* Gets the collection generator.
1048+
* @return the collection generator
1049+
*/
1050+
public CollectionGen getCollectionGen() {
1051+
return collectionGen;
1052+
}
1053+
10151054
//-----------------------------------------------------------------------
10161055
/**
10171056
* Checks if this property is an array type.
@@ -1157,4 +1196,35 @@ public String getVarArgsCode() {
11571196
return config.getImmutableVarArgs().get(getTypeRaw());
11581197
}
11591198

1199+
/**
1200+
* Take a string of type parameters (such as (@code String, List<String>} and separate it out
1201+
* into the top-level types it contains.
1202+
*
1203+
* @param typesString the param type string
1204+
* @return a list of the top-level types contained within the string (including their own parameter types)
1205+
*/
1206+
private static List<String> separateGenericParamTypes(String typesString) {
1207+
List<String> types = new ArrayList<>();
1208+
1209+
StringBuilder typeBuilder = new StringBuilder();
1210+
int nestingDepth = 0;
1211+
for (Character character : typesString.toCharArray()) {
1212+
if (character.equals('<')) {
1213+
nestingDepth += 1;
1214+
typeBuilder.append(character);
1215+
} else if (character.equals('>')) {
1216+
nestingDepth -= 1;
1217+
typeBuilder.append(character);
1218+
} else if (character.equals(',') && nestingDepth == 0) {
1219+
types.add(typeBuilder.toString().trim());
1220+
typeBuilder = new StringBuilder();
1221+
} else {
1222+
typeBuilder.append(character);
1223+
}
1224+
}
1225+
1226+
types.add(typeBuilder.toString().trim());
1227+
1228+
return types;
1229+
}
11601230
}

0 commit comments

Comments
 (0)