Compare commits

..

1 Commits

Author SHA1 Message Date
Jordan Zimmerman
dfedb0a5e0 In some JDKs the accessors list can be null
Possible fix for #139
2023-02-18 08:45:32 +00:00
91 changed files with 1174 additions and 1298 deletions

60
pom.xml
View File

@@ -1,28 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2019 The original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.soabase.record-builder</groupId>
<artifactId>record-builder</artifactId>
<packaging>pom</packaging>
<version>38-SNAPSHOT</version>
<version>36-SNAPSHOT</version>
<modules>
<module>record-builder-core</module>
@@ -42,7 +25,7 @@
<maven-source-plugin-version>3.2.0</maven-source-plugin-version>
<maven-install-plugin-version>2.5.2</maven-install-plugin-version>
<maven-deploy-plugin-version>2.8.2</maven-deploy-plugin-version>
<maven-license-plugin-version>4.1</maven-license-plugin-version>
<maven-license-plugin-version>1.9.0</maven-license-plugin-version>
<maven-gpg-plugin-version>1.6</maven-gpg-plugin-version>
<maven-javadoc-plugin-version>3.1.1</maven-javadoc-plugin-version>
<maven-clean-plugin-version>3.1.0</maven-clean-plugin-version>
@@ -52,13 +35,11 @@
<maven-surefire-plugin-version>3.0.0-M5</maven-surefire-plugin-version>
<jacoco-maven-plugin-version>0.8.7</jacoco-maven-plugin-version>
<formatter-maven-plugin-version>2.22.0</formatter-maven-plugin-version>
<license-file-path>src/etc/header.txt</license-file-path>
<javapoet-version>1.12.1</javapoet-version>
<junit-jupiter-version>5.5.2</junit-jupiter-version>
<assertj-core.version>3.24.2</assertj-core.version>
<asm-version>7.2</asm-version>
<validation-api-version>2.0.1.Final</validation-api-version>
<hibernate-validator-version>6.0.20.Final</hibernate-validator-version>
@@ -152,12 +133,6 @@
<version>${junit-jupiter-version}</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj-core.version}</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
@@ -220,8 +195,8 @@
</plugin>
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<groupId>com.mycila.maven-license-plugin</groupId>
<artifactId>maven-license-plugin</artifactId>
<version>${maven-license-plugin-version}</version>
<configuration>
<header>${license-file-path}</header>
@@ -361,24 +336,6 @@
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco-maven-plugin-version}</version>
</plugin>
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>${formatter-maven-plugin-version}</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>format</goal>
</goals>
<configuration>
<compilerCompliance>17</compilerCompliance>
<compilerSource>17</compilerSource>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
@@ -399,8 +356,8 @@
</plugin>
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<groupId>com.mycila.maven-license-plugin</groupId>
<artifactId>maven-license-plugin</artifactId>
</plugin>
<plugin>
@@ -417,11 +374,6 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

View File

@@ -1,26 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2019 The original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>io.soabase.record-builder</groupId>
<artifactId>record-builder</artifactId>
<version>38-SNAPSHOT</version>
<version>36-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,47 +15,44 @@
*/
package io.soabase.recordbuilder.core;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.lang.model.element.Modifier;
import java.lang.annotation.*;
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
@Inherited
public @interface RecordBuilder {
@Target({ ElementType.TYPE, ElementType.PACKAGE })
@Target({ElementType.TYPE, ElementType.PACKAGE})
@Retention(RetentionPolicy.SOURCE)
@Inherited
@interface Include {
/**
* @return collection of classes to include
* @return list of classes to include
*/
Class<?>[] value() default {};
/**
* Synonym for {@code value()}. When using the other attributes it maybe clearer to use {@code classes()}
* instead of {@code value()}. Note: both attributes are applied (i.e. a union of classes from both attributes).
* Synonym for {@code value()}. When using the other attributes it maybe more clear to
* use {@code classes()} instead of {@code value()}. Note: both attributes are applied
* (i.e. a union of classes from both attributes).
*
* @return collection of classes
* @return list of classes
*/
Class<?>[] classes() default {};
/**
* Optional list of package names. All records in the packages will get processed as if they were listed as
* classes to include.
* Optional list of package names. All records in the packages will get processed as
* if they were listed as classes to include.
*
* @return collection of package names
* @return list of package names
*/
String[] packages() default {};
/**
* Pattern used to generate the package for the generated class. The value is the literal package name however
* two replacement values can be used. '@' is replaced with the package of the {@code Include} annotation. '*'
* is replaced with the package of the included class.
* Pattern used to generate the package for the generated class. The value
* is the literal package name however two replacement values can be used. '@'
* is replaced with the package of the {@code Include} annotation. '*' is replaced with
* the package of the included class.
*
* @return package pattern
*/
@@ -63,18 +60,18 @@ public @interface RecordBuilder {
}
@Retention(RetentionPolicy.SOURCE)
@Target({ ElementType.TYPE, ElementType.PACKAGE })
@Target(ElementType.TYPE)
@Inherited
@interface Options {
/**
* The builder class name will be the name of the record (prefixed with any enclosing class) plus this suffix.
* E.g. if the record name is "Foo", the builder will be named "FooBuilder".
* The builder class name will be the name of the record (prefixed with any enclosing class) plus this suffix. E.g.
* if the record name is "Foo", the builder will be named "FooBuilder".
*/
String suffix() default "Builder";
/**
* Used by {@code RecordInterface}. The generated record will have the same name as the annotated interface plus
* this suffix. E.g. if the interface name is "Foo", the record will be named "FooRecord".
* Used by {@code RecordInterface}. The generated record will have the same name as the annotated interface
* plus this suffix. E.g. if the interface name is "Foo", the record will be named "FooRecord".
*/
String interfaceSuffix() default "Record";
@@ -129,20 +126,22 @@ public @interface RecordBuilder {
String fileIndent() default " ";
/**
* If the record is declared inside another class, the outer class's name will be prefixed to the builder name
* if this returns true.
* If the record is declared inside of another class, the outer class's name will
* be prefixed to the builder name if this returns true.
*/
boolean prefixEnclosingClassNames() default true;
/**
* If true, any annotations (if applicable) on record components are copied to the builder methods
* If true, any annotations (if applicable) on record components are copied
* to the builder methods
*
* @return true/false
*/
boolean inheritComponentAnnotations() default true;
/**
* Set the default value of {@code Optional} record components to {@code Optional.empty()}
* Set the default value of {@code Optional} record components to
* {@code Optional.empty()}
*/
boolean emptyDefaultForOptional() default true;
@@ -152,8 +151,8 @@ public @interface RecordBuilder {
boolean addConcreteSettersForOptional() default false;
/**
* Add not-null checks for record components annotated with any annotation named either "NotNull", "NoNull", or
* "NonNull" (see {@link #interpretNotNullsPattern()} for the actual regex matching pattern).
* Add not-null checks for record components annotated with any annotation named either "NotNull",
* "NoNull", or "NonNull" (see {@link #interpretNotNullsPattern()} for the actual regex matching pattern).
*/
boolean interpretNotNulls() default false;
@@ -164,51 +163,29 @@ public @interface RecordBuilder {
String interpretNotNullsPattern() default "(?i)((notnull)|(nonnull)|(nonull))";
/**
* <p>
* Pass built records through the Java Validation API if it's available in the classpath.
* </p>
* <p>Pass built records through the Java Validation API if it's available in the classpath.</p>
*
* <p>
* IMPORTANT: if this option is enabled you must include the {@code record-builder-validator} dependency in
* addition to {@code record-builder-core}. {@code record-builder-validator} is implemented completely via
* reflection and does not require other dependencies. Alternatively, you can define your own class with the
* package {@code package io.soabase.recordbuilder.validator;} named {@code RecordBuilderValidator} which has a
* public static method: {@code public static <T> T validate(T o)}.
* </p>
* <p>IMPORTANT:
* if this option is enabled you must include the {@code record-builder-validator} dependency in addition
* to {@code record-builder-core}. {@code record-builder-validator} is implemented completely via reflection and
* does not require other dependencies. Alternatively, you can define your own class with the package {@code package io.soabase.recordbuilder.validator;}
* named {@code RecordBuilderValidator} which has a public static method: {@code public static <T> T validate(T o)}.</p>
*/
boolean useValidationApi() default false;
/**
* Adds special handling for record components of type {@link java.util.List}, {@link java.util.Set},
* {@link java.util.Map} and {@link java.util.Collection}. When the record is built, any components of these
* types are passed through an added shim method that uses the corresponding immutable collection (e.g.
* {@code List.copyOf(o)}) or an empty immutable collection if the component is {@code null}.
*
* @see #useUnmodifiableCollections()
* {@link java.util.Map} and {@link java.util.Collection}. When the record is built, any components
* of these types are passed through an added shim method that uses the corresponding immutable collection
* (e.g. {@code List.copyOf(o)}) or an empty immutable collection if the component is {@code null}.
*/
boolean useImmutableCollections() default false;
/**
* Adds special handling for record components of type: {@link java.util.List}, {@link java.util.Set},
* {@link java.util.Map} and {@link java.util.Collection}. When the record is built, any components of these
* types are passed through an added shim method that uses the corresponding unmodifiable collection (e.g.
* {@code Collections.unmodifiableList(o)}) or an empty immutable collection if the component is {@code null}.
*
* <p>
* For backward compatibility, when {@link #useImmutableCollections()} returns {@code true}, this property is
* ignored.
*
* @see #useImmutableCollections()
*
* @since 37
*/
boolean useUnmodifiableCollections() default false;
/**
* When enabled, collection types ({@code List}, {@code Set} and {@code Map}) are handled specially. The setters
* for these types now create an internal collection and items are added to that collection. Additionally,
* "adder" methods prefixed with {@link #singleItemBuilderPrefix()} are created to add single items to these
* collections.
* When enabled, collection types ({@code List}, {@code Set} and {@code Map}) are handled specially.
* The setters for these types now create an internal collection and items are added to that
* collection. Additionally, "adder" methods prefixed with {@link #singleItemBuilderPrefix()} are created
* to add single items to these collections.
*/
boolean addSingleItemCollectionBuilders() default false;
@@ -218,14 +195,14 @@ public @interface RecordBuilder {
String singleItemBuilderPrefix() default "add";
/**
* When enabled, adds functional methods to the nested "With" class (such as {@code map()} and
* {@code accept()}).
* When enabled, adds functional methods to the nested "With" class (such as {@code map()} and {@code accept()}).
*/
boolean addFunctionalMethodsToWith() default false;
/**
* If set, all builder setter methods will be prefixed with this string. Camel-casing will still be enforced, so
* if this option is set to "set" a field named "myField" will get a corresponding setter named "setMyField".
* If set, all builder setter methods will be prefixed with this string. Camel-casing will
* still be enforced, so if this option is set to "set" a field named "myField" will get
* a corresponding setter named "setMyField".
*/
String setterPrefix() default "";
@@ -235,41 +212,45 @@ public @interface RecordBuilder {
boolean enableGetters() default true;
/**
* If set, all builder getter methods will be prefixed with this string. Camel-casing will still be enforced, so
* if this option is set to "get", a field named "myField" will get a corresponding getter named "getMyField".
* If set, all builder getter methods will be prefixed with this string. Camel-casing will
* still be enforced, so if this option is set to "get", a field named "myField" will get
* a corresponding getter named "getMyField".
*/
String getterPrefix() default "";
/**
* If set, all boolean builder getter methods will be prefixed with this string. Camel-casing will still be
* enforced, so if this option is set to "is", a field named "myField" will get a corresponding getter named
* "isMyField".
* If set, all boolean builder getter methods will be prefixed with this string.
* Camel-casing will still be enforced, so if this option is set to "is", a field named
* "myField" will get a corresponding getter named "isMyField".
*/
String booleanPrefix() default "";
/**
* If set, the Builder will contain an internal interface with this name. This interface contains getters for
* all the fields in the Record prefixed with the value supplied in {@link this.getterPrefix} and
* {@link this.booleanPrefix}. This interface can be implemented by the original Record to have proper
* bean-style prefixed getters. Please note that unless either of the aforementioned prefixes are set, this
* option does nothing.
* If set, the Builder will contain an internal interface with this name. This interface
* contains getters for all the fields in the Record prefixed with the value supplied in
* {@link this.getterPrefix} and {@link this.booleanPrefix}. This interface can be
* implemented by the original Record to have proper bean-style prefixed getters.
*
* Please note that unless either of the aforementioned prefixes are set,
* this option does nothing.
*/
String beanClassName() default "";
/**
* If true, generated classes are annotated with {@code RecordBuilderGenerated} which has a retention policy of
* {@code CLASS}. This ensures that analyzers such as Jacoco will ignore the generated class.
* If true, generated classes are annotated with {@code RecordBuilderGenerated} which has a retention
* policy of {@code CLASS}. This ensures that analyzers such as Jacoco will ignore the generated class.
*/
boolean addClassRetainedGenerated() default false;
/**
* The {@link #fromMethodName} method instantiates an internal private class. This is the name of that class.
* The {@link #fromMethodName} method instantiates an internal private class. This is the
* name of that class.
*/
String fromWithClassName() default "_FromWith";
/**
* If true, a functional-style builder is added so that record instances can be instantiated without
* {@code new}.
* If true, a functional-style builder is added so that record instances can be instantiated
* without {@code new}.
*/
boolean addStaticBuilder() default true;
@@ -291,8 +272,9 @@ public @interface RecordBuilder {
*/
String mutableMapClassName() default "_MutableMap";
/**
* Any additional {@link javax.lang.model.element.Modifier} you wish to apply to the builder. For example to
* Any additional {@link javax.lang.model.element.Modifier} you wish to apply to the builder. For example to
* make the builder public when the record is package protect.
*/
Modifier[] builderClassModifiers() default {};

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,12 +17,19 @@ package io.soabase.recordbuilder.core;
import java.lang.annotation.*;
/**
* An alternate form of {@code @RecordBuilder} that has most optional features turned on
*/
@RecordBuilder.Template(options = @RecordBuilder.Options(interpretNotNulls = true, useImmutableCollections = true, addSingleItemCollectionBuilders = true, addFunctionalMethodsToWith = true, addClassRetainedGenerated = true))
@RecordBuilder.Template(options = @RecordBuilder.Options(
interpretNotNulls = true,
useImmutableCollections = true,
addSingleItemCollectionBuilders = true,
addFunctionalMethodsToWith = true,
addClassRetainedGenerated = true
))
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
@Inherited
/**
* An alternate form of {@code @RecordBuilder} that has most
* optional features turned on
*/
public @interface RecordBuilderFull {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,7 +24,7 @@ import static java.lang.annotation.ElementType.*;
/**
* Jacoco ignores classes and methods annotated with `*Generated`
*/
@Target({ PACKAGE, TYPE, METHOD, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, PARAMETER })
@Target({PACKAGE, TYPE, METHOD, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, PARAMETER})
@Retention(RetentionPolicy.CLASS)
public @interface RecordBuilderGenerated {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,20 +23,21 @@ import java.lang.annotation.*;
public @interface RecordInterface {
boolean addRecordBuilder() default true;
@Target({ ElementType.TYPE, ElementType.PACKAGE })
@Target({ElementType.TYPE, ElementType.PACKAGE})
@Retention(RetentionPolicy.SOURCE)
@Inherited
@interface Include {
/**
* @return collection of classes to include
* @return list of classes to include
*/
Class<?>[] value() default {};
/**
* Synonym for {@code value()}. When using the other attributes it maybe clearer to use {@code classes()}
* instead of {@code value()}. Note: both attributes are applied (i.e. a union of classes from both attributes).
* Synonym for {@code value()}. When using the other attributes it maybe more clear to
* use {@code classes()} instead of {@code value()}. Note: both attributes are applied
* (i.e. a union of classes from both attributes).
*
* @return collection of classes
* @return list of classes
*/
Class<?>[] classes() default {};
@@ -48,9 +49,10 @@ public @interface RecordInterface {
boolean addRecordBuilder() default true;
/**
* Pattern used to generate the package for the generated class. The value is the literal package name however
* two replacement values can be used. '@' is replaced with the package of the {@code Include} annotation. '*'
* is replaced with the package of the included class.
* Pattern used to generate the package for the generated class. The value
* is the literal package name however two replacement values can be used. '@'
* is replaced with the package of the {@code Include} annotation. '*' is replaced with
* the package of the included class.
*
* @return package pattern
*/

View File

@@ -1,26 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2019 The original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>io.soabase.record-builder</groupId>
<artifactId>record-builder</artifactId>
<version>38-SNAPSHOT</version>
<version>36-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,6 @@ import static io.soabase.recordbuilder.processor.RecordBuilderProcessor.recordBu
class CollectionBuilderUtils {
private final boolean useImmutableCollections;
private final boolean useUnmodifiableCollections;
private final boolean addSingleItemCollectionBuilders;
private final boolean addClassRetainedGenerated;
private final String listShimName;
@@ -51,24 +50,18 @@ class CollectionBuilderUtils {
private static final Class<?> mapType = Map.class;
private static final Class<?> setType = Set.class;
private static final Class<?> collectionType = Collection.class;
private static final Class<?> collectionsType = Collections.class;
private static final TypeName listTypeName = TypeName.get(listType);
private static final TypeName mapTypeName = TypeName.get(mapType);
private static final TypeName setTypeName = TypeName.get(setType);
private static final TypeName collectionTypeName = TypeName.get(collectionType);
private static final TypeName collectionsTypeName = TypeName.get(collectionsType);
private static final TypeVariableName tType = TypeVariableName.get("T");
private static final TypeVariableName kType = TypeVariableName.get("K");
private static final TypeVariableName vType = TypeVariableName.get("V");
private static final ParameterizedTypeName parameterizedListType = ParameterizedTypeName
.get(ClassName.get(List.class), tType);
private static final ParameterizedTypeName parameterizedMapType = ParameterizedTypeName
.get(ClassName.get(Map.class), kType, vType);
private static final ParameterizedTypeName parameterizedSetType = ParameterizedTypeName
.get(ClassName.get(Set.class), tType);
private static final ParameterizedTypeName parameterizedCollectionType = ParameterizedTypeName
.get(ClassName.get(Collection.class), tType);
private static final ParameterizedTypeName parameterizedListType = ParameterizedTypeName.get(ClassName.get(List.class), tType);
private static final ParameterizedTypeName parameterizedMapType = ParameterizedTypeName.get(ClassName.get(Map.class), kType, vType);
private static final ParameterizedTypeName parameterizedSetType = ParameterizedTypeName.get(ClassName.get(Set.class), tType);
private static final ParameterizedTypeName parameterizedCollectionType = ParameterizedTypeName.get(ClassName.get(Collection.class), tType);
private static final Class<?> mutableListType = ArrayList.class;
private static final Class<?> mutableMapType = HashMap.class;
@@ -82,7 +75,6 @@ class CollectionBuilderUtils {
CollectionBuilderUtils(List<RecordClassType> recordComponents, RecordBuilder.Options metaData) {
useImmutableCollections = metaData.useImmutableCollections();
useUnmodifiableCollections = !useImmutableCollections && metaData.useUnmodifiableCollections();
addSingleItemCollectionBuilders = metaData.addSingleItemCollectionBuilders();
addClassRetainedGenerated = metaData.addClassRetainedGenerated();
@@ -95,24 +87,22 @@ class CollectionBuilderUtils {
setMakerMethodName = disambiguateGeneratedMethodName(recordComponents, "__ensureSetMutable", 0);
mapMakerMethodName = disambiguateGeneratedMethodName(recordComponents, "__ensureMapMutable", 0);
mutableListSpec = buildMutableCollectionSubType(metaData.mutableListClassName(), mutableListTypeName,
parameterizedListType, tType);
mutableSetSpec = buildMutableCollectionSubType(metaData.mutableSetClassName(), mutableSetTypeName,
parameterizedSetType, tType);
mutableMapSpec = buildMutableCollectionSubType(metaData.mutableMapClassName(), mutableMapTypeName,
parameterizedMapType, kType, vType);
mutableListSpec = buildMutableCollectionSubType(metaData.mutableListClassName(), mutableListTypeName, parameterizedListType, tType);
mutableSetSpec = buildMutableCollectionSubType(metaData.mutableSetClassName(), mutableSetTypeName, parameterizedSetType, tType);
mutableMapSpec = buildMutableCollectionSubType(metaData.mutableMapClassName(), mutableMapTypeName, parameterizedMapType, kType, vType);
}
enum SingleItemsMetaDataMode {
STANDARD, STANDARD_FOR_SETTER, EXCLUDE_WILDCARD_TYPES
STANDARD,
STANDARD_FOR_SETTER,
EXCLUDE_WILDCARD_TYPES
}
record SingleItemsMetaData(Class<?> singleItemCollectionClass, List<TypeName> typeArguments, TypeName wildType) {
}
Optional<SingleItemsMetaData> singleItemsMetaData(RecordClassType component, SingleItemsMetaDataMode mode) {
if (addSingleItemCollectionBuilders
&& (component.typeName() instanceof ParameterizedTypeName parameterizedTypeName)) {
if (addSingleItemCollectionBuilders && (component.typeName() instanceof ParameterizedTypeName parameterizedTypeName)) {
Class<?> collectionClass = null;
ClassName wildcardClass = null;
int typeArgumentQty = 0;
@@ -132,25 +122,21 @@ class CollectionBuilderUtils {
var hasWildcardTypeArguments = hasWildcardTypeArguments(parameterizedTypeName, typeArgumentQty);
if (collectionClass != null) {
return switch (mode) {
case STANDARD -> singleItemsMetaDataWithWildType(parameterizedTypeName, collectionClass, wildcardClass,
typeArgumentQty);
case STANDARD -> singleItemsMetaDataWithWildType(parameterizedTypeName, collectionClass, wildcardClass, typeArgumentQty);
case STANDARD_FOR_SETTER -> {
if (hasWildcardTypeArguments) {
yield Optional.of(new SingleItemsMetaData(collectionClass, parameterizedTypeName.typeArguments,
component.typeName()));
case STANDARD_FOR_SETTER -> {
if (hasWildcardTypeArguments) {
yield Optional.of(new SingleItemsMetaData(collectionClass, parameterizedTypeName.typeArguments, component.typeName()));
}
yield singleItemsMetaDataWithWildType(parameterizedTypeName, collectionClass, wildcardClass, typeArgumentQty);
}
yield singleItemsMetaDataWithWildType(parameterizedTypeName, collectionClass, wildcardClass,
typeArgumentQty);
}
case EXCLUDE_WILDCARD_TYPES -> {
if (hasWildcardTypeArguments) {
yield Optional.empty();
case EXCLUDE_WILDCARD_TYPES -> {
if (hasWildcardTypeArguments) {
yield Optional.empty();
}
yield singleItemsMetaDataWithWildType(parameterizedTypeName, collectionClass, wildcardClass, typeArgumentQty);
}
yield singleItemsMetaDataWithWildType(parameterizedTypeName, collectionClass, wildcardClass,
typeArgumentQty);
}
};
}
}
@@ -158,8 +144,7 @@ class CollectionBuilderUtils {
}
boolean isImmutableCollection(RecordClassType component) {
return (useImmutableCollections || useUnmodifiableCollections)
&& (isList(component) || isMap(component) || isSet(component) || isCollection(component));
return useImmutableCollections && (isList(component) || isMap(component) || isSet(component) || component.rawTypeName().equals(collectionTypeName));
}
boolean isList(RecordClassType component) {
@@ -174,12 +159,8 @@ class CollectionBuilderUtils {
return component.rawTypeName().equals(setTypeName);
}
private boolean isCollection(RecordClassType component) {
return component.rawTypeName().equals(collectionTypeName);
}
void addShimCall(CodeBlock.Builder builder, RecordClassType component) {
if (useImmutableCollections || useUnmodifiableCollections) {
if (useImmutableCollections) {
if (isList(component)) {
needsListShim = true;
needsListMutableMaker = true;
@@ -192,7 +173,7 @@ class CollectionBuilderUtils {
needsSetShim = true;
needsSetMutableMaker = true;
builder.add("$L($L)", setShimName, component.name());
} else if (isCollection(component)) {
} else if (component.rawTypeName().equals(collectionTypeName)) {
needsCollectionShim = true;
builder.add("$L($L)", collectionShimName, component.name());
} else {
@@ -210,7 +191,7 @@ class CollectionBuilderUtils {
return mapShimName;
} else if (isSet(component)) {
return setShimName;
} else if (isCollection(component)) {
} else if (component.rawTypeName().equals(collectionTypeName)) {
return collectionShimName;
} else {
throw new IllegalArgumentException(component + " is not a supported collection type");
@@ -230,13 +211,12 @@ class CollectionBuilderUtils {
}
void addShims(TypeSpec.Builder builder) {
if (!useImmutableCollections && !useUnmodifiableCollections) {
if (!useImmutableCollections) {
return;
}
if (needsListShim) {
builder.addMethod(
buildShimMethod(listShimName, listTypeName, collectionType, parameterizedListType, tType));
builder.addMethod(buildShimMethod(listShimName, listTypeName, collectionType, parameterizedListType, tType));
}
if (needsSetShim) {
builder.addMethod(buildShimMethod(setShimName, setTypeName, collectionType, parameterizedSetType, tType));
@@ -250,37 +230,30 @@ class CollectionBuilderUtils {
}
void addMutableMakers(TypeSpec.Builder builder) {
if (!useImmutableCollections && !useUnmodifiableCollections) {
if (!useImmutableCollections) {
return;
}
if (needsListMutableMaker) {
builder.addMethod(
buildMutableMakerMethod(listMakerMethodName, mutableListSpec.name, parameterizedListType, tType));
builder.addMethod(buildMutableMakerMethod(listMakerMethodName, mutableListSpec.name, parameterizedListType, tType));
builder.addType(mutableListSpec);
}
if (needsSetMutableMaker) {
builder.addMethod(
buildMutableMakerMethod(setMakerMethodName, mutableSetSpec.name, parameterizedSetType, tType));
builder.addMethod(buildMutableMakerMethod(setMakerMethodName, mutableSetSpec.name, parameterizedSetType, tType));
builder.addType(mutableSetSpec);
}
if (needsMapMutableMaker) {
builder.addMethod(buildMutableMakerMethod(mapMakerMethodName, mutableMapSpec.name, parameterizedMapType,
kType, vType));
builder.addMethod(buildMutableMakerMethod(mapMakerMethodName, mutableMapSpec.name, parameterizedMapType, kType, vType));
builder.addType(mutableMapSpec);
}
}
private Optional<SingleItemsMetaData> singleItemsMetaDataWithWildType(ParameterizedTypeName parameterizedTypeName,
Class<?> collectionClass, ClassName wildcardClass, int typeArgumentQty) {
private Optional<SingleItemsMetaData> singleItemsMetaDataWithWildType(ParameterizedTypeName parameterizedTypeName, Class<?> collectionClass, ClassName wildcardClass, int typeArgumentQty) {
TypeName wildType;
if (typeArgumentQty == 1) {
wildType = ParameterizedTypeName.get(wildcardClass,
WildcardTypeName.subtypeOf(parameterizedTypeName.typeArguments.get(0)));
wildType = ParameterizedTypeName.get(wildcardClass, WildcardTypeName.subtypeOf(parameterizedTypeName.typeArguments.get(0)));
} else { // if (typeArgumentQty == 2)
wildType = ParameterizedTypeName.get(wildcardClass,
WildcardTypeName.subtypeOf(parameterizedTypeName.typeArguments.get(0)),
WildcardTypeName.subtypeOf(parameterizedTypeName.typeArguments.get(1)));
wildType = ParameterizedTypeName.get(wildcardClass, WildcardTypeName.subtypeOf(parameterizedTypeName.typeArguments.get(0)), WildcardTypeName.subtypeOf(parameterizedTypeName.typeArguments.get(1)));
}
return Optional.of(new SingleItemsMetaData(collectionClass, parameterizedTypeName.typeArguments, wildType));
}
@@ -304,65 +277,47 @@ class CollectionBuilderUtils {
return name;
}
private MethodSpec buildShimMethod(String name, TypeName mainType, Class<?> abstractType,
ParameterizedTypeName parameterizedType, TypeVariableName... typeVariables) {
var code = buildShimMethodBody(mainType, parameterizedType);
TypeName[] wildCardTypeArguments = parameterizedType.typeArguments.stream().map(WildcardTypeName::subtypeOf)
.toList().toArray(new TypeName[0]);
private MethodSpec buildShimMethod(String name, TypeName mainType, Class<?> abstractType, ParameterizedTypeName parameterizedType, TypeVariableName... typeVariables) {
var code = CodeBlock.of("return (o != null) ? $T.copyOf(o) : $T.of()", mainType, mainType);
TypeName[] wildCardTypeArguments = parameterizedType.typeArguments.stream().map(WildcardTypeName::subtypeOf).toList().toArray(new TypeName[0]);
var extendedParameterizedType = ParameterizedTypeName.get(ClassName.get(abstractType), wildCardTypeArguments);
return MethodSpec.methodBuilder(name).addAnnotation(generatedRecordBuilderAnnotation)
.addModifiers(Modifier.PRIVATE, Modifier.STATIC).addTypeVariables(Arrays.asList(typeVariables))
.returns(parameterizedType).addParameter(extendedParameterizedType, "o").addStatement(code).build();
return MethodSpec.methodBuilder(name)
.addAnnotation(generatedRecordBuilderAnnotation)
.addModifiers(Modifier.PRIVATE, Modifier.STATIC)
.addTypeVariables(Arrays.asList(typeVariables))
.returns(parameterizedType)
.addParameter(extendedParameterizedType, "o")
.addStatement(code)
.build();
}
private CodeBlock buildShimMethodBody(TypeName mainType, ParameterizedTypeName parameterizedType) {
if (!useUnmodifiableCollections) {
return CodeBlock.of("return (o != null) ? $T.copyOf(o) : $T.of()", mainType, mainType);
}
if (mainType.equals(listTypeName)) {
return CodeBlock.of("return (o != null) ? $T.<$T>unmodifiableList(($T) o) : $T.<$T>emptyList()",
collectionsTypeName, tType, parameterizedType, collectionsTypeName, tType);
}
if (mainType.equals(setTypeName)) {
return CodeBlock.of("return (o != null) ? $T.<$T>unmodifiableSet(($T) o) : $T.<$T>emptySet()",
collectionsTypeName, tType, parameterizedType, collectionsTypeName, tType);
}
if (mainType.equals(mapTypeName)) {
return CodeBlock.of("return (o != null) ? $T.<$T, $T>unmodifiableMap(($T) o) : $T.<$T, $T>emptyMap()",
collectionsTypeName, kType, vType, parameterizedType, collectionsTypeName, kType, vType);
}
throw new IllegalStateException("Cannot build shim method for" + mainType);
}
private MethodSpec buildMutableMakerMethod(String name, String mutableCollectionType,
ParameterizedTypeName parameterizedType, TypeVariableName... typeVariables) {
private MethodSpec buildMutableMakerMethod(String name, String mutableCollectionType, ParameterizedTypeName parameterizedType, TypeVariableName... typeVariables) {
var nullCase = CodeBlock.of("if (o == null) return new $L<>()", mutableCollectionType);
var isMutableCase = CodeBlock.of("if (o instanceof $L) return o", mutableCollectionType);
var defaultCase = CodeBlock.of("return new $L<>(o)", mutableCollectionType);
return MethodSpec.methodBuilder(name).addAnnotation(generatedRecordBuilderAnnotation)
.addModifiers(Modifier.PRIVATE, Modifier.STATIC).addTypeVariables(Arrays.asList(typeVariables))
.returns(parameterizedType).addParameter(parameterizedType, "o").addStatement(nullCase)
.addStatement(isMutableCase).addStatement(defaultCase).build();
return MethodSpec.methodBuilder(name)
.addAnnotation(generatedRecordBuilderAnnotation)
.addModifiers(Modifier.PRIVATE, Modifier.STATIC)
.addTypeVariables(Arrays.asList(typeVariables))
.returns(parameterizedType)
.addParameter(parameterizedType, "o")
.addStatement(nullCase)
.addStatement(isMutableCase)
.addStatement(defaultCase)
.build();
}
private TypeSpec buildMutableCollectionSubType(String className, ClassName mutableCollectionType,
ParameterizedTypeName parameterizedType, TypeVariableName... typeVariables) {
TypeName[] typeArguments = new TypeName[] {};
private TypeSpec buildMutableCollectionSubType(String className, ClassName mutableCollectionType, ParameterizedTypeName parameterizedType, TypeVariableName... typeVariables) {
TypeName[] typeArguments = new TypeName[]{};
typeArguments = Arrays.stream(typeVariables).toList().toArray(typeArguments);
TypeSpec.Builder builder = TypeSpec.classBuilder(className).addAnnotation(generatedRecordBuilderAnnotation)
TypeSpec.Builder builder = TypeSpec.classBuilder(className)
.addAnnotation(generatedRecordBuilderAnnotation)
.addModifiers(Modifier.PRIVATE, Modifier.STATIC)
.superclass(ParameterizedTypeName.get(mutableCollectionType, typeArguments))
.addTypeVariables(Arrays.asList(typeVariables))
.addMethod(MethodSpec.constructorBuilder().addAnnotation(generatedRecordBuilderAnnotation)
.addStatement("super()").build())
.addMethod(MethodSpec.constructorBuilder().addAnnotation(generatedRecordBuilderAnnotation)
.addParameter(parameterizedType, "o").addStatement("super(o)").build());
.addMethod(MethodSpec.constructorBuilder().addAnnotation(generatedRecordBuilderAnnotation).addStatement("super()").build())
.addMethod(MethodSpec.constructorBuilder().addAnnotation(generatedRecordBuilderAnnotation).addParameter(parameterizedType, "o").addStatement("super(o)").build());
if (addClassRetainedGenerated) {
builder.addAnnotation(recordBuilderGeneratedAnnotation);
@@ -372,25 +327,21 @@ class CollectionBuilderUtils {
}
private MethodSpec buildCollectionsShimMethod() {
var code = buildCollectionShimMethodBody();
return MethodSpec.methodBuilder(collectionShimName).addAnnotation(generatedRecordBuilderAnnotation)
.addModifiers(Modifier.PRIVATE, Modifier.STATIC).addTypeVariable(tType)
.returns(parameterizedCollectionType).addParameter(parameterizedCollectionType, "o").addCode(code)
var code = CodeBlock.builder()
.add("if (o instanceof Set) {\n")
.indent()
.addStatement("return $T.copyOf(o)", setTypeName)
.unindent()
.addStatement("}")
.addStatement("return (o != null) ? $T.copyOf(o) : $T.of()", listTypeName, listTypeName)
.build();
return MethodSpec.methodBuilder(collectionShimName)
.addAnnotation(generatedRecordBuilderAnnotation)
.addModifiers(Modifier.PRIVATE, Modifier.STATIC)
.addTypeVariable(tType)
.returns(parameterizedCollectionType)
.addParameter(parameterizedCollectionType, "o")
.addCode(code)
.build();
}
private CodeBlock buildCollectionShimMethodBody() {
if (!useUnmodifiableCollections) {
return CodeBlock.builder().add("if (o instanceof Set) {\n").indent()
.addStatement("return $T.copyOf(o)", setTypeName).unindent().addStatement("}")
.addStatement("return (o != null) ? $T.copyOf(o) : $T.of()", listTypeName, listTypeName).build();
}
return CodeBlock.builder().beginControlFlow("if (o instanceof $T)", listType)
.addStatement("return $T.<$T>unmodifiableList(($T) o)", collectionsTypeName, tType,
parameterizedListType)
.endControlFlow().beginControlFlow("if (o instanceof $T)", setType)
.addStatement("return $T.<$T>unmodifiableSet(($T) o)", collectionsTypeName, tType, parameterizedSetType)
.endControlFlow().addStatement("return $T.<$T>emptyList()", collectionsTypeName, tType).build();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,43 +37,49 @@ import java.util.Optional;
import java.util.stream.Collectors;
public class ElementUtils {
public static Optional<? extends AnnotationMirror> findAnnotationMirror(ProcessingEnvironment processingEnv,
Element element, String annotationClass) {
public static Optional<? extends AnnotationMirror> findAnnotationMirror(ProcessingEnvironment processingEnv, Element element, String annotationClass) {
return processingEnv.getElementUtils().getAllAnnotationMirrors(element).stream()
.filter(e -> e.getAnnotationType().toString().equals(annotationClass)).findFirst();
.filter(e -> e.getAnnotationType().toString().equals(annotationClass))
.findFirst();
}
public static Optional<? extends AnnotationValue> getAnnotationValue(
Map<? extends ExecutableElement, ? extends AnnotationValue> values, String name) {
return values.entrySet().stream().filter(e -> e.getKey().getSimpleName().toString().equals(name))
.map(Map.Entry::getValue).findFirst();
public static Optional<? extends AnnotationValue> getAnnotationValue(Map<? extends ExecutableElement, ? extends AnnotationValue> values, String name) {
return values.entrySet()
.stream()
.filter(e -> e.getKey().getSimpleName().toString().equals(name))
.map(Map.Entry::getValue)
.findFirst();
}
@SuppressWarnings("unchecked")
public static List<TypeMirror> getAttributeTypeMirrorList(AnnotationValue attribute) {
List<? extends AnnotationValue> values = (attribute != null)
? (List<? extends AnnotationValue>) attribute.getValue() : Collections.emptyList();
return values.stream().map(v -> (TypeMirror) v.getValue()).collect(Collectors.toList());
public static List<TypeMirror> getAttributeTypeMirrorList(AnnotationValue attribute)
{
List<? extends AnnotationValue> values = (attribute != null) ? (List<? extends AnnotationValue>)attribute.getValue() : Collections.emptyList();
return values.stream().map(v -> (TypeMirror)v.getValue()).collect(Collectors.toList());
}
@SuppressWarnings("unchecked")
public static List<String> getAttributeStringList(AnnotationValue attribute) {
List<? extends AnnotationValue> values = (attribute != null)
? (List<? extends AnnotationValue>) attribute.getValue() : Collections.emptyList();
return values.stream().map(v -> (String) v.getValue()).collect(Collectors.toList());
public static List<String> getAttributeStringList(AnnotationValue attribute)
{
List<? extends AnnotationValue> values = (attribute != null) ? (List<? extends AnnotationValue>)attribute.getValue() : Collections.emptyList();
return values.stream().map(v -> (String)v.getValue()).collect(Collectors.toList());
}
public static boolean getBooleanAttribute(AnnotationValue attribute) {
public static boolean getBooleanAttribute(AnnotationValue attribute)
{
Object value = (attribute != null) ? attribute.getValue() : null;
if (value != null) {
if ( value != null )
{
return Boolean.parseBoolean(String.valueOf(value));
}
return false;
}
public static String getStringAttribute(AnnotationValue attribute, String defaultValue) {
public static String getStringAttribute(AnnotationValue attribute, String defaultValue)
{
Object value = (attribute != null) ? attribute.getValue() : null;
if (value != null) {
if ( value != null )
{
return String.valueOf(value);
}
return defaultValue;
@@ -93,8 +99,7 @@ public class ElementUtils {
return (index > -1) ? name.substring(0, index) : "";
}
public static ClassType getClassType(String packageName, String simpleName,
List<? extends TypeParameterElement> typeParameters) {
public static ClassType getClassType(String packageName, String simpleName, List<? extends TypeParameterElement> typeParameters) {
return getClassType(ClassName.get(packageName, simpleName), typeParameters);
}
@@ -102,8 +107,7 @@ public class ElementUtils {
return getClassType(ClassName.get(typeElement), typeParameters);
}
public static ClassType getClassType(ClassName builderClassName,
List<? extends TypeParameterElement> typeParameters) {
public static ClassType getClassType(ClassName builderClassName, List<? extends TypeParameterElement> typeParameters) {
if (typeParameters.isEmpty()) {
return new ClassType(builderClassName, builderClassName.simpleName());
}
@@ -111,13 +115,10 @@ public class ElementUtils {
return new ClassType(ParameterizedTypeName.get(builderClassName, typeNames), builderClassName.simpleName());
}
public static RecordClassType getRecordClassType(ProcessingEnvironment processingEnv,
RecordComponentElement recordComponent, List<? extends AnnotationMirror> accessorAnnotations,
List<? extends AnnotationMirror> canonicalConstructorAnnotations) {
public static RecordClassType getRecordClassType(ProcessingEnvironment processingEnv, RecordComponentElement recordComponent, List<? extends AnnotationMirror> accessorAnnotations, List<? extends AnnotationMirror> canonicalConstructorAnnotations) {
var typeName = TypeName.get(recordComponent.asType());
var rawTypeName = TypeName.get(processingEnv.getTypeUtils().erasure(recordComponent.asType()));
return new RecordClassType(typeName, rawTypeName, recordComponent.getSimpleName().toString(),
accessorAnnotations, canonicalConstructorAnnotations);
return new RecordClassType(typeName, rawTypeName, recordComponent.getSimpleName().toString(), accessorAnnotations, canonicalConstructorAnnotations);
}
public static String getWithMethodName(ClassType component, String prefix) {
@@ -128,30 +129,27 @@ public class ElementUtils {
return prefix + Character.toUpperCase(name.charAt(0)) + name.substring(1);
}
public static String getBuilderName(TypeElement element, RecordBuilder.Options metaData, ClassType classType,
String suffix) {
public static String getBuilderName(TypeElement element, RecordBuilder.Options metaData, ClassType classType, String suffix) {
// generate the class name
var baseName = classType.name() + suffix;
return metaData.prefixEnclosingClassNames() ? (getBuilderNamePrefix(element.getEnclosingElement()) + baseName)
: baseName;
return metaData.prefixEnclosingClassNames() ? (getBuilderNamePrefix(element.getEnclosingElement()) + baseName) : baseName;
}
public static Optional<? extends Element> findCanonicalConstructor(TypeElement record) {
if (record.getKind() != ElementKind.RECORD) {
if ( record.getKind() != ElementKind.RECORD ) {
return Optional.empty();
}
// based on
// https://github.com/openjdk/jdk/pull/3556/files#diff-a6270f4b50989abe733607c69038b2036306d13f77276af005d023b7fc57f1a2R2368
var componentList = record.getRecordComponents().stream().map(e -> e.asType().toString())
.collect(Collectors.toList());
return record.getEnclosedElements().stream().filter(element -> element.getKind() == ElementKind.CONSTRUCTOR)
.filter(element -> {
var parameters = ((ExecutableElement) element).getParameters();
var parametersList = parameters.stream().map(e -> e.asType().toString())
.collect(Collectors.toList());
return componentList.equals(parametersList);
}).findFirst();
// based on https://github.com/openjdk/jdk/pull/3556/files#diff-a6270f4b50989abe733607c69038b2036306d13f77276af005d023b7fc57f1a2R2368
var componentList = record.getRecordComponents().stream().map(e -> e.asType().toString()).collect(Collectors.toList());
return record.getEnclosedElements().stream()
.filter(element -> element.getKind() == ElementKind.CONSTRUCTOR)
.filter(element -> {
var parameters = ((ExecutableElement)element).getParameters();
var parametersList = parameters.stream().map(e -> e.asType().toString()).collect(Collectors.toList());
return componentList.equals(parametersList);
})
.findFirst();
}
private static String getBuilderNamePrefix(Element element) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,8 +28,7 @@ class IncludeHelper {
private final List<TypeElement> classTypeElements;
private final Map<? extends ExecutableElement, ? extends AnnotationValue> annotationValues;
IncludeHelper(ProcessingEnvironment processingEnv, Element element, AnnotationMirror annotationMirror,
boolean packagesSupported) {
IncludeHelper(ProcessingEnvironment processingEnv, Element element, AnnotationMirror annotationMirror, boolean packagesSupported) {
annotationValues = processingEnv.getElementUtils().getElementValuesWithDefaults(annotationMirror);
var value = ElementUtils.getAnnotationValue(annotationValues, "value");
var classes = ElementUtils.getAnnotationValue(annotationValues, "classes");
@@ -42,11 +41,9 @@ class IncludeHelper {
var packagesList = packages.map(ElementUtils::getAttributeStringList).orElseGet(List::of);
if (valueList.isEmpty() && classesList.isEmpty() && packagesList.isEmpty()) {
if (packagesSupported) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
"At least one of \"value\", \"classes\" or \"packages\" required", element);
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "At least one of \"value\", \"classes\" or \"packages\" required", element);
} else {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
"At least one of \"value\" or \"classes\" required", element);
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "At least one of \"value\" or \"classes\" required", element);
}
isValid = false;
}
@@ -54,8 +51,7 @@ class IncludeHelper {
isValid = processList(processingEnv, isValid, element, classesList, classTypeElements);
packages.ifPresent(annotationValue -> processPackages(processingEnv, classTypeElements, packagesList));
} else {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Could not read attribute for annotation",
element);
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Could not read attribute for annotation", element);
isValid = false;
}
this.isValid = isValid;
@@ -74,13 +70,11 @@ class IncludeHelper {
return classTypeElements;
}
private boolean processList(ProcessingEnvironment processingEnv, boolean isValid, Element element,
List<TypeMirror> list, ArrayList<TypeElement> classTypeElements) {
private boolean processList(ProcessingEnvironment processingEnv, boolean isValid, Element element, List<TypeMirror> list, ArrayList<TypeElement> classTypeElements) {
for (var typeMirror : list) {
TypeElement typeElement = (TypeElement) processingEnv.getTypeUtils().asElement(typeMirror);
if (typeElement == null) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
"Could not get element for: " + typeMirror, element);
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Could not get element for: " + typeMirror, element);
isValid = false;
} else {
classTypeElements.add(typeElement);
@@ -89,8 +83,7 @@ class IncludeHelper {
return isValid;
}
private void processPackages(ProcessingEnvironment processingEnv, List<TypeElement> classTypeElements,
List<String> packagesList) {
private void processPackages(ProcessingEnvironment processingEnv, List<TypeElement> classTypeElements, List<String> packagesList) {
for (var packageName : packagesList) {
var packageElement = processingEnv.getElementUtils().getPackageElement(packageName);
for (var child : packageElement.getEnclosedElements()) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,33 +51,30 @@ class InternalRecordInterfaceProcessor {
private record Component(ExecutableElement element, Optional<String> alternateName) {
}
InternalRecordInterfaceProcessor(ProcessingEnvironment processingEnv, TypeElement iface, boolean addRecordBuilder,
RecordBuilder.Options metaData, Optional<String> packageNameOpt, boolean fromTemplate) {
InternalRecordInterfaceProcessor(ProcessingEnvironment processingEnv, TypeElement iface, boolean addRecordBuilder, RecordBuilder.Options metaData, Optional<String> packageNameOpt, boolean fromTemplate) {
this.processingEnv = processingEnv;
packageName = packageNameOpt.orElseGet(() -> ElementUtils.getPackageName(iface));
recordComponents = getRecordComponents(iface);
this.iface = iface;
ClassType ifaceClassType = ElementUtils.getClassType(iface, iface.getTypeParameters());
recordClassType = ElementUtils.getClassType(packageName,
getBuilderName(iface, metaData, ifaceClassType, metaData.interfaceSuffix()), iface.getTypeParameters());
List<TypeVariableName> typeVariables = iface.getTypeParameters().stream().map(TypeVariableName::get)
.collect(Collectors.toList());
recordClassType = ElementUtils.getClassType(packageName, getBuilderName(iface, metaData, ifaceClassType, metaData.interfaceSuffix()), iface.getTypeParameters());
List<TypeVariableName> typeVariables = iface.getTypeParameters().stream().map(TypeVariableName::get).collect(Collectors.toList());
MethodSpec methodSpec = generateArgumentList();
TypeSpec.Builder builder = TypeSpec.classBuilder(recordClassType.name()).addSuperinterface(iface.asType())
.addMethod(methodSpec).addModifiers(Modifier.PUBLIC).addAnnotation(generatedRecordInterfaceAnnotation)
TypeSpec.Builder builder = TypeSpec.classBuilder(recordClassType.name())
.addSuperinterface(iface.asType())
.addMethod(methodSpec)
.addModifiers(Modifier.PUBLIC)
.addAnnotation(generatedRecordInterfaceAnnotation)
.addTypeVariables(typeVariables);
if (metaData.addClassRetainedGenerated()) {
builder.addAnnotation(recordBuilderGeneratedAnnotation);
}
if (addRecordBuilder) {
ClassType builderClassType = ElementUtils.getClassType(packageName,
getBuilderName(iface, metaData, recordClassType, metaData.suffix()) + "."
+ metaData.withClassName(),
iface.getTypeParameters());
ClassType builderClassType = ElementUtils.getClassType(packageName, getBuilderName(iface, metaData, recordClassType, metaData.suffix()) + "." + metaData.withClassName(), iface.getTypeParameters());
builder.addAnnotation(RecordBuilder.class);
builder.addSuperinterface(builderClassType.typeName());
if (fromTemplate) {
@@ -115,29 +112,30 @@ class InternalRecordInterfaceProcessor {
// javapoet does yet support records - so a class was created and we can reshape it
// The class will look something like this:
/*
* // Auto generated by io.soabase.recordbuilder.core.RecordBuilder: https://github.com/Randgalt/record-builder
* package io.soabase.recordbuilder.test;
*
* import io.soabase.recordbuilder.core.RecordBuilder; import javax.annotation.processing.Generated;
*
* @Generated("io.soabase.recordbuilder.core.RecordInterface")
*
* @RecordBuilder public class MyRecord implements MyInterface { void __FAKE__(String name, int age) { } }
*/
Pattern pattern = Pattern.compile("(.*)(implements.*)(\\{)(.*" + FAKE_METHOD_NAME + ")(\\(.*\\))(.*)",
Pattern.MULTILINE | Pattern.DOTALL);
// Auto generated by io.soabase.recordbuilder.core.RecordBuilder: https://github.com/Randgalt/record-builder
package io.soabase.recordbuilder.test;
import io.soabase.recordbuilder.core.RecordBuilder;
import javax.annotation.processing.Generated;
@Generated("io.soabase.recordbuilder.core.RecordInterface")
@RecordBuilder
public class MyRecord implements MyInterface {
void __FAKE__(String name, int age) {
}
}
*/
Pattern pattern = Pattern.compile("(.*)(implements.*)(\\{)(.*" + FAKE_METHOD_NAME + ")(\\(.*\\))(.*)", Pattern.MULTILINE | Pattern.DOTALL);
Matcher matcher = pattern.matcher(classSource);
if (!matcher.find() || matcher.groupCount() != 6) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
"Internal error generating record. Group count: " + matcher.groupCount(), iface);
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Internal error generating record. Group count: " + matcher.groupCount(), iface);
}
String declaration = matcher.group(1).trim().replace("class", "record");
String implementsSection = matcher.group(2).trim();
String argumentList = matcher.group(5).trim();
StringBuilder fixedRecord = new StringBuilder(declaration).append(argumentList).append(' ')
.append(implementsSection).append(" {");
StringBuilder fixedRecord = new StringBuilder(declaration).append(argumentList).append(' ').append(implementsSection).append(" {");
alternateMethods.forEach(method -> fixedRecord.append('\n').append(method));
fixedRecord.append('}');
return fixedRecord.toString();
@@ -147,23 +145,27 @@ class InternalRecordInterfaceProcessor {
MethodSpec.Builder builder = MethodSpec.methodBuilder(FAKE_METHOD_NAME);
recordComponents.forEach(component -> {
String name = component.alternateName.orElseGet(() -> component.element.getSimpleName().toString());
ParameterSpec parameterSpec = ParameterSpec.builder(ClassName.get(component.element.getReturnType()), name)
.build();
builder.addTypeVariables(component.element.getTypeParameters().stream().map(TypeVariableName::get)
.collect(Collectors.toList()));
ParameterSpec parameterSpec = ParameterSpec.builder(ClassName.get(component.element.getReturnType()), name).build();
builder.addTypeVariables(component.element.getTypeParameters().stream().map(TypeVariableName::get).collect(Collectors.toList()));
builder.addParameter(parameterSpec);
});
return builder.build();
}
private List<String> buildAlternateMethods(List<Component> recordComponents) {
return recordComponents.stream().filter(component -> component.alternateName.isPresent()).map(component -> {
var method = MethodSpec.methodBuilder(component.element.getSimpleName().toString())
.addAnnotation(Override.class).addAnnotation(generatedRecordInterfaceAnnotation)
.returns(ClassName.get(component.element.getReturnType())).addModifiers(Modifier.PUBLIC)
.addCode("return $L();", component.alternateName.get()).build();
return method.toString();
}).collect(Collectors.toList());
return recordComponents.stream()
.filter(component -> component.alternateName.isPresent())
.map(component -> {
var method = MethodSpec.methodBuilder(component.element.getSimpleName().toString())
.addAnnotation(Override.class)
.addAnnotation(generatedRecordInterfaceAnnotation)
.returns(ClassName.get(component.element.getReturnType()))
.addModifiers(Modifier.PUBLIC)
.addCode("return $L();", component.alternateName.get())
.build();
return method.toString();
})
.collect(Collectors.toList());
}
private List<Component> getRecordComponents(TypeElement iface) {
@@ -171,8 +173,7 @@ class InternalRecordInterfaceProcessor {
try {
getRecordComponents(iface, components, new HashSet<>(), new HashSet<>());
if (components.isEmpty()) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
"Annotated interface has no component methods", iface);
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Annotated interface has no component methods", iface);
}
} catch (IllegalInterface e) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage(), iface);
@@ -188,32 +189,29 @@ class InternalRecordInterfaceProcessor {
}
private void getRecordComponents(TypeElement iface, Collection<Component> components, Set<String> visitedSet,
Set<String> usedNames) {
private void getRecordComponents(TypeElement iface, Collection<Component> components, Set<String> visitedSet, Set<String> usedNames) {
if (!visitedSet.add(iface.getQualifiedName().toString())) {
return;
}
iface.getEnclosedElements().stream()
.filter(element -> (element.getKind() == ElementKind.METHOD)
&& !(element.getModifiers().contains(Modifier.STATIC)))
.map(element -> ((ExecutableElement) element)).filter(element -> {
.filter(element -> (element.getKind() == ElementKind.METHOD) && !(element.getModifiers().contains(Modifier.STATIC)))
.map(element -> ((ExecutableElement) element))
.filter(element -> {
if (element.isDefault()) {
return element.getAnnotation(IgnoreDefaultMethod.class) == null;
}
return true;
}).peek(element -> {
})
.peek(element -> {
if (!element.getParameters().isEmpty() || element.getReturnType().getKind() == TypeKind.VOID) {
throw new IllegalInterface(String.format(
"Non-static, non-default methods must take no arguments and must return a value. Bad method: %s.%s()",
iface.getSimpleName(), element.getSimpleName()));
throw new IllegalInterface(String.format("Non-static, non-default methods must take no arguments and must return a value. Bad method: %s.%s()", iface.getSimpleName(), element.getSimpleName()));
}
if (!element.getTypeParameters().isEmpty()) {
throw new IllegalInterface(
String.format("Interface methods cannot have type parameters. Bad method: %s.%s()",
iface.getSimpleName(), element.getSimpleName()));
throw new IllegalInterface(String.format("Interface methods cannot have type parameters. Bad method: %s.%s()", iface.getSimpleName(), element.getSimpleName()));
}
}).filter(element -> usedNames.add(element.getSimpleName().toString()))
})
.filter(element -> usedNames.add(element.getSimpleName().toString()))
.map(element -> new Component(element, stripBeanPrefix(element.getSimpleName().toString())))
.collect(Collectors.toCollection(() -> components));
iface.getInterfaces().forEach(parentIface -> {
@@ -223,8 +221,10 @@ class InternalRecordInterfaceProcessor {
}
private Optional<String> stripBeanPrefix(String name) {
return javaBeanPrefixes.stream().filter(prefix -> name.startsWith(prefix) && (name.length() > prefix.length()))
.findFirst().map(prefix -> {
return javaBeanPrefixes.stream()
.filter(prefix -> name.startsWith(prefix) && (name.length() > prefix.length()))
.findFirst()
.map(prefix -> {
var stripped = name.substring(prefix.length());
return Character.toLowerCase(stripped.charAt(0)) + stripped.substring(1);
});

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,7 +43,8 @@ public record OptionalType(TypeName typeName, TypeName valueType) {
if (!(component.typeName() instanceof ParameterizedTypeName parameterizedType)) {
return Optional.of(new OptionalType(optionalType, TypeName.get(Object.class)));
}
final TypeName containingType = parameterizedType.typeArguments.isEmpty() ? TypeName.get(Object.class)
final TypeName containingType = parameterizedType.typeArguments.isEmpty()
? TypeName.get(Object.class)
: parameterizedType.typeArguments.get(0);
return Optional.of(new OptionalType(optionalType, containingType));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,36 +25,35 @@ class RecordBuilderOptions {
private static final Map<String, Object> defaultValues = buildDefaultValues();
static RecordBuilder.Options build(Map<String, String> options) {
return (RecordBuilder.Options) Proxy.newProxyInstance(RecordBuilderOptions.class.getClassLoader(),
new Class[] { RecordBuilder.Options.class }, (proxy, method, args) -> {
var name = method.getName();
var defaultValue = defaultValues.get(name);
var option = options.get(name);
if (option != null) {
if (defaultValue instanceof String) {
return option;
}
if (defaultValue instanceof Boolean) {
return Boolean.parseBoolean(option);
}
if (defaultValue instanceof Integer) {
return Integer.parseInt(option);
}
if (defaultValue instanceof Long) {
return Long.parseLong(option);
}
if (defaultValue instanceof Double) {
return Double.parseDouble(option);
}
throw new IllegalArgumentException("Unhandled option type: " + defaultValue.getClass());
}
return defaultValue;
});
return (RecordBuilder.Options)Proxy.newProxyInstance(RecordBuilderOptions.class.getClassLoader(), new Class[]{RecordBuilder.Options.class}, (proxy, method, args) -> {
var name = method.getName();
var defaultValue = defaultValues.get(name);
var option = options.get(name);
if (option != null) {
if (defaultValue instanceof String) {
return option;
}
if (defaultValue instanceof Boolean) {
return Boolean.parseBoolean(option);
}
if (defaultValue instanceof Integer) {
return Integer.parseInt(option);
}
if (defaultValue instanceof Long) {
return Long.parseLong(option);
}
if (defaultValue instanceof Double) {
return Double.parseDouble(option);
}
throw new IllegalArgumentException("Unhandled option type: " + defaultValue.getClass());
}
return defaultValue;
});
}
private static Map<String, Object> buildDefaultValues() {
var workMap = new HashMap<String, Object>();
for (Method method : RecordBuilder.Options.class.getDeclaredMethods()) {
for ( Method method : RecordBuilder.Options.class.getDeclaredMethods()) {
workMap.put(method.getName(), method.getDefaultValue());
}
workMap.put("toString", "Generated RecordBuilder.Options");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,23 +38,20 @@ import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
public class RecordBuilderProcessor extends AbstractProcessor {
public class RecordBuilderProcessor
extends AbstractProcessor {
private static final String RECORD_BUILDER = RecordBuilder.class.getName();
private static final String RECORD_BUILDER_INCLUDE = RecordBuilder.Include.class.getName().replace('$', '.');
private static final String RECORD_INTERFACE = RecordInterface.class.getName();
private static final String RECORD_INTERFACE_INCLUDE = RecordInterface.Include.class.getName().replace('$', '.');
static final AnnotationSpec generatedRecordBuilderAnnotation = AnnotationSpec.builder(Generated.class)
.addMember("value", "$S", RecordBuilder.class.getName()).build();
static final AnnotationSpec generatedRecordInterfaceAnnotation = AnnotationSpec.builder(Generated.class)
.addMember("value", "$S", RecordInterface.class.getName()).build();
static final AnnotationSpec recordBuilderGeneratedAnnotation = AnnotationSpec.builder(RecordBuilderGenerated.class)
.build();
static final AnnotationSpec generatedRecordBuilderAnnotation = AnnotationSpec.builder(Generated.class).addMember("value", "$S", RecordBuilder.class.getName()).build();
static final AnnotationSpec generatedRecordInterfaceAnnotation = AnnotationSpec.builder(Generated.class).addMember("value", "$S", RecordInterface.class.getName()).build();
static final AnnotationSpec recordBuilderGeneratedAnnotation = AnnotationSpec.builder(RecordBuilderGenerated.class).build();
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
annotations.forEach(annotation -> roundEnv.getElementsAnnotatedWith(annotation)
.forEach(element -> process(annotation, element)));
annotations.forEach(annotation -> roundEnv.getElementsAnnotatedWith(annotation).forEach(element -> process(annotation, element)));
return false;
}
@@ -65,7 +62,7 @@ public class RecordBuilderProcessor extends AbstractProcessor {
@Override
public SourceVersion getSupportedSourceVersion() {
// we don't directly return RELEASE_14 as that may
// we don't directly return RELEASE_14 as that may
// not exist in prior releases
// if we're running on an older release, returning latest()
// is fine as we won't encounter any records anyway
@@ -79,16 +76,14 @@ public class RecordBuilderProcessor extends AbstractProcessor {
processRecordBuilder(typeElement, getMetaData(typeElement), Optional.empty());
} else if (annotationClass.equals(RECORD_INTERFACE)) {
var typeElement = (TypeElement) element;
processRecordInterface(typeElement, element.getAnnotation(RecordInterface.class).addRecordBuilder(),
getMetaData(typeElement), Optional.empty(), false);
processRecordInterface(typeElement, element.getAnnotation(RecordInterface.class).addRecordBuilder(), getMetaData(typeElement), Optional.empty(), false);
} else if (annotationClass.equals(RECORD_BUILDER_INCLUDE) || annotationClass.equals(RECORD_INTERFACE_INCLUDE)) {
processIncludes(element, getMetaData(element), annotationClass);
} else {
var recordBuilderTemplate = annotation.getAnnotation(RecordBuilder.Template.class);
if (recordBuilderTemplate != null) {
if (recordBuilderTemplate.asRecordInterface()) {
processRecordInterface((TypeElement) element, true, recordBuilderTemplate.options(),
Optional.empty(), true);
processRecordInterface((TypeElement) element, true, recordBuilderTemplate.options(), Optional.empty(), true);
} else {
processRecordBuilder((TypeElement) element, recordBuilderTemplate.options(), Optional.empty());
}
@@ -98,34 +93,27 @@ public class RecordBuilderProcessor extends AbstractProcessor {
private RecordBuilder.Options getMetaData(Element element) {
var recordSpecificMetaData = element.getAnnotation(RecordBuilder.Options.class);
return (recordSpecificMetaData != null) ? recordSpecificMetaData
: RecordBuilderOptions.build(processingEnv.getOptions());
return (recordSpecificMetaData != null) ? recordSpecificMetaData : RecordBuilderOptions.build(processingEnv.getOptions());
}
private void processIncludes(Element element, RecordBuilder.Options metaData, String annotationClass) {
var isRecordBuilderInclude = annotationClass.equals(RECORD_BUILDER_INCLUDE);
var annotationMirrorOpt = ElementUtils.findAnnotationMirror(processingEnv, element, annotationClass);
if (annotationMirrorOpt.isEmpty()) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
"Could not get annotation mirror for: " + annotationClass, element);
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Could not get annotation mirror for: " + annotationClass, element);
} else {
var includeHelper = new IncludeHelper(processingEnv, element, annotationMirrorOpt.get(),
isRecordBuilderInclude);
var includeHelper = new IncludeHelper(processingEnv, element, annotationMirrorOpt.get(), isRecordBuilderInclude);
if (includeHelper.isValid()) {
var packagePattern = ElementUtils.getStringAttribute(ElementUtils
.getAnnotationValue(includeHelper.getAnnotationValues(), "packagePattern").orElse(null), "*");
var packagePattern = ElementUtils.getStringAttribute(ElementUtils.getAnnotationValue(includeHelper.getAnnotationValues(), "packagePattern").orElse(null), "*");
for (var typeElement : includeHelper.getClassTypeElements()) {
var packageName = buildPackageName(packagePattern, element, typeElement);
if (packageName != null) {
if (isRecordBuilderInclude) {
processRecordBuilder(typeElement, metaData, Optional.of(packageName));
} else {
var addRecordBuilderOpt = ElementUtils
.getAnnotationValue(includeHelper.getAnnotationValues(), "addRecordBuilder");
var addRecordBuilder = addRecordBuilderOpt.map(ElementUtils::getBooleanAttribute)
.orElse(true);
processRecordInterface(typeElement, addRecordBuilder, metaData, Optional.of(packageName),
false);
var addRecordBuilderOpt = ElementUtils.getAnnotationValue(includeHelper.getAnnotationValues(), "addRecordBuilder");
var addRecordBuilder = addRecordBuilderOpt.map(ElementUtils::getBooleanAttribute).orElse(true);
processRecordInterface(typeElement, addRecordBuilder, metaData, Optional.of(packageName), false);
}
}
}
@@ -142,8 +130,7 @@ public class RecordBuilderProcessor extends AbstractProcessor {
if (builderElement instanceof PackageElement) {
return replaced.replace("@", ((PackageElement) builderElement).getQualifiedName().toString());
}
return replaced.replace("@",
((PackageElement) builderElement.getEnclosingElement()).getQualifiedName().toString());
return replaced.replace("@", ((PackageElement) builderElement.getEnclosingElement()).getQualifiedName().toString());
}
private PackageElement findPackageElement(Element actualElement, Element includedClass) {
@@ -157,63 +144,37 @@ public class RecordBuilderProcessor extends AbstractProcessor {
return findPackageElement(actualElement, includedClass.getEnclosingElement());
}
private void processRecordInterface(TypeElement element, boolean addRecordBuilder, RecordBuilder.Options metaData,
Optional<String> packageName, boolean fromTemplate) {
private void processRecordInterface(TypeElement element, boolean addRecordBuilder, RecordBuilder.Options metaData, Optional<String> packageName, boolean fromTemplate) {
if (!element.getKind().isInterface()) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
"RecordInterface only valid for interfaces.", element);
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "RecordInterface only valid for interfaces.", element);
return;
}
validateMetaData(metaData, element);
var internalProcessor = new InternalRecordInterfaceProcessor(processingEnv, element, addRecordBuilder, metaData,
packageName, fromTemplate);
var internalProcessor = new InternalRecordInterfaceProcessor(processingEnv, element, addRecordBuilder, metaData, packageName, fromTemplate);
if (!internalProcessor.isValid()) {
return;
}
writeRecordInterfaceJavaFile(element, internalProcessor.packageName(), internalProcessor.recordClassType(),
internalProcessor.recordType(), metaData, internalProcessor::toRecord);
writeRecordInterfaceJavaFile(element, internalProcessor.packageName(), internalProcessor.recordClassType(), internalProcessor.recordType(), metaData, internalProcessor::toRecord);
}
private void processRecordBuilder(TypeElement record, RecordBuilder.Options metaData,
Optional<String> packageName) {
private void processRecordBuilder(TypeElement record, RecordBuilder.Options metaData, Optional<String> packageName) {
// we use string based name comparison for the element kind,
// as the ElementKind.RECORD enum doesn't exist on JRE releases
// older than Java 14, and we don't want to throw unexpected
// NoSuchFieldErrors
if (!"RECORD".equals(record.getKind().name())) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "RecordBuilder only valid for records.",
record);
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "RecordBuilder only valid for records.", record);
return;
}
validateMetaData(metaData, record);
var internalProcessor = new InternalRecordBuilderProcessor(processingEnv, record, metaData, packageName);
writeRecordBuilderJavaFile(record, internalProcessor.packageName(), internalProcessor.builderClassType(),
internalProcessor.builderType(), metaData);
writeRecordBuilderJavaFile(record, internalProcessor.packageName(), internalProcessor.builderClassType(), internalProcessor.builderType(), metaData);
}
private void validateMetaData(RecordBuilder.Options metaData, Element record) {
var useImmutableCollections = metaData.useImmutableCollections();
var useUnmodifiableCollections = metaData.useUnmodifiableCollections();
if (useImmutableCollections && useUnmodifiableCollections) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.MANDATORY_WARNING,
"Options.useUnmodifiableCollections property is ignored as Options.useImmutableCollections is set to true",
record);
}
}
private void writeRecordBuilderJavaFile(TypeElement record, String packageName, ClassType builderClassType,
TypeSpec builderType, RecordBuilder.Options metaData) {
private void writeRecordBuilderJavaFile(TypeElement record, String packageName, ClassType builderClassType, TypeSpec builderType, RecordBuilder.Options metaData) {
// produces the Java file
JavaFile javaFile = javaFileBuilder(packageName, builderType, metaData);
Filer filer = processingEnv.getFiler();
try {
String fullyQualifiedName = packageName.isEmpty() ? builderClassType.name()
: (packageName + "." + builderClassType.name());
String fullyQualifiedName = packageName.isEmpty() ? builderClassType.name() : (packageName + "." + builderClassType.name());
JavaFileObject sourceFile = filer.createSourceFile(fullyQualifiedName);
try (Writer writer = sourceFile.openWriter()) {
javaFile.writeTo(writer);
@@ -223,8 +184,7 @@ public class RecordBuilderProcessor extends AbstractProcessor {
}
}
private void writeRecordInterfaceJavaFile(TypeElement element, String packageName, ClassType classType,
TypeSpec type, RecordBuilder.Options metaData, Function<String, String> toRecordProc) {
private void writeRecordInterfaceJavaFile(TypeElement element, String packageName, ClassType classType, TypeSpec type, RecordBuilder.Options metaData, Function<String, String> toRecordProc) {
JavaFile javaFile = javaFileBuilder(packageName, type, metaData);
String classSourceCode = javaFile.toString();
@@ -233,8 +193,7 @@ public class RecordBuilderProcessor extends AbstractProcessor {
Filer filer = processingEnv.getFiler();
try {
String fullyQualifiedName = packageName.isEmpty() ? classType.name()
: (packageName + "." + classType.name());
String fullyQualifiedName = packageName.isEmpty() ? classType.name() : (packageName + "." + classType.name());
JavaFileObject sourceFile = filer.createSourceFile(fullyQualifiedName);
try (Writer writer = sourceFile.openWriter()) {
writer.write(recordSourceCode);
@@ -245,8 +204,7 @@ public class RecordBuilderProcessor extends AbstractProcessor {
}
private JavaFile javaFileBuilder(String packageName, TypeSpec type, RecordBuilder.Options metaData) {
var javaFileBuilder = JavaFile.builder(packageName, type).skipJavaLangImports(true)
.indent(metaData.fileIndent());
var javaFileBuilder = JavaFile.builder(packageName, type).skipJavaLangImports(true).indent(metaData.fileIndent());
var comment = metaData.fileComment();
if ((comment != null) && !comment.isEmpty()) {
javaFileBuilder.addFileComment(comment);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,9 +25,7 @@ public class RecordClassType extends ClassType {
private final List<? extends AnnotationMirror> accessorAnnotations;
private final List<? extends AnnotationMirror> canonicalConstructorAnnotations;
public RecordClassType(TypeName typeName, TypeName rawTypeName, String name,
List<? extends AnnotationMirror> accessorAnnotations,
List<? extends AnnotationMirror> canonicalConstructorAnnotations) {
public RecordClassType(TypeName typeName, TypeName rawTypeName, String name, List<? extends AnnotationMirror> accessorAnnotations, List<? extends AnnotationMirror> canonicalConstructorAnnotations) {
super(typeName, name);
this.rawTypeName = rawTypeName;
this.accessorAnnotations = accessorAnnotations;

View File

@@ -1,26 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2019 The original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>io.soabase.record-builder</groupId>
<artifactId>record-builder</artifactId>
<version>38-SNAPSHOT</version>
<version>36-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -64,12 +47,6 @@
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,11 @@ package io.soabase.recordbuilder.test;
import io.soabase.recordbuilder.core.RecordBuilder;
import io.soabase.recordbuilder.core.RecordInterface;
@RecordInterface.Include({ Thingy.class })
@RecordBuilder.Include({ Nested.NestedRecord.class })
@RecordInterface.Include({
Thingy.class
})
@RecordBuilder.Include({
Nested.NestedRecord.class
})
public class Builder {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,7 +24,11 @@ import java.util.Map;
import java.util.Set;
@RecordBuilder
@RecordBuilder.Options(addSingleItemCollectionBuilders = true, useImmutableCollections = true, mutableListClassName = "PersonalizedMutableList")
@RecordBuilder.Options(
addSingleItemCollectionBuilders = true,
useImmutableCollections = true,
mutableListClassName = "PersonalizedMutableList"
)
public record CollectionCopying<T>(List<String> list, Set<T> set, Map<Instant, T> map, Collection<T> collection,
int count) implements CollectionCopyingBuilder.With<T> {
int count) implements CollectionCopyingBuilder.With<T> {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,11 +25,10 @@ import java.util.Set;
@RecordBuilder
@RecordBuilder.Options(useImmutableCollections = true, addFunctionalMethodsToWith = true)
public record CollectionRecord<T, X extends Point>(List<T> l, Set<T> s, Map<T, X> m, Collection<X> c)
implements CollectionRecordBuilder.With<T, X> {
public record CollectionRecord<T, X extends Point>(List<T> l, Set<T> s, Map<T, X> m,
Collection<X> c) implements CollectionRecordBuilder.With<T, X> {
public static void main(String[] args) {
var r = new CollectionRecord<>(List.of("hey"), Set.of("there"), Map.of("one", new Point(10, 20)),
Set.of(new Point(30, 40)));
var r = new CollectionRecord<>(List.of("hey"), Set.of("there"), Map.of("one", new Point(10, 20)), Set.of(new Point(30, 40)));
Instant now = r.map((l1, s1, m1, c1) -> Instant.now());
r.accept((l1, s1, m1, c1) -> {
});

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,5 @@ import java.util.Set;
@RecordBuilder
@RecordBuilder.Options(useImmutableCollections = true)
public record CollectionRecordConflicts(List<String> __list, Set<String> __set, Map<String, String> __map,
Collection<String> __collection) implements CollectionRecordConflictsBuilder.With {
public record CollectionRecordConflicts(List<String> __list, Set<String> __set, Map<String, String> __map, Collection<String> __collection) implements CollectionRecordConflictsBuilder.With {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,13 +16,14 @@
package io.soabase.recordbuilder.test;
import java.util.List;
import java.util.Map;
import io.soabase.recordbuilder.core.RecordBuilder;
import io.soabase.recordbuilder.test.CustomMethodNamesBuilder.Bean;
@RecordBuilder
@RecordBuilder.Options(setterPrefix = "set", getterPrefix = "get", booleanPrefix = "is", beanClassName = "Bean")
public record CustomMethodNames<K, V>(Map<K, V> kvMap, int theValue, List<Integer> theList, boolean theBoolean)
implements Bean, CustomMethodNamesBuilder.With {
@RecordBuilder.Options(
setterPrefix = "set", getterPrefix = "get", booleanPrefix = "is", beanClassName = "Bean")
public record CustomMethodNames(
int theValue,
List<Integer> theList,
boolean theBoolean) implements Bean {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,8 +21,10 @@ import javax.lang.model.type.ErrorType;
import java.util.List;
@RecordBuilder
public record ExceptionDetails(String internalMessage, String endUserMessage, String httpStatus, ErrorType errorType,
List<String> jsonProblems, Throwable cause) {
public record ExceptionDetails(
String internalMessage, String endUserMessage, String httpStatus,
ErrorType errorType, List<String> jsonProblems, Throwable cause
) {
@Override
public List<String> jsonProblems() {
if (jsonProblems == null) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,5 @@ import java.util.List;
import java.util.Map;
@RecordBuilderFull
public record FullRecord(@NotNull List<Number> numbers, @NotNull Map<Number, FullRecord> fullRecords,
@NotNull String justAString) {
public record FullRecord(@NotNull List<Number> numbers, @NotNull Map<Number, FullRecord> fullRecords, @NotNull String justAString) {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,5 @@ import io.soabase.recordbuilder.core.RecordBuilder;
@RecordBuilder.Options(prefixEnclosingClassNames = false)
@RecordBuilder.Include(IncludeWithOption.Hey.class)
public class IncludeWithOption {
public static record Hey(String s) {
}
public static record Hey(String s){}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,10 @@ package io.soabase.recordbuilder.test;
import io.soabase.recordbuilder.core.RecordBuilder;
@RecordBuilder.Template(options = @RecordBuilder.Options(fileComment = "This is a test", withClassName = "Com"), asRecordInterface = true)
@RecordBuilder.Template(options = @RecordBuilder.Options(
fileComment = "This is a test",
withClassName = "Com"),
asRecordInterface = true
)
public @interface MyInterfaceTemplate {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,10 @@ package io.soabase.recordbuilder.test;
import io.soabase.recordbuilder.core.RecordBuilder;
@RecordBuilder.Template(options = @RecordBuilder.Options(fileComment = "This is a test", withClassName = "Com"))
public @interface MyTemplate {
@RecordBuilder.Template(options = @RecordBuilder.Options(
fileComment = "This is a test",
withClassName = "Com"
))
public @interface MyTemplate
{
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,5 @@
package io.soabase.recordbuilder.test;
public class Nested {
record NestedRecord(int x, int y) {
}
record NestedRecord(int x, int y){}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,9 @@
/*
* Copyright 2019 The original author or authors
package io.soabase.recordbuilder.test;
import io.soabase.recordbuilder.core.RecordBuilder;
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,22 +17,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.soabase.recordbuilder.test;
import io.soabase.recordbuilder.core.RecordBuilder;
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
@RecordBuilder.Options(addStaticBuilder = false)
@RecordBuilder
public record NoStaticBuilder(String foo) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,5 +15,4 @@
*/
package io.soabase.recordbuilder.test;
public record Pair<T, U>(T t, U u) {
}
public record Pair<T, U>(T t, U u) {}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,5 +15,4 @@
*/
package io.soabase.recordbuilder.test;
public record Point(int x, int y) {
}
public record Point(int x, int y) {}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,5 +18,4 @@ package io.soabase.recordbuilder.test;
import io.soabase.recordbuilder.core.RecordBuilder;
@RecordBuilder
public record RecordWithAnR(int r, String b) {
}
public record RecordWithAnR(int r, String b) {}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,4 @@ import java.util.OptionalLong;
@RecordBuilder.Options(emptyDefaultForOptional = true, addConcreteSettersForOptional = true)
@RecordBuilder
public record RecordWithOptional(@NotNull Optional<String> value, Optional raw, OptionalInt i, OptionalLong l,
OptionalDouble d) {
}
public record RecordWithOptional(@NotNull Optional<String> value, Optional raw, OptionalInt i, OptionalLong l, OptionalDouble d) {}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,4 @@ import io.soabase.recordbuilder.core.RecordBuilder;
@RecordBuilder.Options(emptyDefaultForOptional = true)
@RecordBuilder
public record RecordWithOptional2(Optional<String> value, Optional raw, OptionalInt i, OptionalLong l,
OptionalDouble d) {
}
public record RecordWithOptional2(Optional<String> value, Optional raw, OptionalInt i, OptionalLong l, OptionalDouble d) {}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,5 @@ import javax.validation.constraints.NotNull;
@RecordBuilder
@RecordBuilder.Options(useValidationApi = true)
public record RequestWithValid(@NotNull @Valid Part part) implements RequestWithValidBuilder.With {
public record Part(@NotBlank String name) {
}
public record Part(@NotBlank String name) {}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,5 @@ import java.util.List;
@RecordBuilder.Options(interpretNotNulls = true)
@RecordBuilder
public record RequiredRecord(@NotNull String hey, @NotNull int i, @NotNull List<String> l)
implements RequiredRecordBuilder.With {
public record RequiredRecord(@NotNull String hey, @NotNull int i, @NotNull List<String> l) implements RequiredRecordBuilder.With {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,7 +24,11 @@ import java.util.Map;
import java.util.Set;
@RecordBuilder
@RecordBuilder.Options(addSingleItemCollectionBuilders = true, singleItemBuilderPrefix = "add1", useImmutableCollections = true, addFunctionalMethodsToWith = true)
public record SingleItems<T>(List<String> strings, Set<List<T>> sets, Map<Instant, T> map, Collection<T> collection)
implements SingleItemsBuilder.With<T> {
@RecordBuilder.Options(
addSingleItemCollectionBuilders = true,
singleItemBuilderPrefix = "add1",
useImmutableCollections = true,
addFunctionalMethodsToWith = true
)
public record SingleItems<T>(List<String> strings, Set<List<T>> sets, Map<Instant, T> map, Collection<T> collection) implements SingleItemsBuilder.With<T> {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@ package io.soabase.recordbuilder.test;
import io.soabase.recordbuilder.core.RecordBuilder;
@RecordBuilder
@RecordBuilder.Options(enableGetters = false, enableWither = false)
public record StrippedFeaturesRecord(int aField) {
}
@RecordBuilder.Options(
enableGetters = false,
enableWither = false
)
public record StrippedFeaturesRecord(int aField) {}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,5 +18,6 @@ package io.soabase.recordbuilder.test;
import java.time.Instant;
@MyTemplate
public record TemplateTest(String text, Instant date) implements TemplateTestBuilder.Com {
public record TemplateTest(String text, Instant date) implements TemplateTestBuilder.Com
{
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,29 +0,0 @@
/*
* Copyright 2019 The original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.soabase.recordbuilder.test;
import io.soabase.recordbuilder.core.RecordBuilder;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@RecordBuilder
@RecordBuilder.Options(useUnmodifiableCollections = true)
record UnmodifiableCollectionsRecord(List<Integer> aList, Set<String> orderedSet, Map<String, Integer> orderedMap,
Collection<String> aCollection) {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,8 +24,9 @@ import java.util.Map;
import java.util.Set;
@RecordBuilder
@RecordBuilder.Options(addSingleItemCollectionBuilders = true, useImmutableCollections = true)
public record WildcardSingleItems<T>(List<? extends String> strings, Set<? extends List<? extends T>> sets,
Map<? extends Instant, ? extends T> map, Collection<? extends T> collection)
implements WildcardSingleItemsBuilder.With<T> {
@RecordBuilder.Options(
addSingleItemCollectionBuilders = true,
useImmutableCollections = true
)
public record WildcardSingleItems<T>(List<? extends String> strings, Set<? extends List<? extends T>> sets, Map<? extends Instant, ? extends T> map, Collection<? extends T> collection) implements WildcardSingleItemsBuilder.With<T> {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,9 @@ package io.soabase.recordbuilder.test.includes;
import io.soabase.recordbuilder.core.RecordBuilder;
@RecordBuilder.Include(packages = "io.soabase.recordbuilder.test.includes.pack", classes = JustATest.class)
@RecordBuilder.Include(
packages = "io.soabase.recordbuilder.test.includes.pack",
classes = JustATest.class
)
public class IncludeFactory {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,5 @@ import java.util.Map;
@RecordBuilderFull
@RecordBuilderGenerated
public record FullRecordForJacoco(@NotNull List<Number> numbers, @NotNull Map<Number, FullRecordForJacoco> fullRecords,
@NotNull String justAString) {
public record FullRecordForJacoco(@NotNull List<Number> numbers, @NotNull Map<Number, FullRecordForJacoco> fullRecords, @NotNull String justAString) {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@RecordBuilder.Include(value = { Point.class, Pair.class }, packagePattern = "*.foo")
@RecordBuilder.Options(fileComment = "MyLicense - Auto generated")
@RecordBuilder.Include(value = {Point.class, Pair.class}, packagePattern = "*.foo")
@RecordInterface.Include(value = Customer.class, addRecordBuilder = false, packagePattern = "*.bar")
package io.soabase.recordbuilder.test;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,8 @@ import io.soabase.recordbuilder.core.RecordBuilder;
import javax.lang.model.element.Modifier;
@RecordBuilder.Options(builderClassModifiers = { Modifier.PUBLIC })
@RecordBuilder.Options(builderClassModifiers = {Modifier.PUBLIC})
@RecordBuilder
record PackagePrivateRecordWithPublicBuilder(String value) {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,20 +52,30 @@ class TestCollections {
map.put("one", Point(10, 20));
var collectionAsSet = new HashSet<Point>();
collectionAsSet.add(Point(30, 40));
var r = CollectionRecordBuilder.<String, Point> builder().l(list).s(set).m(map).c(collectionAsSet).build();
var r = CollectionRecordBuilder.<String, Point>builder()
.l(list)
.s(set)
.m(map)
.c(collectionAsSet)
.build();
assertValues(r, list, set, map, collectionAsSet);
assertValueChanges(r, list, set, map, collectionAsSet);
assertImmutable(r);
var collectionAsList = new ArrayList<Point>();
var x = CollectionRecordBuilder.<String, Point> builder().l(list).s(set).m(map).c(collectionAsList).build();
var x = CollectionRecordBuilder.<String, Point>builder()
.l(list)
.s(set)
.m(map)
.c(collectionAsList)
.build();
assertTrue(x.c() instanceof List);
}
@Test
void testCollectionRecordImmutableWithers() {
var r = CollectionRecordBuilder.<String, Point> builder().build();
var r = CollectionRecordBuilder.<String, Point>builder().build();
var list = new ArrayList<String>();
list.add("one");
@@ -103,8 +113,7 @@ class TestCollections {
assertThrows(UnsupportedOperationException.class, () -> r.c().add(Point(1, 2)));
}
private void assertValueChanges(CollectionRecord<String, Point> r, ArrayList<String> list, HashSet<String> set,
HashMap<String, Point> map, HashSet<Point> collectionAsSet) {
private void assertValueChanges(CollectionRecord<String, Point> r, ArrayList<String> list, HashSet<String> set, HashMap<String, Point> map, HashSet<Point> collectionAsSet) {
list.add("two");
set.add("two");
map.put("two", Point(50, 60));
@@ -116,8 +125,7 @@ class TestCollections {
assertNotEquals(r.c(), collectionAsSet);
}
private void assertValues(CollectionRecord<String, Point> r, ArrayList<String> list, HashSet<String> set,
HashMap<String, Point> map, HashSet<Point> collectionAsSet) {
private void assertValues(CollectionRecord<String, Point> r, ArrayList<String> list, HashSet<String> set, HashMap<String, Point> map, HashSet<Point> collectionAsSet) {
assertEquals(r.l(), list);
assertEquals(r.s(), set);
assertEquals(r.m(), map);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,65 +24,91 @@ import java.util.*;
public class TestImmutableCollections {
@Test
public void testImmutableListNotCopiedWhenNotChanged() {
var item = CollectionCopyingBuilder.<String> builder().addList("a").addList("b").addList("c").build();
var item = CollectionCopyingBuilder.<String>builder()
.addList("a")
.addList("b")
.addList("c")
.build();
Assertions.assertEquals(item.list(), List.of("a", "b", "c"));
var oldList = item.list();
var copy = item.with().count(1).build();
var copy = item.with()
.count(1)
.build();
Assertions.assertSame(oldList, copy.list());
var otherCopy = item.with().count(2).build();
var otherCopy = item.with()
.count(2)
.build();
Assertions.assertSame(oldList, otherCopy.list());
}
@Test
public void testImmutableSetNotCopiedWhenNotChanged() {
var item = CollectionCopyingBuilder.<String> builder().addSet(Arrays.asList("1", "2", "3")).build();
var item = CollectionCopyingBuilder.<String>builder()
.addSet(Arrays.asList("1", "2", "3"))
.build();
Assertions.assertEquals(item.set(), Set.of("1", "2", "3"));
var oldSet = item.set();
var copy = item.with().count(1).build();
var copy = item.with()
.count(1)
.build();
Assertions.assertSame(oldSet, copy.set());
var otherCopy = item.with().count(2).build();
var otherCopy = item.with()
.count(2)
.build();
Assertions.assertSame(oldSet, otherCopy.set());
}
@Test
public void testImmutableCollectionNotCopiedWhenNotChanged() {
var item = CollectionCopyingBuilder.<String> builder().collection(List.of("foo", "bar", "baz")).build();
var item = CollectionCopyingBuilder.<String>builder()
.collection(List.of("foo", "bar", "baz"))
.build();
Assertions.assertEquals(item.collection(), List.of("foo", "bar", "baz"));
var oldCollection = item.collection();
var copy = item.with().count(1).build();
var copy = item.with()
.count(1)
.build();
Assertions.assertSame(oldCollection, copy.collection());
var otherCopy = item.with().count(2).build();
var otherCopy = item.with()
.count(2)
.build();
Assertions.assertSame(oldCollection, otherCopy.collection());
}
@Test
public void testImmutableMapNotCopiedWhenNotChanged() {
var item = CollectionCopyingBuilder.<String> builder().addMap(Instant.MAX, "future")
.addMap(Instant.MIN, "before").build();
var item = CollectionCopyingBuilder.<String>builder()
.addMap(Instant.MAX, "future")
.addMap(Instant.MIN, "before")
.build();
Assertions.assertEquals(item.map(), Map.of(Instant.MAX, "future", Instant.MIN, "before"));
var oldMap = item.map();
var copy = item.with().count(1).build();
var copy = item.with()
.count(1)
.build();
Assertions.assertSame(oldMap, copy.map());
var otherCopy = item.with().count(2).build();
var otherCopy = item.with()
.count(2)
.build();
Assertions.assertSame(oldMap, otherCopy.map());
}
@@ -90,7 +116,9 @@ public class TestImmutableCollections {
@Test
void testSourceListNotModified() {
var item = new CollectionCopying<>(new ArrayList<>(), null, null, null, 0);
var modifiedItem = CollectionCopyingBuilder.builder(item).addList("a").build();
var modifiedItem = CollectionCopyingBuilder.builder(item)
.addList("a")
.build();
Assertions.assertEquals(modifiedItem.list(), List.of("a"));
Assertions.assertTrue(item.list().isEmpty());
@@ -99,7 +127,9 @@ public class TestImmutableCollections {
@Test
void testSourceSetNotModified() {
var item = new CollectionCopying<>(null, new HashSet<>(), null, null, 0);
var modifiedItem = CollectionCopyingBuilder.builder(item).addSet("a").build();
var modifiedItem = CollectionCopyingBuilder.builder(item)
.addSet("a")
.build();
Assertions.assertEquals(modifiedItem.set(), Set.of("a"));
Assertions.assertTrue(item.set().isEmpty());
@@ -108,7 +138,9 @@ public class TestImmutableCollections {
@Test
void testSourceMapNotModified() {
var item = new CollectionCopying<>(null, null, new HashMap<>(), null, 0);
var modifiedItem = CollectionCopyingBuilder.builder(item).addMap(Instant.MIN, "a").build();
var modifiedItem = CollectionCopyingBuilder.builder(item)
.addMap(Instant.MIN, "a")
.build();
Assertions.assertEquals(modifiedItem.map(), Map.of(Instant.MIN, "a"));
Assertions.assertTrue(item.map().isEmpty());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,12 @@ class TestOptional {
@Test
void testRawSetters() {
var record = RecordWithOptionalBuilder.builder().value("value").raw("rawValue").i(42).l(424242L).d(42.42)
var record = RecordWithOptionalBuilder.builder()
.value("value")
.raw("rawValue")
.i(42)
.l(424242L)
.d(42.42)
.build();
Assertions.assertEquals(Optional.of("value"), record.value());
Assertions.assertEquals(Optional.of("rawValue"), record.raw());
@@ -47,8 +52,13 @@ class TestOptional {
@Test
void testOptionalSetters() {
var record = RecordWithOptional2Builder.builder().value(Optional.of("value")).raw(Optional.of("rawValue"))
.i(OptionalInt.of(42)).l(OptionalLong.of(424242L)).d(OptionalDouble.of(42.42)).build();
var record = RecordWithOptional2Builder.builder()
.value(Optional.of("value"))
.raw(Optional.of("rawValue"))
.i(OptionalInt.of(42))
.l(OptionalLong.of(424242L))
.d(OptionalDouble.of(42.42))
.build();
Assertions.assertEquals(Optional.of("value"), record.value());
Assertions.assertEquals(Optional.of("rawValue"), record.raw());
Assertions.assertEquals(OptionalInt.of(42), record.i());
@@ -62,7 +72,9 @@ class TestOptional {
String value = null;
// when
var record = RecordWithOptionalBuilder.builder().value(value).build();
var record = RecordWithOptionalBuilder.builder()
.value(value)
.build();
// then
Assertions.assertEquals(Optional.empty(), record.value());

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2019 The original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.soabase.recordbuilder.test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class TestOptionsOnPackage {
@Test
void testOptionsOnInclude() throws IOException {
String text = Files.readString(
Path.of("target/generated-sources/annotations/io/soabase/recordbuilder/test/foo/PairBuilder.java"));
Assertions.assertTrue(text.contains("// MyLicense - Auto generated"));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,7 +33,10 @@ class TestRecordBuilderFull {
@Test
void testImmutable() {
var record = FullRecordBuilder.builder().fullRecords(new HashMap<>()).numbers(new ArrayList<>()).justAString("")
var record = FullRecordBuilder.builder()
.fullRecords(new HashMap<>())
.numbers(new ArrayList<>())
.justAString("")
.build();
Assertions.assertThrows(UnsupportedOperationException.class, () -> record.fullRecords().put(1, record));
Assertions.assertThrows(UnsupportedOperationException.class, () -> record.numbers().add(1));

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,9 +26,11 @@ import java.time.Instant;
import static io.soabase.recordbuilder.test.SimpleGenericRecordBuilder.SimpleGenericRecord;
import static io.soabase.recordbuilder.test.SimpleRecordBuilder.SimpleRecord;
public class TestRecordInterface {
public class TestRecordInterface
{
@Test
public void testHasDefaults() {
public void testHasDefaults()
{
var r1 = new HasDefaultsRecord(Instant.MIN, Instant.MAX);
var r2 = r1.with(b -> b.tomorrow(Instant.MIN));
Assertions.assertEquals(Instant.MIN, r1.time());
@@ -38,8 +40,9 @@ public class TestRecordInterface {
}
@Test
public void testStaticConstructor() {
var simple = SimpleRecord(10, "hey");
public void testStaticConstructor()
{
var simple = SimpleRecord(10,"hey");
Assertions.assertEquals(simple.i(), 10);
Assertions.assertEquals(simple.s(), "hey");
@@ -50,15 +53,26 @@ public class TestRecordInterface {
}
@Test
public void testBuilderStreamWithValues() {
var stream = SimpleRecordBuilder.stream(SimpleRecordBuilder.builder().i(19).s("value").build()).toList();
Assertions.assertEquals(stream, List.of(Map.entry("i", 19), Map.entry("s", "value")));
public void testBuilderStreamWithValues()
{
var stream = SimpleRecordBuilder.stream(SimpleRecordBuilder.builder()
.i(19)
.s("value")
.build())
.toList();
Assertions.assertEquals(stream, List.of(
Map.entry("i", 19),
Map.entry("s", "value")));
}
@Test
public void testBuilderStreamWithNulls() {
var stream = SimpleRecordBuilder.stream(SimpleRecordBuilder.builder().build()).toList();
Assertions.assertEquals(stream,
List.of(new SimpleImmutableEntry<>("i", 0), new SimpleImmutableEntry<>("s", null)));
public void testBuilderStreamWithNulls()
{
var stream = SimpleRecordBuilder.stream(SimpleRecordBuilder.builder()
.build())
.toList();
Assertions.assertEquals(stream, List.of(
new SimpleImmutableEntry<>("i", 0),
new SimpleImmutableEntry<>("s", null)));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,16 +26,26 @@ import java.util.Set;
public class TestSingleItems {
@Test
public void testInternalCollections() {
public void testInternalCollections()
{
var now = Instant.now();
var item = SingleItemsBuilder.<String> builder().add1Map(now, "now").add1Map(Instant.MIN, "before")
.add1Sets(Arrays.asList("1", "2")).add1Sets(List.of("3")).add1Strings("a").add1Strings("b")
.add1Strings("c").build();
var item = SingleItemsBuilder.<String>builder()
.add1Map(now, "now")
.add1Map(Instant.MIN, "before")
.add1Sets(Arrays.asList("1", "2"))
.add1Sets(List.of("3"))
.add1Strings("a")
.add1Strings("b")
.add1Strings("c")
.build();
Assertions.assertEquals(item.map(), Map.of(now, "now", Instant.MIN, "before"));
Assertions.assertEquals(item.sets(), Set.of(List.of("1", "2"), List.of("3")));
Assertions.assertEquals(item.strings(), List.of("a", "b", "c"));
var copy = item.with().add1Strings("new").add1Map(Instant.MAX, "after").add1Sets(List.of("10", "20", "30"))
var copy = item.with()
.add1Strings("new")
.add1Map(Instant.MAX, "after")
.add1Sets(List.of("10", "20", "30"))
.build();
Assertions.assertNotEquals(item, copy);
Assertions.assertEquals(copy.map(), Map.of(now, "now", Instant.MIN, "before", Instant.MAX, "after"));
@@ -45,15 +55,20 @@ public class TestSingleItems {
var stringsToAdd = Arrays.asList("x", "y", "z");
var listToAdd = Arrays.asList(List.of("aa", "bb"), List.of("cc"));
var mapToAdd = Map.of(now.plusMillis(1), "now+1", now.plusMillis(2), "now+2");
var streamed = SingleItemsBuilder.builder(item).add1Strings(stringsToAdd.stream()).add1Sets(listToAdd.stream())
.add1Map(mapToAdd.entrySet().stream()).build();
Assertions.assertEquals(streamed.map(),
Map.of(now, "now", Instant.MIN, "before", now.plusMillis(1), "now+1", now.plusMillis(2), "now+2"));
Assertions.assertEquals(streamed.sets(),
Set.of(List.of("1", "2"), List.of("3"), List.of("aa", "bb"), List.of("cc")));
var streamed = SingleItemsBuilder.builder(item)
.add1Strings(stringsToAdd.stream())
.add1Sets(listToAdd.stream())
.add1Map(mapToAdd.entrySet().stream())
.build();
Assertions.assertEquals(streamed.map(), Map.of(now, "now", Instant.MIN, "before", now.plusMillis(1), "now+1", now.plusMillis(2), "now+2"));
Assertions.assertEquals(streamed.sets(), Set.of(List.of("1", "2"), List.of("3"), List.of("aa", "bb"), List.of("cc")));
Assertions.assertEquals(streamed.strings(), Arrays.asList("a", "b", "c", "x", "y", "z"));
var nulls = SingleItemsBuilder.builder(item).strings(null).sets(null).map(null).build();
var nulls = SingleItemsBuilder.builder(item)
.strings(null)
.sets(null)
.map(null)
.build();
Assertions.assertEquals(nulls.map(), Map.of());
Assertions.assertEquals(nulls.sets(), Set.of());
Assertions.assertEquals(nulls.strings(), List.of());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,72 +0,0 @@
/*
* Copyright 2019 The original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.soabase.recordbuilder.test;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import static java.util.Map.entry;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class TestUnmodifiableCollectionsBuilder {
@Test
void shouldWrapCollectionsWithUnmodifiableView() {
// given
var list = new ArrayList<Integer>();
list.add(2);
list.add(1);
list.add(0);
var orderedSet = new LinkedHashSet<String>();
orderedSet.add("C");
orderedSet.add("B");
orderedSet.add("A");
var orderedMap = new LinkedHashMap<String, Integer>();
orderedMap.put("C", 2);
orderedMap.put("B", 1);
orderedMap.put("A", 0);
var collection = new HashSet<String>();
collection.add("C");
collection.add("B");
collection.add("A");
// when
var record = UnmodifiableCollectionsRecordBuilder.builder().aList(list).orderedSet(orderedSet)
.orderedMap(orderedMap).aCollection(collection).build();
// then
assertAll(() -> assertThrows(UnsupportedOperationException.class, () -> record.aList().add(9)),
() -> assertThat(record.aList()).containsExactly(2, 1, 0),
() -> assertThrows(UnsupportedOperationException.class, () -> record.orderedSet().add("newElement")),
() -> assertThat(record.orderedSet()).containsExactly("C", "B", "A"),
() -> assertThrows(UnsupportedOperationException.class, () -> record.orderedMap().put("newElement", 9)),
() -> assertThat(record.orderedMap()).containsExactly(entry("C", 2), entry("B", 1), entry("A", 0)),
() -> assertThrows(UnsupportedOperationException.class, () -> record.aCollection().add("newElement")),
() -> assertThat(record.aCollection()).containsExactlyInAnyOrder("C", "B", "A"));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,9 +46,11 @@ class TestValidation {
@Test
void testRequestWithValid() {
Assertions.assertDoesNotThrow(
() -> RequestWithValidBuilder.builder().part(new RequestWithValid.Part("jsfjsf")).build());
Assertions.assertThrows(ValidationException.class,
() -> RequestWithValidBuilder.builder().part(new RequestWithValid.Part("")).build());
Assertions.assertDoesNotThrow(() -> RequestWithValidBuilder.builder()
.part(new RequestWithValid.Part("jsfjsf"))
.build());
Assertions.assertThrows(ValidationException.class, () -> RequestWithValidBuilder.builder()
.part(new RequestWithValid.Part(""))
.build());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,48 +18,52 @@ package io.soabase.recordbuilder.test;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.*;
public class TestVariousOptions {
@Test
public void builderGetsCustomSetterAndGetterNames() {
var obj = CustomMethodNamesBuilder.builder().setKvMap(Map.of(1, "one")).setTheValue(1).setTheList(List.of(2))
.setTheBoolean(true);
assertEquals(1, obj.getTheValue());
assertEquals(List.of(2), obj.getTheList());
assertTrue(obj.isTheBoolean());
assertEquals(new CustomMethodNames<>(Map.of(1, "one"), 1, List.of(2), true), obj.build());
}
@Test
public void builderGetsCustomSetterAndGetterNames() {
var obj = CustomMethodNamesBuilder.builder()
.setTheValue(1)
.setTheList(List.of(2))
.setTheBoolean(true);
assertEquals(1, obj.getTheValue());
assertEquals(List.of(2), obj.getTheList());
assertTrue(obj.isTheBoolean());
assertEquals(new CustomMethodNames(1, List.of(2), true), obj.build());
}
@Test
public void withBuilderGetsCustomSetterAndGetterNames() {
var obj = CustomMethodNamesBuilder.from(
CustomMethodNamesBuilder.builder().setTheValue(1).setTheList(List.of(2)).setTheBoolean(true).build());
assertEquals(1, obj.theValue());
assertEquals(List.of(2), obj.theList());
assertTrue(obj.theBoolean());
}
@Test
public void withBuilderGetsCustomSetterAndGetterNames() {
var obj = CustomMethodNamesBuilder.from(CustomMethodNamesBuilder.builder()
.setTheValue(1)
.setTheList(List.of(2))
.setTheBoolean(true)
.build());
assertEquals(1, obj.getTheValue());
assertEquals(List.of(2), obj.getTheList());
assertTrue(obj.isTheBoolean());
}
@Test
public void recordHasPrefixedGetters() {
var obj = new CustomMethodNames<>(Map.of(1, "one"), 1, List.of(2), true);
assertEquals(1, obj.getTheValue());
assertEquals(List.of(2), obj.getTheList());
assertTrue(obj.isTheBoolean());
}
@Test
public void recordHasPrefixedGetters() {
var obj = new CustomMethodNames(1, List.of(2), true);
assertEquals(1, obj.getTheValue());
assertEquals(List.of(2), obj.getTheList());
assertTrue(obj.isTheBoolean());
}
@Test
public void noStaticBuilder() {
boolean hasStaticBuilder = Stream.of(NoStaticBuilderBuilder.class.getDeclaredMethods())
.anyMatch(method -> method.getName().equals("NoStaticBuilder"));
assertFalse(hasStaticBuilder);
@Test
public void noStaticBuilder() {
boolean hasStaticBuilder = Stream.of(NoStaticBuilderBuilder.class.getDeclaredMethods())
.anyMatch(method -> method.getName().equals("NoStaticBuilder"));
assertFalse(hasStaticBuilder);
hasStaticBuilder = Stream.of(SimpleRecordBuilder.class.getDeclaredMethods())
.anyMatch(method -> method.getName().equals("SimpleRecord"));
assertTrue(hasStaticBuilder);
}
hasStaticBuilder = Stream.of(SimpleRecordBuilder.class.getDeclaredMethods())
.anyMatch(method -> method.getName().equals("SimpleRecord"));
assertTrue(hasStaticBuilder);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@ class TestVisibility {
Assertions.assertTrue(Modifier.isPublic(WrapperProtectedRecordBuilder.class.getModifiers()));
}
@Test
void testMatchesWithModifers() {
Assertions.assertFalse(Modifier.isPublic(PackagePrivateRecordWithPublicBuilder.class.getModifiers()));

View File

@@ -1,26 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2019 The original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>io.soabase.record-builder</groupId>
<artifactId>record-builder</artifactId>
<version>38-SNAPSHOT</version>
<version>36-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 The original author or authors
/**
* Copyright 2019 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
Copyright ${year} The original author or authors
Copyright 2019 Jordan Zimmerman
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.