diff --git a/.github/semantic.yml b/.github/semantic.yml new file mode 100644 index 0000000..052d4d5 --- /dev/null +++ b/.github/semantic.yml @@ -0,0 +1 @@ +titleAndCommits: true \ No newline at end of file diff --git a/README.md b/README.md index ab5e3c8..ecc79cf 100644 --- a/README.md +++ b/README.md @@ -2,27 +2,31 @@ Java record newContext generator -[![Maven Central version](https://img.shields.io/maven-central/v/pl.com.labaj/auto-record)](https://mvnrepository.com/artifact/pl.com.labaj/auto-record) -[![javadoc](https://javadoc.io/badge2/pl.com.labaj/auto-record/javadoc.svg)](https://javadoc.io/doc/pl.com.labaj/auto-record) +[![Maven Central version](https://img.shields.io/maven-central/v/pl.com.labaj.autorecord/auto-record)](https://mvnrepository.com/artifact/pl.com.labaj.autorecord/auto-record) +[![javadoc](https://javadoc.io/badge2/pl.com.labaj.autorecord/auto-record-project/javadoc.svg)](https://javadoc.io/doc/pl.com.labaj.autorecord/auto-record-project) [![CI Verify Status](https://github.com/pawellabaj/auto-record/actions/workflows/verify.yml/badge.svg?branch=main)](https://github.com/pawellabaj/auto-record/actions/workflows/verify.yml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=pl.com.labaj%3Aauto-record-project&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=pl.com.labaj%3Aauto-record-project) [![Sonatype Lift Status](https://lift.sonatype.com/api/badge/github.com/pawellabaj/auto-record)](https://lift.sonatype.com/results/github.com/pawellabaj/auto-record) -[![Reproducible Builds](https://img.shields.io/badge/Reproducible_Builds-ok-success?labelColor=1e5b96)](https://github.com/jvm-repo-rebuild/reproducible-central#pl.com.labaj:auto-record) +[![Reproducible Builds](https://img.shields.io/badge/Reproducible_Builds-ok-success?labelColor=1e5b96)](https://github.com/jvm-repo-rebuild/reproducible-central#pl.com.labaj.autorecord:auto-record) +[![OpenSSF Best Practices](https://bestpractices.coreinfrastructure.org/projects/7700/badge)](https://bestpractices.coreinfrastructure.org/projects/7700) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](.github/CODE_OF_CONDUCT.md) ## What is AutoRecord -AutoRecord is a code generator that helps you easily generate Java records. +AutoRecord is a code generator that helps you easily generate Java records. It provides an easy way to avoid writing repetitive boilerplate code. It generates the code with features such as: + * [nullability](https://github.com/pawellabaj/auto-record/wiki/Nullability) checking -* [builders](https://github.com/pawellabaj/auto-record/wiki/Record-Builder) - incorporating [Randgalt/record-builder](https://github.com/Randgalt/record-builder) library +* [builders](https://github.com/pawellabaj/auto-record/wiki/Record-Builder) - + incorporating [Randgalt/record-builder](https://github.com/Randgalt/record-builder) library * [memoization](https://github.com/pawellabaj/auto-record/wiki/Memoization) * [ignoring fields](https://github.com/pawellabaj/auto-record/wiki/Ignored-components) in `hashCode()` and `equals()` methods -* generated _common_ methods if the record has an [array component](https://github.com/pawellabaj/auto-record/wiki/Array-components) +* generated _common_ methods if the record has an [array recordComponent](https://github.com/pawellabaj/auto-record/wiki/Array-components) * exclusion from [JaCoCo test coverage](https://github.com/pawellabaj/auto-record/wiki/JaCoCo-exclusion) analysis AutoRecord allows users to customize record generation process by: + * specifying [options](https://github.com/pawellabaj/auto-record/wiki/Single-record-options) * using [custom annotation](https://github.com/pawellabaj/auto-record/wiki/Custom-annotations) templates * implementing [custom extensions](https://github.com/pawellabaj/auto-record/wiki/Extensions) @@ -30,12 +34,14 @@ AutoRecord allows users to customize record generation process by: ## Why AutoRecord was created Google [AutoValue](https://github.com/google/auto) has long been used as a way to work with _Value Classes_ in an easy way. -However, when Java [records](https://docs.oracle.com/en/java/javase/17/language/records.html) were introduced, they lacked some features that AutoValue had, such as nullability checking, builders, and memoization. +However, when Java [records](https://docs.oracle.com/en/java/javase/17/language/records.html) were introduced, they lacked some features that AutoValue had, +such as nullability checking, builders, and memoization. This is why AutoRecord was created. ## How to use AutoRecord -To use AutoRecord, simply annotate your interface with [@AutoRecord](https://github.com/pawellabaj/auto-record/blob/main/modules/auto-record/src/main/java/pl/com/labaj/autorecord/AutoRecord.java) annotation: +To use AutoRecord, simply annotate your interface +with [@AutoRecord](https://github.com/pawellabaj/auto-record/blob/main/modules/auto-record/src/main/java/pl/com/labaj/autorecord/AutoRecord.java) annotation: ```java import pl.com.labaj.autorecord.AutoRecord; @@ -43,6 +49,7 @@ import pl.com.labaj.autorecord.AutoRecord; @AutoRecord interface Person { String name(); + int age(); } ``` @@ -51,7 +58,9 @@ AutoRecord will then generate a Java record class that implements your interface ```java import pl.com.labaj.autorecord.GeneratedWithAutoRecord; + import javax.annotation.processing.Generated; + import static java.util.Objects.requireNonNull; @Generated("pl.com.labaj.autorecord.AutoRecord") @@ -71,6 +80,17 @@ record PersonRecord(String name, int age) implements Person { For more information on how to use AutoRecord and all its features, please visit the project's [Wiki](https://github.com/pawellabaj/auto-record/wiki). +## Projects maintained in this repository + +| Project | Description | +|:---------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------| +| [Auto Record API](https://github.com/pawellabaj/auto-record/tree/main/modules/auto-record-api) | Annotations to mark interfaces to be processed | +| [Auto Record](https://github.com/pawellabaj/auto-record/tree/main/modules/auto-record) | Annotation processor to generate records | +| [Auto Record Utilities](https://github.com/pawellabaj/auto-record/tree/main/modules/auto-record-utils) | Utility classes used by generated records | +| [ARICE API](https://github.com/pawellabaj/auto-record/tree/main/arice/api) | Annotations to mark interface methods, used by ARICE | +| [ARICE (Auto Record Immutable Collections Extension)](https://github.com/pawellabaj/auto-record/tree/main/arice/extension) | The extension for customizing a record generation process with support for immutable collections | +| [ARICE Utilities](https://github.com/pawellabaj/auto-record/tree/main/arice/utils) | Utility classes used by ARICE | + ## Getting started ### Maven @@ -78,10 +98,12 @@ For more information on how to use AutoRecord and all its features, please visit Add the following dependency to your `pom.xml` file: ```xml + - pl.com.labaj + pl.com.labaj.autorecord auto-record ${auto-record.version} + provided ``` @@ -91,7 +113,7 @@ Declare the following dependency in your `build.gradle` script: ```groovy dependencies { - annotationProcessor 'pl.com.labaj:auto-record:${autoRecordVersion}' + annotationProcessor 'pl.com.labaj.autorecord:auto-record:${autoRecordVersion}' } ``` @@ -100,7 +122,9 @@ dependencies { Depending on your IDE you are likely to need to enable Annotation Processing in your IDE settings. ## Contributing -We welcome contributions from all developers! If you would like to contribute to AutoRecord, please refer to the [Contributing guide](.github/CONTRIBUTING.md) for more information on how to get started. + +We welcome contributions from all developers! If you would like to contribute to AutoRecord, please refer to the [Contributing guide](.github/CONTRIBUTING.md) +for more information on how to get started. ## License diff --git a/arice/api/README.md b/arice/api/README.md new file mode 100644 index 0000000..8e6b72e --- /dev/null +++ b/arice/api/README.md @@ -0,0 +1,34 @@ +# ARICE API + +Annotations to mark methods in an interface, used during annotation processing with ARICE usage. + +Please, see [WIKI](https://github.com/pawellabaj/auto-record/wiki/ARICE) for information. + +## Usage + +If you use annotation processing in your project, the library will be provided as a dependency of [ARICE](https://github.com/pawellabaj/auto-record/tree/main/arice/extension). + +If you just want to mark your sources (ieg. annotations are being processed in other project or build step), +use `arice-api` directly. + +### Maven + +Add the following dependency to your `pom.xml` file: + +```xml + + + pl.com.labaj.autorecord + arice-api + ${arice.version} + +``` + +### Gradle + +Declare the following dependency in your `build.gradle` script: + +```groovy +dependencies { + annotationProcessor 'pl.com.labaj.autorecord:arice-api:${ariceVersion}' +} \ No newline at end of file diff --git a/arice/api/pom.xml b/arice/api/pom.xml new file mode 100644 index 0000000..e9c2a12 --- /dev/null +++ b/arice/api/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + pl.com.labaj.autorecord + arice-project + 1.0.0-SNAPSHOT + ../project/pom.xml + + + ARICE API + + arice-api + \ No newline at end of file diff --git a/arice/api/src/main/java/pl/com/labaj/autorecord/extension/arice/Mutable.java b/arice/api/src/main/java/pl/com/labaj/autorecord/extension/arice/Mutable.java new file mode 100644 index 0000000..1668048 --- /dev/null +++ b/arice/api/src/main/java/pl/com/labaj/autorecord/extension/arice/Mutable.java @@ -0,0 +1,41 @@ +package pl.com.labaj.autorecord.extension.arice; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import org.apiguardian.api.API; + +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_PARAMETER; +import static java.lang.annotation.RetentionPolicy.SOURCE; +import static org.apiguardian.api.API.Status.STABLE; + +/** + * Annotates methods in @AutoRecord annotated interface for which the corresponding components in generated record are not relevant + * for copying to immutable collection. + * + * @see ARICE Wiki + */ +@Retention(SOURCE) +@Target({METHOD, TYPE_PARAMETER, PARAMETER}) +@Inherited +@API(status = STABLE) +public @interface Mutable {} diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/package-info.java b/arice/api/src/main/java/pl/com/labaj/autorecord/extension/arice/package-info.java similarity index 62% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/package-info.java rename to arice/api/src/main/java/pl/com/labaj/autorecord/extension/arice/package-info.java index 1a4c9ba..49e9159 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/package-info.java +++ b/arice/api/src/main/java/pl/com/labaj/autorecord/extension/arice/package-info.java @@ -14,19 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - /** - * Provides annotations and interfaces needed to process extensions. - * - *

{@link pl.com.labaj.autorecord.extension.AutoRecordExtension} interface has to be implemented by all extensions. + * Provides annotations used to annotate interfaces or methods that are used by ImmutableCollectionsExtension. * - * @see Extensions Wiki - * @since 2.1.0 + * @see Wiki */ - -@API(status = STABLE, since = "2.1.0") -package pl.com.labaj.autorecord.extension; - -import org.apiguardian.api.API; - -import static org.apiguardian.api.API.Status.STABLE; \ No newline at end of file +package pl.com.labaj.autorecord.extension.arice; \ No newline at end of file diff --git a/arice/extension/.skip-annotation-processing b/arice/extension/.skip-annotation-processing new file mode 100644 index 0000000..e69de29 diff --git a/arice/extension/README.md b/arice/extension/README.md new file mode 100644 index 0000000..f14f909 --- /dev/null +++ b/arice/extension/README.md @@ -0,0 +1,43 @@ +# ARICE (Auto Record Immutable Collections Extension) + +An implementations of the link AutoRecord extension for customizing a record generation process with support for immutable collections. + +## Documentation + +For more information on how to use ARICE and all its features, please visit the project's [Wiki](https://github.com/pawellabaj/auto-record/wiki/ARICE). + +## Getting started + +### Maven + +Add the following dependency to your `pom.xml` file: + +```xml + + pl.com.labaj.autorecord + auto-record + ${auto-record.version} + provided + + + pl.com.labaj.autorecord + arice + ${arice.version} + provided + +``` + +### Gradle + +Declare the following dependency in your `build.gradle` script: + +```groovy +dependencies { + annotationProcessor 'pl.com.labaj.autorecord:auto-record:${autoRecordVersion}', + implementation 'pl.com.labaj.autorecord:arice:${ariceVersion}' +} +``` + +### IDE + +Depending on your IDE you are likely to need to enable Annotation Processing in your IDE settings. diff --git a/arice/extension/pom.xml b/arice/extension/pom.xml new file mode 100644 index 0000000..3656d24 --- /dev/null +++ b/arice/extension/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + pl.com.labaj.autorecord + arice-project + 1.0.0-SNAPSHOT + ../project/pom.xml + + + ARICE + Auto Record Immutable Collections Extension + arice-extension + + + ${project.basedir}/../../.build/lic-header.txt + + + + + pl.com.labaj.autorecord + auto-record + + + pl.com.labaj.autorecord + arice-api + + + pl.com.labaj.autorecord + arice-utils + + + \ No newline at end of file diff --git a/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/ARICEUtilities.java b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/ARICEUtilities.java new file mode 100644 index 0000000..81a8550 --- /dev/null +++ b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/ARICEUtilities.java @@ -0,0 +1,54 @@ +package pl.com.labaj.autorecord.extension.arice; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import org.apiguardian.api.API; + +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.SOURCE; +import static org.apiguardian.api.API.Status.MAINTAINED; + +/** + * This annotation is used to mark the generated record, so the ARICE (AutoRecord Immutable Collections Extension) + * knows what utility class it should generate. + * + * @since 3.0.0 + */ +@Retention(SOURCE) +@Target(TYPE) +@Inherited +@API(status = MAINTAINED) +public @interface ARICEUtilities { + /** + * Specifies the fully qualified name of the utility class that provides additional methods and functionality + * to support the ARICE extension. + * + * @return the fully qualified name of the utility class. + */ + String className(); + + /** + * Specifies an array of fully qualified names of immutable types. These types will be used in conjunction with the ARICE extension + * + * @return an array of fully qualified names of immutable types. + */ + String[] immutableTypes() default {}; +} diff --git a/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/ARICEUtilitiesProcessor.java b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/ARICEUtilitiesProcessor.java new file mode 100644 index 0000000..1cb9872 --- /dev/null +++ b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/ARICEUtilitiesProcessor.java @@ -0,0 +1,116 @@ +package pl.com.labaj.autorecord.extension.arice; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import org.apiguardian.api.API; +import pl.com.labaj.autorecord.processor.context.MessagerLogger; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Messager; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; +import java.util.Arrays; +import java.util.Objects; +import java.util.Set; + +import static org.apiguardian.api.API.Status.MAINTAINED; + +/** + * {@inheritDoc} + */ +@API(status = MAINTAINED) +@SupportedAnnotationTypes("pl.com.labaj.autorecord.extension.arice.ARICEUtilities") +public class ARICEUtilitiesProcessor extends AbstractProcessor { + private static final ExtensionContext extContext = new ExtensionContext(); + + private Messager messager; + private MethodsClassGenerator methodsClassGenerator; + + /** + * {@inheritDoc} + */ + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + + extContext.init(processingEnv); + + messager = processingEnv.getMessager(); + var filer = processingEnv.getFiler(); + + methodsClassGenerator = new MethodsClassGenerator(extContext, filer); + } + + /** + * {@inheritDoc} + */ + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + annotations.forEach(annotation -> processAnnotation(roundEnv, annotation)); + + return false; + } + + private void processAnnotation(RoundEnvironment roundEnv, TypeElement annotation) { + var logger = new MessagerLogger(messager, annotation); + + roundEnv.getElementsAnnotatedWith(annotation).stream() + .map(element -> element.getAnnotation(ARICEUtilities.class)) + .map(NameTypes::toNameTypes) + .distinct() + .forEach(nameTypes -> methodsClassGenerator.generate(nameTypes.className, nameTypes.immutableTypes, logger)); + } + + record NameTypes(String className, String[] immutableTypes) { + private static NameTypes toNameTypes(ARICEUtilities a) { + return new NameTypes(a.className(), a.immutableTypes()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof NameTypes nameTypes)) return false; + return className.equals(nameTypes.className) && Arrays.equals(immutableTypes, nameTypes.immutableTypes); + } + + @Override + public int hashCode() { + int result = Objects.hash(className); + result = 31 * result + Arrays.hashCode(immutableTypes); + return result; + } + + @Override + public String toString() { + return "NameTypes{" + + "className='" + className + '\'' + + ", immutableTypes=" + Arrays.toString(immutableTypes) + + '}'; + } + } +} diff --git a/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/CopyMethodsGenerator.java b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/CopyMethodsGenerator.java new file mode 100644 index 0000000..841577f --- /dev/null +++ b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/CopyMethodsGenerator.java @@ -0,0 +1,201 @@ +package pl.com.labaj.autorecord.extension.arice; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.squareup.javapoet.AnnotationSpec; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.CodeBlock; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterSpec; +import com.squareup.javapoet.ParameterizedTypeName; +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeVariableName; +import pl.com.labaj.autorecord.context.Logger; +import pl.com.labaj.autorecord.context.StaticImports; + +import java.util.EnumMap; +import java.util.List; +import java.util.Optional; + +import static java.util.Comparator.reverseOrder; +import static java.util.Objects.isNull; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toMap; +import static javax.lang.model.element.Modifier.PUBLIC; +import static javax.lang.model.element.Modifier.STATIC; +import static org.apache.commons.lang3.StringUtils.substringBefore; +import static pl.com.labaj.autorecord.extension.arice.InterfaceType.allProcessedTypes; + +class CopyMethodsGenerator { + private static final EnumMap METHOD_GENERATORS = allProcessedTypes().stream() + .collect(toMap( + identity(), + CopyMethodsGenerator::builderFor, + (b1, b2) -> b1, + () -> new EnumMap<>(InterfaceType.class) + )); + + private final ExtensionContext extContext; + + private final StaticImports staticImports; + private final Logger logger; + + CopyMethodsGenerator(ExtensionContext extContext, StaticImports staticImports, Logger logger) { + this.extContext = extContext; + this.staticImports = staticImports; + this.logger = logger; + } + + List generateMethods(TypesStructure structure) { + return allProcessedTypes().stream() + .sorted(reverseOrder()) + .map(METHOD_GENERATORS::get) + .map(methodGenerator -> methodGenerator.generateMethod(extContext, structure, staticImports, logger)) + .toList(); + } + + private static MethodGenerator builderFor(InterfaceType iType) { + return (extContext, structure, staticImports, logger) -> { + var methodBuilder = getMethodBuilder(extContext, iType, logger); + + immutableTypesBlock(structure, iType) + .ifPresent(methodBuilder::addCode); + subTypesBlocks(extContext, iType, structure, logger) + .forEach(methodBuilder::addCode); + + var returnStatement = isNull(iType.factoryClassName()) + ? CodeBlock.of("return $L", iType.argumentName()) + : CodeBlock.of("return $T.$L($L)", iType.factoryClassName(), iType.factoryMethodName(), iType.argumentName()); + methodBuilder.addStatement(returnStatement); + + return methodBuilder.build(); + }; + } + + private static MethodSpec.Builder getMethodBuilder(ExtensionContext extContext, InterfaceType iType, Logger logger) { + var builder = MethodSpec.methodBuilder("immutable") + .addModifiers(PUBLIC, STATIC); + + if (iType.genericNames().isEmpty()) { + var annotation = AnnotationSpec.builder(SuppressWarnings.class) + .addMember("value", "$S", "unchecked") + .build(); + builder.addAnnotation(annotation); + } + + getTypeName(extContext, iType, builder, true); + var trimmedTypeName = getTypeName(extContext, iType, builder, false); + + var parameterSpec = ParameterSpec.builder(trimmedTypeName, iType.argumentName()).build(); + + builder.returns(trimmedTypeName) + .addParameter(parameterSpec); + + return builder; + } + + private static TypeName getTypeName(ExtensionContext extContext, InterfaceType iType, MethodSpec.Builder methodBuilder, boolean full) { + + var mirrorType = extContext.getInterfaceMirrorType(iType); + + if (iType.genericNames().isEmpty()) { + return TypeName.get(mirrorType); + } + + var className = (ClassName) ClassName.get(mirrorType); + var typeVariableNames = iType.genericNames().stream() + .map(name -> full ? name : substringBefore(name, " ")) + .map(TypeVariableName::get) + .toList(); + + if (full) { + typeVariableNames.forEach(methodBuilder::addTypeVariable); + } + return ParameterizedTypeName.get(className, typeVariableNames.toArray(TypeVariableName[]::new)); + } + + private static Optional immutableTypesBlock(TypesStructure structure, InterfaceType iType) { + var immutableTypeNames = structure.getClassNames(iType); + if (immutableTypeNames.isEmpty()) { + return Optional.empty(); + } + + var ifFormat = new StringBuilder("if ("); + var i = 0; + for (var iterator = immutableTypeNames.iterator(); iterator.hasNext(); i++) { + iterator.next(); + String name = iType.argumentName(); + + ifFormat.append(name).append(" instanceof $T"); + if (!iType.genericNames().isEmpty()) { + var genericClause = iType.genericNames().stream() + .map(n -> substringBefore(n, " ")) + .collect(joining(", ", "<", ">")); + ifFormat.append(genericClause); + } + + if (iterator.hasNext()) { + ifFormat.append(i == 0 ? "\n$>$>|| " : "\n|| "); + } + } + ifFormat.append(")"); + + var size = immutableTypeNames.size(); + var block = CodeBlock.builder() + .beginControlFlow(ifFormat.toString(), immutableTypeNames.toArray()) + .addStatement(size > 1 ? "$<$ subTypesBlocks(ExtensionContext extContext, InterfaceType iType, TypesStructure structure, Logger logger) { + return iType.directSubTypes().stream() + .filter(structure::needsAdditionalMethod) + .sorted(reverseOrder()) + .map(subPType -> subTypeBlock(extContext, subPType, iType, structure)) + .toList(); + } + + private static CodeBlock subTypeBlock(ExtensionContext extContext, InterfaceType iType, InterfaceType parent, TypesStructure structure) { + var argumentName = iType.argumentName(); + + var parentGenericNames = parent.genericNames(); + var genericClause = parentGenericNames.isEmpty() || !iType.checkGenericInInstanceOf() + ? "" + : parentGenericNames.stream().collect(joining(",", "<", ">")); + var statement = structure.needsAdditionalMethod(iType) + ? CodeBlock.of("return $L($L)", "immutable", argumentName) + : CodeBlock.of("return $T.$L($L)", iType.factoryClassName(), iType.factoryMethodName(), argumentName); + + return CodeBlock.builder() + .beginControlFlow("if ($L instanceof $T$L $L)", parent.argumentName(), extContext.getInterfaceMirrorType(iType), genericClause, argumentName) + .addStatement(statement) + .endControlFlow() + .build(); + } + + @FunctionalInterface + private interface MethodGenerator { + MethodSpec generateMethod(ExtensionContext extContext, + TypesStructure structure, + StaticImports staticImports, + Logger logger); + } +} diff --git a/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/ExtensionContext.java b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/ExtensionContext.java new file mode 100644 index 0000000..ca276bd --- /dev/null +++ b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/ExtensionContext.java @@ -0,0 +1,140 @@ +package pl.com.labaj.autorecord.extension.arice; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import pl.com.labaj.autorecord.context.Logger; +import pl.com.labaj.autorecord.processor.AutoRecordProcessorException; + +import javax.annotation.Nullable; +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; +import static java.util.stream.Collectors.toSet; +import static pl.com.labaj.autorecord.extension.arice.InterfaceType.allProcessedTypes; + +class ExtensionContext { + private Types existingTypeUtils; + private Elements elementUtils; + private Types typeUtils; + + private final Map immutableTypes = new HashMap<>(); + private final EnumMap interfaceTypes = new EnumMap<>(InterfaceType.class); + + void init(ProcessingEnvironment processingEnv) { + resetIfNeeded(processingEnv); + allProcessedTypes().forEach(name -> interfaceTypes.computeIfAbsent(name, this::loadType)); + } + + Set getTypes(Set immutableNames) { + return immutableNames.stream() + .map(name -> immutableTypes.computeIfAbsent(name, this::loadType)) + .filter(Objects::nonNull) + .collect(toSet()); + } + + TypeMirror erasure(TypeMirror type) { + return typeUtils.erasure(type); + } + + boolean isSubtype(TypeMirror type, InterfaceType iType, Logger logger) { + var pTypeMirror = interfaceTypes.get(iType); + if (nonNull(pTypeMirror)) { + return typeUtils.isSubtype(type, pTypeMirror); + } + + logger.warning("isSubtype fallback for :" + iType); + + return typeUtils.directSupertypes(type).stream() + .map(Object::toString) + .anyMatch(s -> s.equals(iType.className())); + } + + boolean isSubtype(TypeMirror type1, TypeMirror type2) { + return typeUtils.isSubtype(type1, type2); + } + + boolean isSameType(TypeMirror type, InterfaceType iType, Logger logger) { + var pTypeMirror = interfaceTypes.get(iType); + if (nonNull(pTypeMirror)) { + return typeUtils.isSameType(type, pTypeMirror); + } + + logger.warning("isSameType fallback for :" + iType); + + return type.toString().equals(iType.className()); + } + + boolean isSameType(TypeMirror type1, TypeMirror type2) { + return typeUtils.isSameType(type1, type2); + } + + @Nullable + TypeMirror getImmutableType(String name, Logger logger) { + var typeMirror = immutableTypes.computeIfAbsent(name, this::loadType); + + if (isNull(typeMirror)) { + logger.debug("Cannot get type for " + name); + } + + return typeMirror; + } + + TypeMirror getInterfaceMirrorType(InterfaceType iType) { + var typeMirror = interfaceTypes.computeIfAbsent(iType, this::loadType); + + if (isNull(typeMirror)) { + throw new AutoRecordProcessorException("Cannot get type for " + iType+ ". Please check if it's in classpath"); + } + + return typeMirror; + } + + private void resetIfNeeded(ProcessingEnvironment processingEnv) { + + if (isNull(existingTypeUtils) || existingTypeUtils != processingEnv.getTypeUtils()) { + elementUtils = processingEnv.getElementUtils(); + typeUtils = processingEnv.getTypeUtils(); + existingTypeUtils = typeUtils; + + immutableTypes.clear(); + interfaceTypes.clear(); + } + } + + private TypeMirror loadType(InterfaceType iType) { + return loadType(iType.className()); + } + + private TypeMirror loadType(String className) { + var typeElement = elementUtils.getTypeElement(className); + if (isNull(typeElement)) { + return null; + } + + var type = typeElement.asType(); + return typeUtils.erasure(type); + } +} diff --git a/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/ImmutableCollectionsExtension.java b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/ImmutableCollectionsExtension.java new file mode 100644 index 0000000..df230b5 --- /dev/null +++ b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/ImmutableCollectionsExtension.java @@ -0,0 +1,189 @@ +package pl.com.labaj.autorecord.extension.arice; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.squareup.javapoet.AnnotationSpec; +import com.squareup.javapoet.CodeBlock; +import io.soabase.recordbuilder.core.RecordBuilder; +import org.apiguardian.api.API; +import pl.com.labaj.autorecord.AutoRecord; +import pl.com.labaj.autorecord.context.Context; +import pl.com.labaj.autorecord.context.StaticImports; +import pl.com.labaj.autorecord.extension.CompactConstructorExtension; + +import javax.annotation.processing.Generated; +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.type.TypeMirror; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import static java.lang.Character.isLowerCase; +import static java.lang.Integer.toUnsignedString; +import static java.lang.Math.abs; +import static java.util.stream.Collectors.joining; +import static javax.lang.model.type.TypeKind.ARRAY; +import static org.apiguardian.api.API.Status.STABLE; +import static pl.com.labaj.autorecord.extension.arice.Names.ARICE_PACKAGE; +import static pl.com.labaj.autorecord.extension.arice.Names.allImmutableNames; +import static pl.com.labaj.autorecord.extension.arice.Names.notPredefinedNames; +import static pl.com.labaj.autorecord.extension.arice.RecordComponent.debugInfo; + +/** + * An implementations of the {@link AutoRecord} {@link CompactConstructorExtension} for customizing a record generation process + * with support for immutable collections. + *

+ * Components that are collections or maps a being copied to their corresponding immutable types. + *

+ * Note: It is recommended to disable {@link RecordBuilder.Options#useImmutableCollections() useImmutableCollections} + * and {@link RecordBuilder.Options#useUnmodifiableCollections() useUnmodifiableCollections} features for generated builders. + */ +@API(status = STABLE) +public class ImmutableCollectionsExtension implements CompactConstructorExtension { + private static final AnnotationSpec GENERATED_WITH_EXTENSION_ANNOTATION = AnnotationSpec.builder(Generated.class) + .addMember("value", "$S", AutoRecord.class.getName()) + .addMember("comments", "$S", ImmutableCollectionsExtension.class.getName()) + .build(); + + private static final String METHODS_CLASS_NAME = ARICE_PACKAGE + ".ARICE"; + + private static final ExtensionContext extContext = new ExtensionContext(); + + private Set immutableTypeNames; + private Set immutableTypes; + private Set immutableNames; + private List componentsToProcess; + private String methodsClassName; + + /** + * Initializes the extension with the given processing environment and parameters. + * + * @param processingEnv The {@link ProcessingEnvironment} providing access to facilities provided by the annotation processing tool. + * @param parameters An array of type names that are immutable. + */ + @Override + public void init(ProcessingEnvironment processingEnv, String[] parameters) { + extContext.init(processingEnv); + + immutableTypeNames = notPredefinedNames(parameters); + immutableNames = allImmutableNames(parameters); + immutableTypes = extContext.getTypes(immutableNames); + methodsClassName = methodsClassName(); + } + + /** + * Determines whether the extension should generate the compact constructor content. + *

+ * It filters out components that: + *

+ * If something has left, the content will be generated. + * + * @param isGeneratedByProcessor A flag indicating if the compact constructor is generated by the + * {@link AutoRecord} Processor. If the flag is {@code false}, + * it means that compact constructor won't be generated without extensions. + * @param context The {@link Context} object containing the annotation processing information. + * @return {@code true} if the compact constructor should be generated by the extension, {@code false} otherwise. + */ + @Override + public boolean shouldGenerateCompactConstructor(boolean isGeneratedByProcessor, Context context) { + var logger = context.logger(); + + var componentBuilder = new RecordComponent.Builder(extContext, immutableTypes, logger); + var declaredComponents = context.components() + .stream() + .filter(recordComponent -> !recordComponent.isAnnotatedWith(Mutable.class)) + .filter(recordComponent -> !recordComponent.type().getKind().isPrimitive()) + .filter(recordComponent -> recordComponent.type().getKind() != ARRAY) + .toList(); + if (declaredComponents.isEmpty()) { + return false; + } + + componentsToProcess = declaredComponents.stream() + .map(componentBuilder::toExtensionRecordComponent) + .filter(Objects::nonNull) + .toList(); + + return !componentsToProcess.isEmpty(); + } + + /** + * {@inheritDoc} + */ + @Override + public CodeBlock suffixCompactConstructorContent(Context context, StaticImports staticImports) { + var logger = context.logger(); + + var structreBuilder = new TypesStructure.Builder(extContext, immutableNames); + var structure = structreBuilder.buildStructure(logger); + + if (logger.isDebugEnabled()) { + logger.debug("Components to process:\n" + debugInfo(componentsToProcess)); + logger.note("Types structure:\n" + structure.debugInfo()); + } + + var codeBuilder = CodeBlock.builder(); + + var statementGenerator = new StatementGenerator(extContext, structure, methodsClassName, staticImports, logger); + + componentsToProcess.stream() + .map(statementGenerator::generateStatement) + .forEach(codeBuilder::addStatement); + + return codeBuilder.build(); + } + + /** + * Returns, among others, {@link ARICEUtilities} annotation that specifies: + * + * @param context The {@link Context} object containing the annotation processing information. + * @param staticImports The {@link StaticImports} object containing {@code static import} statements that will be added into the generated record. + * @return A list of {@link AnnotationSpec} representing annotations that need to be added to the generated record. + */ + @Override + public List annotationsToSupportCompactConstructor(Context context, StaticImports staticImports) { + var builder = AnnotationSpec.builder(ARICEUtilities.class) + .addMember("className", "$S", methodsClassName); + + immutableTypeNames.forEach(parameter -> builder.addMember("immutableTypes", "$S", parameter)); + + return List.of(GENERATED_WITH_EXTENSION_ANNOTATION, builder.build()); + } + + private String methodsClassName() { + return METHODS_CLASS_NAME + (immutableTypeNames.isEmpty() ? "" : "_" + stringHashCode(immutableTypeNames)); + } + + private String stringHashCode(Set parameters) { + var hashCode = parameters.stream() + .sorted() + .mapToInt(String::hashCode) + .reduce(1, (h1, h2) -> 31 * h1 + h2); + var alphaString = toUnsignedString(abs(hashCode), 26); + + return alphaString.chars() + .map(i -> isLowerCase(i) ? i - 22 : i + 17) + .map(i -> 65 + (i + 17) % 26) + .mapToObj(Character::toString) + .collect(joining()); + } +} diff --git a/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/InterfaceType.java b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/InterfaceType.java new file mode 100644 index 0000000..660c2b6 --- /dev/null +++ b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/InterfaceType.java @@ -0,0 +1,169 @@ +package pl.com.labaj.autorecord.extension.arice; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.squareup.javapoet.ClassName; + +import javax.annotation.Nullable; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; +import static org.apache.commons.lang3.StringUtils.capitalize; +import static pl.com.labaj.autorecord.extension.arice.Names.ARICE_IMMUTABLE_COLLECTION_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.arice.Names.ARICE_IMMUTABLE_DEQUE_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.arice.Names.GUAVA_IMMUTABLE_LIST_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.arice.Names.GUAVA_IMMUTABLE_LIST_MULTIMAP_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.arice.Names.GUAVA_IMMUTABLE_MAP_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.arice.Names.GUAVA_IMMUTABLE_MULTIMAP_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.arice.Names.GUAVA_IMMUTABLE_MULTISET_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.arice.Names.GUAVA_IMMUTABLE_RANGE_MAP_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.arice.Names.GUAVA_IMMUTABLE_RANGE_SET_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.arice.Names.GUAVA_IMMUTABLE_SET_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.arice.Names.GUAVA_IMMUTABLE_SET_MULTIMAP_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.arice.Names.GUAVA_IMMUTABLE_SORTED_MAP_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.arice.Names.GUAVA_IMMUTABLE_SORTED_MULTISET_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.arice.Names.GUAVA_IMMUTABLE_SORTED_SET_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.arice.Names.GUAVA_IMMUTABLE_TABLE_CLASS_NAME; + +@SuppressWarnings("java:S1192") +enum InterfaceType { + TABLE("com.google.common.collect.Table", Set.of(), GUAVA_IMMUTABLE_TABLE_CLASS_NAME, "copyOf") { + @Override + List genericNames() {return List.of("R", "C", "V");} + }, + RANGE_MAP("com.google.common.collect.RangeMap", Set.of(), GUAVA_IMMUTABLE_RANGE_MAP_CLASS_NAME, "copyOf") { + @Override + List genericNames() {return List.of("K extends Comparable", "V");} + }, + RANGE_SET("com.google.common.collect.RangeSet", Set.of(), GUAVA_IMMUTABLE_RANGE_SET_CLASS_NAME, "copyOf") { + @Override + List genericNames() {return List.of("E extends Comparable");} + }, + LIST_MULTIMAP("com.google.common.collect.ListMultimap", Set.of(), GUAVA_IMMUTABLE_LIST_MULTIMAP_CLASS_NAME, "copyOf") { + @Override + List genericNames() {return List.of("K", "V");} + }, + SET_MULTIMAP("com.google.common.collect.SetMultimap", Set.of(), GUAVA_IMMUTABLE_SET_MULTIMAP_CLASS_NAME, "copyOf") { + @Override + List genericNames() {return List.of("K", "V");} + }, + MULTIMAP("com.google.common.collect.Multimap", Set.of(SET_MULTIMAP, LIST_MULTIMAP), GUAVA_IMMUTABLE_MULTIMAP_CLASS_NAME, "copyOf") { + @Override + List genericNames() {return List.of("K", "V");} + }, + SORTED_MULTISET("com.google.common.collect.SortedMultiset", Set.of(), GUAVA_IMMUTABLE_SORTED_MULTISET_CLASS_NAME, "copyOfSorted"), + MULTISET("com.google.common.collect.Multiset", Set.of(SORTED_MULTISET), GUAVA_IMMUTABLE_MULTISET_CLASS_NAME, "copyOf"), + NAVIGABLE_MAP("java.util.NavigableMap", Set.of(), GUAVA_IMMUTABLE_SORTED_MAP_CLASS_NAME, "copyOfSorted") { + @Override + List genericNames() {return List.of("K", "V");} + }, + SORTED_MAP("java.util.SortedMap", Set.of(NAVIGABLE_MAP), GUAVA_IMMUTABLE_SORTED_MAP_CLASS_NAME, "copyOfSorted") { + @Override + List genericNames() {return List.of("K", "V");} + }, + MAP("java.util.Map", Set.of(SORTED_MAP), GUAVA_IMMUTABLE_MAP_CLASS_NAME, "copyOf") { + @Override + List genericNames() {return List.of("K", "V");} + }, + DEQUE("java.util.Deque", Set.of(), ARICE_IMMUTABLE_DEQUE_CLASS_NAME, "copyOfQueue"), + QUEUE("java.util.Queue", Set.of(DEQUE), ARICE_IMMUTABLE_DEQUE_CLASS_NAME, "copyOfQueue"), + LIST("java.util.List", Set.of(), GUAVA_IMMUTABLE_LIST_CLASS_NAME, "copyOf"), + NAVIGABLE_SET("java.util.NavigableSet", Set.of(), GUAVA_IMMUTABLE_SORTED_SET_CLASS_NAME, "copyOfSorted"), + SORTED_SET("java.util.SortedSet", Set.of(NAVIGABLE_SET), GUAVA_IMMUTABLE_SORTED_SET_CLASS_NAME, "copyOfSorted"), + SET("java.util.Set", Set.of(SORTED_SET), GUAVA_IMMUTABLE_SET_CLASS_NAME, "copyOf"), + COLLECTION("java.util.Collection", Set.of(SET, LIST, QUEUE, MULTISET), ARICE_IMMUTABLE_COLLECTION_CLASS_NAME, "copyOfCollection"), + OBJECT("java.lang.Object", Set.of(COLLECTION, MAP, MULTIMAP, RANGE_SET, RANGE_MAP, TABLE), null, null) { + @Override + List genericNames() {return List.of();} + }; + + private static final Set ALL_TYPES = EnumSet.allOf(InterfaceType.class); + private static final Map NAME_TO_TYPES = ALL_TYPES.stream() + .collect(toMap(InterfaceType::className, identity())); + private final String className; + + private final Set directSubTypes; + private final ClassName factoryClassName; + private final String factoryMethodName; + private final String argumentName; + + InterfaceType(String className, Set directSubTypes, ClassName factoryClassName, String factoryMethodName) { + this.className = className; + this.directSubTypes = directSubTypes; + this.factoryClassName = factoryClassName; + this.factoryMethodName = factoryMethodName; + + argumentName = nameToCamelCase(); + } + + static Set allProcessedTypes() { + return ALL_TYPES; + } + + @Nullable + static InterfaceType interfaceTypeWith(String className) { + return NAME_TO_TYPES.get(className); + } + + String className() { + return className; + } + + String argumentName() { + return argumentName; + } + + Set directSubTypes() { + return directSubTypes; + } + + boolean checkGenericInInstanceOf() { + return true; + } + + ClassName factoryClassName() { + return factoryClassName; + } + + String factoryMethodName() { + return factoryMethodName; + } + + List genericNames() { + return List.of("E"); + } + + @Override + public String toString() { + return className; + } + + private String nameToCamelCase() { + var parts = name().split("_"); + var camelCase = new StringBuilder(parts[0].toLowerCase()); + + for (int i = 1; i < parts.length; i++) { + camelCase.append(capitalize(parts[i].toLowerCase())); + } + + return camelCase.toString(); + } +} diff --git a/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/MethodsClassGenerator.java b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/MethodsClassGenerator.java new file mode 100644 index 0000000..0d8a3bb --- /dev/null +++ b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/MethodsClassGenerator.java @@ -0,0 +1,137 @@ +package pl.com.labaj.autorecord.extension.arice; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.squareup.javapoet.AnnotationSpec; +import com.squareup.javapoet.CodeBlock; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.TypeSpec; +import org.apache.commons.lang3.StringUtils; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; +import pl.com.labaj.autorecord.context.Logger; +import pl.com.labaj.autorecord.processor.AutoRecordProcessor; +import pl.com.labaj.autorecord.processor.AutoRecordProcessorException; +import pl.com.labaj.autorecord.processor.context.MessagerLogger; +import pl.com.labaj.autorecord.processor.context.StaticImportsCollectors; + +import javax.annotation.processing.Filer; +import javax.annotation.processing.Generated; +import java.util.Arrays; +import java.util.List; + +import static javax.lang.model.element.Modifier.FINAL; +import static javax.lang.model.element.Modifier.PRIVATE; +import static javax.lang.model.element.Modifier.PUBLIC; +import static org.apache.commons.lang3.StringUtils.rightPad; +import static pl.com.labaj.autorecord.extension.arice.Names.ARICE_PACKAGE; +import static pl.com.labaj.autorecord.extension.arice.Names.allImmutableNames; + +class MethodsClassGenerator { + private static final AnnotationSpec GENERATED_WITH_EXTENSION_ANNOTATION = AnnotationSpec.builder(Generated.class) + .addMember("value", "$S", AutoRecordProcessor.class.getName()) + .addMember("value", "$S", ARICEUtilitiesProcessor.class.getName()) + .addMember("comments", "$S", ImmutableCollectionsExtension.class.getName()) + .build(); + private static final AnnotationSpec GENERATED_WITH_AUTO_RECORD_ANNOTATION = AnnotationSpec.builder(GeneratedWithAutoRecord.class).build(); + private static final List ANNOTATIONS = List.of( + GENERATED_WITH_EXTENSION_ANNOTATION, + GENERATED_WITH_AUTO_RECORD_ANNOTATION + ); + private final ExtensionContext extContext; + private final Filer filer; + + public MethodsClassGenerator(ExtensionContext extContext, Filer filer) { + this.extContext = extContext; + this.filer = filer; + } + + public void generate(String className, String[] immutableTypes, MessagerLogger logger) { + logStartEnd("[START] ", className, logger); + + try { + var classSimpleName = StringUtils.removeStart(className, ARICE_PACKAGE + "."); + var classBuilder = TypeSpec.classBuilder(classSimpleName); + var staticImports = new StaticImportsCollectors(); + + classBuilder.addAnnotations(ANNOTATIONS) + .addModifiers(PUBLIC, FINAL) + .addJavadoc(generateJavadoc(immutableTypes)); + + var structure = fillStructure(immutableTypes, logger); + var additionalMethodsGenerator = new CopyMethodsGenerator(extContext, staticImports, logger); + additionalMethodsGenerator.generateMethods(structure) + .forEach(classBuilder::addMethod); + + var constructor = MethodSpec.constructorBuilder() + .addModifiers(PRIVATE) + .build(); + classBuilder.addMethod(constructor); + + var javaFile = buildJavaFile(staticImports, classBuilder.build()); + javaFile.writeTo(filer); + + logStartEnd("[ END ] ", className, logger); + } catch (Exception e) { + throw new AutoRecordProcessorException("Cannot generate file for " + className, e); + } + } + + private TypesStructure fillStructure(String[] names, Logger logger) { + var structreBuilder = new TypesStructure.Builder(extContext, allImmutableNames(names)); + var structure = structreBuilder.buildStructure(logger); + + if (logger.isDebugEnabled()) { + logger.note("Types structure:\n" + structure.debugInfo()); + } + + return structure; + } + + private CodeBlock generateJavadoc(String[] immutableTypes) { + var builder = CodeBlock.builder() + .add("Class providing methods to copy collections to their corresponding immutable versions"); + + if (immutableTypes.length > 0) { + builder.add("\n") + .add("

\n") + .add("User defined immutable types:\n") + .add("

    \n"); + + Arrays.stream(immutableTypes) + .map(type -> "\t
  • {@link " + type + "}
  • \n") + .forEach(builder::add); + + builder.add("
"); + } + return builder.build(); + } + + private JavaFile buildJavaFile(StaticImportsCollectors staticImports, TypeSpec classSpec) { + var javaFileBuilder = JavaFile.builder(ARICE_PACKAGE, classSpec); + staticImports.forEach(javaFileBuilder::addStaticImport); + + return javaFileBuilder.build(); + } + + private void logStartEnd(String prefix, String className, Logger logger) { + if (logger.isDebugEnabled()) { + var message = rightPad(prefix + className + " ", 100, "-"); + logger.note(message); + } + } +} diff --git a/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/Names.java b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/Names.java new file mode 100644 index 0000000..65a6719 --- /dev/null +++ b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/Names.java @@ -0,0 +1,97 @@ + +package pl.com.labaj.autorecord.extension.arice; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.squareup.javapoet.ClassName; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.toSet; + +final class Names { + static final String ARICE_PACKAGE = "pl.com.labaj.autorecord.extension.arice"; + static final String ARICE_COLLECTIONS_PACKAGE = "pl.com.labaj.autorecord.collections"; + + private static final String GUAVA_PACKAGE = "com.google.common.collect"; + private static final String IMMUTABLE_SET = "ImmutableSet"; + private static final String IMMUTABLE_SORTED_SET = "ImmutableSortedSet"; + private static final String IMMUTABLE_LIST = "ImmutableList"; + private static final String IMMUTABLE_MAP = "ImmutableMap"; + private static final String IMMUTABLE_SORTED_MAP = "ImmutableSortedMap"; + private static final String IMMUTABLE_COLLECTION = "ImmutableCollection"; + private static final String IMMUTABLE_DEQUE = "ImmutableDeque"; + private static final String IMMUTABLE_MULTISET = "ImmutableMultiset"; + private static final String IMMUTABLE_SORTED_MULTISET = "ImmutableSortedMultiset"; + private static final String IMMUTABLE_MULTIMAP = "ImmutableMultimap"; + private static final String IMMUTABLE_SET_MULTIMAP = "ImmutableSetMultimap"; + private static final String IMMUTABLE_LIST_MULTIMAP = "ImmutableListMultimap"; + private static final String IMMUTABLE_RANGE_SET = "ImmutableRangeSet"; + private static final String IMMUTABLE_RANGE_MAP = "ImmutableRangeMap"; + private static final String IMMUTABLE_TABLE = "ImmutableTable"; + + static final ClassName GUAVA_IMMUTABLE_SET_CLASS_NAME = ClassName.get(GUAVA_PACKAGE, IMMUTABLE_SET); + static final ClassName GUAVA_IMMUTABLE_SORTED_SET_CLASS_NAME = ClassName.get(GUAVA_PACKAGE, IMMUTABLE_SORTED_SET); + static final ClassName GUAVA_IMMUTABLE_LIST_CLASS_NAME = ClassName.get(GUAVA_PACKAGE, IMMUTABLE_LIST); + static final ClassName GUAVA_IMMUTABLE_MAP_CLASS_NAME = ClassName.get(GUAVA_PACKAGE, IMMUTABLE_MAP); + static final ClassName GUAVA_IMMUTABLE_SORTED_MAP_CLASS_NAME = ClassName.get(GUAVA_PACKAGE, IMMUTABLE_SORTED_MAP); + static final ClassName ARICE_IMMUTABLE_COLLECTION_CLASS_NAME = ClassName.get(ARICE_COLLECTIONS_PACKAGE, IMMUTABLE_COLLECTION); + static final ClassName ARICE_IMMUTABLE_DEQUE_CLASS_NAME = ClassName.get(ARICE_COLLECTIONS_PACKAGE, IMMUTABLE_DEQUE); + static final ClassName GUAVA_IMMUTABLE_MULTISET_CLASS_NAME = ClassName.get(GUAVA_PACKAGE, IMMUTABLE_MULTISET); + static final ClassName GUAVA_IMMUTABLE_SORTED_MULTISET_CLASS_NAME = ClassName.get(GUAVA_PACKAGE, IMMUTABLE_SORTED_MULTISET); + static final ClassName GUAVA_IMMUTABLE_MULTIMAP_CLASS_NAME = ClassName.get(GUAVA_PACKAGE, IMMUTABLE_MULTIMAP); + static final ClassName GUAVA_IMMUTABLE_SET_MULTIMAP_CLASS_NAME = ClassName.get(GUAVA_PACKAGE, IMMUTABLE_SET_MULTIMAP); + static final ClassName GUAVA_IMMUTABLE_LIST_MULTIMAP_CLASS_NAME = ClassName.get(GUAVA_PACKAGE, IMMUTABLE_LIST_MULTIMAP); + static final ClassName GUAVA_IMMUTABLE_RANGE_SET_CLASS_NAME = ClassName.get(GUAVA_PACKAGE, IMMUTABLE_RANGE_SET); + static final ClassName GUAVA_IMMUTABLE_RANGE_MAP_CLASS_NAME = ClassName.get(GUAVA_PACKAGE, IMMUTABLE_RANGE_MAP); + static final ClassName GUAVA_IMMUTABLE_TABLE_CLASS_NAME = ClassName.get(GUAVA_PACKAGE, IMMUTABLE_TABLE); + + private static final Set PREDEFINED_IMMUTABLE_NAMES = Set.of( + GUAVA_PACKAGE + "." + IMMUTABLE_SET, + GUAVA_PACKAGE + "." + IMMUTABLE_SORTED_SET, + GUAVA_PACKAGE + "." + IMMUTABLE_LIST, + GUAVA_PACKAGE + "." + IMMUTABLE_MAP, + GUAVA_PACKAGE + "." + IMMUTABLE_SORTED_MAP, + ARICE_COLLECTIONS_PACKAGE + "." + IMMUTABLE_COLLECTION, + ARICE_COLLECTIONS_PACKAGE + "." + IMMUTABLE_DEQUE, + GUAVA_PACKAGE + "." + IMMUTABLE_MULTISET, + GUAVA_PACKAGE + "." + IMMUTABLE_SORTED_MULTISET, + GUAVA_PACKAGE + "." + IMMUTABLE_MULTIMAP, + GUAVA_PACKAGE + "." + IMMUTABLE_SET_MULTIMAP, + GUAVA_PACKAGE + "." + IMMUTABLE_LIST_MULTIMAP, + GUAVA_PACKAGE + "." + IMMUTABLE_RANGE_SET, + GUAVA_PACKAGE + "." + IMMUTABLE_RANGE_MAP, + GUAVA_PACKAGE + "." + IMMUTABLE_TABLE + ); + + private Names() {} + + static Set notPredefinedNames(String[] parameters) { + return Arrays.stream(parameters) + .filter(name -> !PREDEFINED_IMMUTABLE_NAMES.contains(name)) + .collect(toSet()); + } + + static Set allImmutableNames(String[] parameters) { + return Stream.concat( + PREDEFINED_IMMUTABLE_NAMES.stream(), + Arrays.stream(parameters) + ).collect(toSet()); + } +} diff --git a/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/RecordComponent.java b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/RecordComponent.java new file mode 100644 index 0000000..76f8b3a --- /dev/null +++ b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/RecordComponent.java @@ -0,0 +1,109 @@ +package pl.com.labaj.autorecord.extension.arice; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import pl.com.labaj.autorecord.context.Logger; + +import javax.annotation.Nullable; +import javax.lang.model.type.TypeMirror; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static java.lang.Boolean.FALSE; +import static java.util.Objects.isNull; +import static java.util.stream.Collectors.joining; +import static javax.lang.model.type.TypeKind.ERROR; +import static pl.com.labaj.autorecord.extension.arice.InterfaceType.OBJECT; +import static pl.com.labaj.autorecord.extension.arice.InterfaceType.allProcessedTypes; +import static pl.com.labaj.autorecord.extension.arice.InterfaceType.interfaceTypeWith; + +record RecordComponent(String name, boolean isNullable, TypeMirror declaredType, InterfaceType pType) { + + static String debugInfo(List recordComponents) { + return recordComponents.stream() + .map(RecordComponent::toString) + .collect(joining("\n")); + } + + @Override + public String toString() { + return name + " : " + declaredType; + } + + static class Builder { + private final ExtensionContext extContext; + private final Set immutableTypes; + private final Logger logger; + private final Map toBeProcessedCache = new HashMap<>(); + + Builder(ExtensionContext extContext, Set immutableTypes, Logger logger) { + this.extContext = extContext; + this.immutableTypes = immutableTypes; + this.logger = logger; + } + + @Nullable + RecordComponent toExtensionRecordComponent(pl.com.labaj.autorecord.context.RecordComponent component) { + var isNullable = component.isAnnotatedWith(Nullable.class); + var componentType = component.type(); + var declaredType = extContext.erasure(componentType); + var shouldBeProcessed = toBeProcessedCache.computeIfAbsent(declaredType.toString(), name -> shouldBeProcessed(declaredType)); + + if (FALSE.equals(shouldBeProcessed)) { + return null; + } + + var pType = interfaceTypeWith(declaredType.toString()); + if (isNull(pType) || declaredType.getKind() == ERROR) { + logger.warning("Unrecognized type for processing " + declaredType); + pType = OBJECT; + } + + return new RecordComponent(component.name(), isNullable, declaredType, pType); + } + + private boolean shouldBeProcessed(TypeMirror componentType) { + if (componentType.getKind() == ERROR) { + return true; + } + + var isImmutableType = immutableTypes.stream() + .anyMatch(immutableType -> extContext.isSameType(componentType, immutableType)); + if (isImmutableType) { + logger.debug(componentType + " is immutable"); + return false; + } + + var hasImmutableSuperType = immutableTypes.stream() + .anyMatch(immutableType -> extContext.isSubtype(componentType, immutableType)); + if (hasImmutableSuperType) { + logger.debug(componentType + " has immutable super type"); + return false; + } + + var isCollection = allProcessedTypes().stream() + .anyMatch(pType -> extContext.isSameType(componentType, pType, logger)); + if (isCollection) { + logger.debug(componentType + " has processed type"); + } + + return isCollection; + } + } +} diff --git a/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/StatementGenerator.java b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/StatementGenerator.java new file mode 100644 index 0000000..ac88d6f --- /dev/null +++ b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/StatementGenerator.java @@ -0,0 +1,93 @@ +package pl.com.labaj.autorecord.extension.arice; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.CodeBlock; +import pl.com.labaj.autorecord.context.Logger; +import pl.com.labaj.autorecord.context.StaticImports; + +import javax.annotation.Nullable; +import java.util.EnumMap; +import java.util.Objects; + +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; +import static org.apache.commons.lang3.StringUtils.substringAfterLast; +import static pl.com.labaj.autorecord.extension.arice.InterfaceType.allProcessedTypes; +import static pl.com.labaj.autorecord.extension.arice.Names.ARICE_PACKAGE; + +final class StatementGenerator { + + private static final EnumMap RECORD_STATEMENT_GENERATORS = allProcessedTypes().stream() + .collect(toMap( + identity(), + StatementGenerator::builderFor, + (b1, b2) -> b1, + () -> new EnumMap<>(InterfaceType.class) + )); + + private final ExtensionContext extContext; + private final TypesStructure structure; + private final String methodsClassName; + private final StaticImports staticImports; + private final Logger logger; + + StatementGenerator(ExtensionContext extContext, TypesStructure structure, String methodsClassName, StaticImports staticImports, Logger logger) { + this.extContext = extContext; + this.structure = structure; + this.methodsClassName = methodsClassName; + this.staticImports = staticImports; + this.logger = logger; + } + + CodeBlock generateStatement(RecordComponent recordComponent) { + var pType = recordComponent.pType(); + + return RECORD_STATEMENT_GENERATORS.get(pType) + .generateStatement(recordComponent, extContext, structure, methodsClassName, staticImports, logger); + } + + private static RecordStatementGenerator builderFor(InterfaceType iType) { + return (component, extensionContext, structure, methodsClassName, staticImports, logger) -> { + var nullable = component.isNullable(); + if (nullable) { + staticImports.add(Objects.class, "isNull"); + } + + var format = nullable ? "$1L = isNull($1L) ? null : $2T.$3L($1L)" : "$1L = $2T.$3L($1L)"; + + if (structure.needsAdditionalMethod(iType)) { + var className = ClassName.get(ARICE_PACKAGE, substringAfterLast(methodsClassName, ".")); + return CodeBlock.of(format, component.name(), className, "immutable"); + } + + return CodeBlock.of(format, component.name(), iType.factoryClassName(), iType.factoryMethodName()); + }; + } + + @FunctionalInterface + private interface RecordStatementGenerator { + @Nullable + CodeBlock generateStatement(RecordComponent recordComponent, + ExtensionContext extensionContext, + TypesStructure structure, + String methodsClassName, + StaticImports staticImports, + Logger logger); + } +} diff --git a/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/TypesStructure.java b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/TypesStructure.java new file mode 100644 index 0000000..d6543e8 --- /dev/null +++ b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/TypesStructure.java @@ -0,0 +1,173 @@ +package pl.com.labaj.autorecord.extension.arice; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.squareup.javapoet.ClassName; +import pl.com.labaj.autorecord.context.Logger; + +import java.util.Collection; +import java.util.EnumMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Stream; + +import static java.util.Comparator.comparing; +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toCollection; +import static java.util.stream.Collectors.toMap; +import static java.util.stream.Collectors.toSet; +import static org.apache.commons.lang3.StringUtils.substringAfterLast; +import static pl.com.labaj.autorecord.extension.arice.InterfaceType.OBJECT; +import static pl.com.labaj.autorecord.extension.arice.InterfaceType.allProcessedTypes; + +final class TypesStructure { + private final EnumMap> classNames; + private final Map additionalMethodNeeded; + + private TypesStructure(EnumMap> classNames, Map additionalMethodNeeded) { + this.classNames = classNames; + this.additionalMethodNeeded = additionalMethodNeeded; + } + + String debugInfo() { + return classNames.entrySet().stream() + .map(item -> { + var types = item.getValue() + .stream() + .map(ClassName::simpleName) + .collect(joining(", ", "(", ")")); + var className = substringAfterLast(item.getKey().className(), "."); + var needsAdditionalMethod = additionalMethodNeeded.get(item.getKey()); + + return className + " : " + needsAdditionalMethod + " -> " + types; + }) + .collect(joining("\n")); + } + + boolean needsAdditionalMethod(InterfaceType iType) { + return additionalMethodNeeded.containsKey(iType) && additionalMethodNeeded.get(iType); + } + + Set getClassNames(InterfaceType iType) { + return classNames.get(iType); + } + + static class Builder { + private final ExtensionContext extContext; + private final Set immutableNames; + + Builder(ExtensionContext extContext, Set immutableNames) { + this.extContext = extContext; + this.immutableNames = immutableNames; + } + + TypesStructure buildStructure(Logger logger) { + var classNames = allProcessedTypes().stream() + .collect(toMap( + identity(), + pType -> collectClassNames(pType, logger), + (s1, s2) -> { + s1.addAll(s2); + return s1; + }, + () -> new EnumMap<>(InterfaceType.class) + )); + + optimizeStructure(OBJECT, classNames); + + var additionalMethodNeeded = new EnumMap(InterfaceType.class); + collectNeedsInfo(null, OBJECT, classNames, additionalMethodNeeded); + + return new TypesStructure(classNames, additionalMethodNeeded); + } + + private Set collectClassNames(InterfaceType iType, Logger logger) { + var classNames = immutableNames + .stream() + .map(immutableName -> extContext.getImmutableType(immutableName, logger)) + .filter(Objects::nonNull) + .filter(type -> extContext.isSubtype(type, iType, logger)) + .map(ClassName::get) + .map(ClassName.class::cast) + .collect(toCollection(() -> new TreeSet<>(comparing(ClassName::canonicalName)))); + + if (iType == OBJECT) { + immutableNames.stream() + .filter(immutableName -> isNull(extContext.getImmutableType(immutableName, logger))) + .map(ClassName::bestGuess) + .forEach(className -> { + logger.debug("Cannot get TypeMirror for " + className + " so added to Object"); + classNames.add(className); + }); + } + + return classNames; + } + + private Set optimizeStructure(InterfaceType iType, EnumMap> classNames) { + var typeClassNames = classNames.get(iType); + + if (iType.directSubTypes().isEmpty()) { + return typeClassNames; + } + + var toRemove = iType.directSubTypes().stream() + .map(subType -> optimizeStructure(subType, classNames)) + .flatMap(Collection::stream) + .collect(toSet()); + + classNames.get(iType).removeAll(toRemove); + + return Stream.concat(toRemove.stream(), typeClassNames.stream()).collect(toSet()); + } + + private boolean collectNeedsInfo(InterfaceType parent, InterfaceType iType, + EnumMap> classNames, + EnumMap additionalMethodNeeded) { + + if (iType.directSubTypes().isEmpty()) { + var result = !classNames.get(iType).isEmpty(); + additionalMethodNeeded.put(iType, result); + return result; + } + + var needs = new AtomicBoolean(); + var subNeededInfo = iType.directSubTypes().stream() + .collect(toMap( + identity(), + subType -> collectNeedsInfo(iType, subType, classNames, additionalMethodNeeded) + )); + + var someSubNeeds = subNeededInfo.values().stream() + .reduce(false, (b1, b2) -> b1 || b2); + needs.set(someSubNeeds); + + if (!needs.get()) { + needs.set(!nonNull(parent)); + } + + additionalMethodNeeded.put(iType, needs.get()); + return needs.get(); + } + } +} diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/package-info.java b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/package-info.java similarity index 70% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/package-info.java rename to arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/package-info.java index ae7273f..23793c2 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/package-info.java +++ b/arice/extension/src/main/java/pl/com/labaj/autorecord/extension/arice/package-info.java @@ -1,5 +1,3 @@ -@API(status = INTERNAL) -package pl.com.labaj.autorecord.processor.context; /*- * Copyright © 2023 Auto Record @@ -16,7 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import org.apiguardian.api.API; - -import static org.apiguardian.api.API.Status.INTERNAL; \ No newline at end of file +/** + * Provides classes used by ARICE (Auto Record Immutable Collections Extension). + * + *

+ * {@link pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension} is the main class. + */ +package pl.com.labaj.autorecord.extension.arice; \ No newline at end of file diff --git a/arice/extension/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/arice/extension/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000..ca7ea81 --- /dev/null +++ b/arice/extension/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +pl.com.labaj.autorecord.extension.arice.ARICEUtilitiesProcessor \ No newline at end of file diff --git a/arice/project/pom.xml b/arice/project/pom.xml new file mode 100644 index 0000000..2a5d14e --- /dev/null +++ b/arice/project/pom.xml @@ -0,0 +1,79 @@ + + + 4.0.0 + + pl.com.labaj.autorecord + auto-record-project + 2.1.1-SNAPSHOT + ../../pom.xml + + + arice-project + 1.0.0-SNAPSHOT + pom + + + ${project.basedir}/../../.build/lic-header.txt + + + + ../api + ../utils + ../extension + ../tests + + + + + + pl.com.labaj.autorecord + auto-record + 2.1.1-SNAPSHOT + + + pl.com.labaj.autorecord + arice-api + 1.0.0-SNAPSHOT + + + pl.com.labaj.autorecord + arice-utils + 1.0.0-SNAPSHOT + + + pl.com.labaj.autorecord + arice-extension + 1.0.0-SNAPSHOT + + + + + + + verify + + + ${project.basedir}/../../target/site/jacoco-aggregate/jacoco.xml + + + + release + + + + + org.honton.chas + git-tag-maven-plugin + + false + v-arice${project.version} + true + + + + + + + \ No newline at end of file diff --git a/arice/tests/pom.xml b/arice/tests/pom.xml new file mode 100644 index 0000000..34e6f00 --- /dev/null +++ b/arice/tests/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + pl.com.labaj.autorecord + arice-project + 1.0.0-SNAPSHOT + ../project/pom.xml + + + arice-tests + + + true + + + + + pl.com.labaj.autorecord + auto-record + + + pl.com.labaj.autorecord + arice-extension + + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + test + + + com.google.testing.compile + compile-testing + test + + + + + + verify + + + ${project.basedir}/../../target/site/jacoco-aggregate/jacoco.xml + + + + release + + + true + none + + + + \ No newline at end of file diff --git a/arice/tests/src/test/java/pl/com/labaj/autorecord/extension/arice/ImmutabilityTest.java b/arice/tests/src/test/java/pl/com/labaj/autorecord/extension/arice/ImmutabilityTest.java new file mode 100644 index 0000000..f73bad9 --- /dev/null +++ b/arice/tests/src/test/java/pl/com/labaj/autorecord/extension/arice/ImmutabilityTest.java @@ -0,0 +1,136 @@ +package pl.com.labaj.autorecord.extension.arice; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import pl.com.labaj.autorecord.AutoRecord; + +import java.util.AbstractCollection; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NavigableSet; +import java.util.Queue; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static pl.com.labaj.autorecord.extension.arice.ImmutabilityTest.TestArgument.data; + +class ImmutabilityTest { + @AutoRecord + @AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension") + static + interface Simple { + Object value(); + @Mutable Object mutableValue(); + } + + + @SuppressWarnings("unchecked") + static Stream testData() { + var aValue = "A"; + return Stream.of( + data(Collection.class, TestCollection::new, collection -> collection.add(aValue)), + data(Set.class, HashSet::new, set -> set.add(aValue)), + data(SortedSet.class, TreeSet::new, set -> set.add(aValue)), + data(NavigableSet.class, TreeSet::new, set -> set.add(aValue)), + data(List.class, ArrayList::new, list -> list.add(aValue)), + data(Queue.class, ArrayDeque::new, queue -> queue.add(aValue)), + data(Deque.class, ArrayDeque::new, queue -> queue.add(aValue)), + data(Map.class, HashMap::new, map -> map.put(aValue, aValue)) + ); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + @ParameterizedTest(name = "{0}") + @MethodSource("testData") + void shouldWrapCollectionWithImmutable(Class aClass, Supplier mutableValueSupplier, Consumer methodToCall) { + //given + var mutableValue = mutableValueSupplier.get(); + + //when + var simpleRecord = new ImmutabilityTest_SimpleRecord(mutableValue, mutableValue); + var immutableValue = simpleRecord.value(); + var mutableValueFromRecord = simpleRecord.mutableValue(); + + System.out.println(mutableValue.getClass() + " -> " + immutableValue.getClass()); + + //then + assertAll( + () -> assertThat(immutableValue).isInstanceOf(aClass), + () -> assertDoesNotThrow(() -> methodToCall.accept(mutableValue)), + () -> assertThrows(UnsupportedOperationException.class, () -> methodToCall.accept(immutableValue)), + () -> assertThat(mutableValueFromRecord).isSameInstanceAs(mutableValue), + () -> assertDoesNotThrow(() -> methodToCall.accept(mutableValueFromRecord)) + ); + } + record TestArgument(Class aClass, Supplier mutableValueSupplier, Consumer methodToCall) implements Arguments { + + + static TestArgument data(Class aClass, Supplier mutableValueSupplier, Consumer methodToCall) { + return new TestArgument<>(aClass, mutableValueSupplier, methodToCall); + } + @Override + public Object[] get() { + return new Object[] {aClass, mutableValueSupplier, methodToCall}; + } + + } + private static class TestCollection extends AbstractCollection implements Collection { + + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public E next() { + return null; + } + }; + } + + @Override + public int size() { + return 0; + } + @Override + public boolean add(E e) { + return false; + } + + } +} diff --git a/arice/tests/src/test/java/pl/com/labaj/autorecord/extension/arice/ImmutableCollectionsExtensionTest.java b/arice/tests/src/test/java/pl/com/labaj/autorecord/extension/arice/ImmutableCollectionsExtensionTest.java new file mode 100644 index 0000000..0ff4669 --- /dev/null +++ b/arice/tests/src/test/java/pl/com/labaj/autorecord/extension/arice/ImmutableCollectionsExtensionTest.java @@ -0,0 +1,167 @@ +package pl.com.labaj.autorecord.extension.arice; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.google.common.collect.ImmutableSet; +import com.google.testing.compile.Compiler; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import pl.com.labaj.autorecord.AutoRecord; +import pl.com.labaj.autorecord.processor.AutoRecordProcessor; + +import javax.annotation.Nullable; +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +import static com.google.testing.compile.CompilationSubject.assertThat; +import static com.google.testing.compile.Compiler.javac; +import static com.google.testing.compile.JavaFileObjects.forResource; +import static java.util.Objects.isNull; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toCollection; +import static java.util.stream.Collectors.toMap; +import static org.apache.commons.lang3.StringUtils.removeStart; +import static org.apache.commons.lang3.StringUtils.substringBefore; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.fail; + +class ImmutableCollectionsExtensionTest { + private static final String[] NAMES = { + "ItemWithObject", + "ItemWithLists", + "ItemWithListsCustomTypes", + "ItemWithMutableLists", + "ItemWithSets", + "ItemWithSetsCustomTypes", + "ItemWithMutableSets", + "ItemWithMaps", + "ItemWithMapsCustomTypes", + "ItemWithQueues", + "ItemWithQueuesCustomTypes", + "ItemWithMutableQueues", + "ItemWithGuavaCollections" + }; + private static final String GENERATED_PATH = "pl/com/labaj/autorecord/testcase/"; + + static Stream names() { + return Arrays.stream(NAMES); + } + + private Compiler compiler; + + @BeforeEach + void setUp() { + compiler = javac() + .withOptions("-proc:only", "-Xprefer:source"); + } + + @ParameterizedTest(name = "{0}.java") + @MethodSource("names") + void shouldGenerateSingleRecord(String name) { + //given + var inputs = List.of( + forResource("in/" + name + ".java"), + forResource("in/UserCollections.java") + ); + + var expectedOutput = forResource("out/" + name + "Record.java"); + + //when + var compilation = compiler + .withProcessors(new AutoRecordProcessor(), new ARICEUtilitiesProcessor()) + .compile(inputs); + + //then + assertAll( + () -> assertThat(compilation).generatedSourceFile(GENERATED_PATH + name + "Record") + .hasSourceEquivalentTo(expectedOutput), + () -> assertThat(compilation).succeeded() + ); + } + + @Test + void shouldGenerateCorrectAllRecordsTogether() { + //given + var inputs = Arrays.stream(NAMES) + .map(name -> forResource("in/" + name + ".java")) + .collect(toCollection(ArrayList::new)); + + inputs.add(forResource("in/UserCollections.java")); + + var expectedOutputs = Arrays.stream(NAMES) + .collect(toMap( + identity(), + name -> forResource("out/" + name + "Record.java") + )); + var expectedArice = forResource("out/ARICE.java"); + + //when + var compilation = compiler + .withProcessors(new AutoRecordProcessor(), new ARICEUtilitiesProcessor()) + .compile(inputs); + + //then + var assertions = Arrays.stream(NAMES) + .map(name -> (Executable) () -> assertThat(compilation).generatedSourceFile(GENERATED_PATH + name + "Record") + .hasSourceEquivalentTo(expectedOutputs.get(name))) + .collect(toCollection(ArrayList::new)); + + assertions.add(() -> assertThat(compilation).succeeded()); + assertions.add(() -> assertThat(compilation).generatedSourceFile("pl/com/labaj/autorecord/extension/arice/ARICE") + .hasSourceEquivalentTo(expectedArice)); + + assertAll(assertions); + } + + @SuppressWarnings("DataFlowIssue") + private static List prepareClasspath() { + var autoRecordJar = findClasspathFile(AutoRecord.class); + var guavaJar = findClasspathFile(ImmutableSet.class); + var nullableJar = findClasspathFile(Nullable.class); + var targetClassesFolder = findClasspathFile(ImmutableCollectionsExtension.class); + + return List.of( + autoRecordJar, + guavaJar, + nullableJar, + targetClassesFolder + ); + } + + private static File findClasspathFile(Class aClass) { + var url = aClass.getResource(aClass.getSimpleName() + ".class"); + var fileInFolderWithoutExtension = aClass.getName().replace('.', '/'); + + if (isNull(url)) { + fail("Cannot get URL for " + aClass.getName()); + return null; + } + var path = url.getPath(); + path = removeStart(path, "jar:"); + path = removeStart(path, "file:"); + path = substringBefore(path, fileInFolderWithoutExtension); + path = substringBefore(path, "!"); + + return new File(path); + } +} diff --git a/arice/tests/src/test/resources/in/ItemWithGuavaCollections.java b/arice/tests/src/test/resources/in/ItemWithGuavaCollections.java new file mode 100644 index 0000000..c3511c6 --- /dev/null +++ b/arice/tests/src/test/resources/in/ItemWithGuavaCollections.java @@ -0,0 +1,50 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multiset; +import com.google.common.collect.RangeMap; +import com.google.common.collect.RangeSet; +import com.google.common.collect.SetMultimap; +import com.google.common.collect.SortedMultiset; +import com.google.common.collect.SortedSetMultimap; +import com.google.common.collect.Table; +import pl.com.labaj.autorecord.AutoRecord; + +@AutoRecord +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension") +interface ItemWithGuavaCollections> { + Multiset multiset(); + + SortedMultiset sortedMultiset(); + + Multimap multimap(); + + SetMultimap setMultiMap(); + + SortedSetMultimap sortedSetMultimap(); + + ListMultimap listMultiMap(); + + RangeSet rangeSet(); + + RangeMap rangeMap(); + + Table table(); +} diff --git a/arice/tests/src/test/resources/in/ItemWithLists.java b/arice/tests/src/test/resources/in/ItemWithLists.java new file mode 100644 index 0000000..052a4b4 --- /dev/null +++ b/arice/tests/src/test/resources/in/ItemWithLists.java @@ -0,0 +1,43 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.google.common.collect.ImmutableList; +import pl.com.labaj.autorecord.AutoRecord; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +@AutoRecord +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension") +interface ItemWithLists { + List list(); + LinkedList linkedList(); + ArrayList arrayList(); + UserCollections.UserList userList(); + UserCollections.UserListImpl userListImpl(); + ImmutableList immutableList(); + @Nullable List nullableList(); + @Nullable LinkedList nullableLinkedList(); + @Nullable ArrayList nullableArrayList(); + @Nullable UserCollections.UserList nullableUserList(); + @Nullable UserCollections.UserListImpl nullableUserListImpl(); + @Nullable ImmutableList nullableImmutableList(); +} diff --git a/arice/tests/src/test/resources/in/ItemWithListsCustomTypes.java b/arice/tests/src/test/resources/in/ItemWithListsCustomTypes.java new file mode 100644 index 0000000..2fad131 --- /dev/null +++ b/arice/tests/src/test/resources/in/ItemWithListsCustomTypes.java @@ -0,0 +1,47 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.google.common.collect.ImmutableList; +import pl.com.labaj.autorecord.AutoRecord; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +@AutoRecord +@AutoRecord.Extension( + extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + parameters = { + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserList" + }) +interface ItemWithListsCustomTypes { + List list(); + LinkedList linkedList(); + ArrayList arrayList(); + UserCollections.UserList userList(); + UserCollections.UserListImpl userListImpl(); + ImmutableList immutableList(); + @Nullable List nullableList(); + @Nullable LinkedList nullableLinkedList(); + @Nullable ArrayList nullableArrayList(); + @Nullable UserCollections.UserList nullableUserList(); + @Nullable UserCollections.UserListImpl nullableUserListImpl(); + @Nullable ImmutableList nullableImmutableList(); +} diff --git a/arice/tests/src/test/resources/in/ItemWithMaps.java b/arice/tests/src/test/resources/in/ItemWithMaps.java new file mode 100644 index 0000000..88faab7 --- /dev/null +++ b/arice/tests/src/test/resources/in/ItemWithMaps.java @@ -0,0 +1,45 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.google.common.collect.ImmutableMap; +import pl.com.labaj.autorecord.AutoRecord; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; +import java.util.NavigableMap; +import java.util.SortedMap; +import java.util.TreeMap; + +@AutoRecord +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension") +interface ItemWithMaps { + Map set(); + HashMap hashMap(); + TreeMap treeMap(); + SortedMap sortedMap(); + NavigableMap navigableMap(); + ImmutableMap immutableMap(); + @Nullable MapnullableSet(); + @Nullable HashMapnullableHashMap(); + @Nullable TreeMapnullableTreeMap(); + @Nullable SortedMapnullableSortedMap(); + @Nullable NavigableMap nullableNavigableMap(); + @Nullable ImmutableMap nullableImmutableMap(); +} diff --git a/arice/tests/src/test/resources/in/ItemWithMapsCustomTypes.java b/arice/tests/src/test/resources/in/ItemWithMapsCustomTypes.java new file mode 100644 index 0000000..2680c00 --- /dev/null +++ b/arice/tests/src/test/resources/in/ItemWithMapsCustomTypes.java @@ -0,0 +1,63 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.google.common.collect.ImmutableMap; +import pl.com.labaj.autorecord.AutoRecord; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; +import java.util.NavigableMap; +import java.util.SortedMap; +import java.util.TreeMap; + +@AutoRecord +@AutoRecord.Extension( + extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + parameters = { + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserMap", + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserSortedMap", + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserNavigableMap" + }) +interface ItemWithMapsCustomTypes { + Map set(); + HashMap hashMap(); + TreeMap treeMap(); + SortedMap sortedMap(); + NavigableMap navigableMap(); + UserCollections.UserMap userMap(); + UserCollections.UserMapImpl userMapImpl(); + UserCollections.UserSortedMap userSortedMap(); + UserCollections.UserSortedMapImpl userSortedMapImpl(); + UserCollections.UserNavigableMap userNavigableMap(); + UserCollections.UserNavigableMapImpl userNavigableMapImpl(); + ImmutableMap immutableMap(); + @Nullable MapnullableSet(); + @Nullable HashMapnullableHashMap(); + @Nullable TreeMapnullableTreeMap(); + @Nullable SortedMapnullableSortedMap(); + @Nullable NavigableMap nullableNavigableMap(); + @Nullable UserCollections.UserMap nullableUserMap(); + @Nullable UserCollections.UserMapImpl nullableUserMapImpl(); + @Nullable UserCollections.UserSortedMap nullableUserSortedMap(); + @Nullable UserCollections.UserSortedMapImpl nullableUserSortedMapImpl(); + @Nullable UserCollections.UserNavigableMap nullableUserNavigableMap(); + @Nullable UserCollections.UserNavigableMapImpl nullableUserNavigableMapImpl(); + @Nullable ImmutableMap nullableImmutableMap(); +} diff --git a/arice/tests/src/test/resources/in/ItemWithMutableLists.java b/arice/tests/src/test/resources/in/ItemWithMutableLists.java new file mode 100644 index 0000000..03f1ba3 --- /dev/null +++ b/arice/tests/src/test/resources/in/ItemWithMutableLists.java @@ -0,0 +1,44 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.google.common.collect.ImmutableList; +import pl.com.labaj.autorecord.AutoRecord; +import pl.com.labaj.autorecord.extension.arice.Mutable; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +@AutoRecord +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension") +interface ItemWithMutableLists { + List list(); + LinkedList linkedList(); + ArrayList arrayList(); + UserCollections.UserList userList(); + UserCollections.UserListImpl userListImpl(); + ImmutableList immutableList(); + @Mutable + List mutableList(); + @Mutable LinkedList mutableLinkedList(); + @Mutable ArrayList mutableArrayList(); + @Mutable UserCollections.UserList mutableUserList(); + @Mutable UserCollections.UserListImpl mutableUserListImpl(); + @Mutable ImmutableList mutableImmutableList(); +} diff --git a/arice/tests/src/test/resources/in/ItemWithMutableQueues.java b/arice/tests/src/test/resources/in/ItemWithMutableQueues.java new file mode 100644 index 0000000..6a63a6d --- /dev/null +++ b/arice/tests/src/test/resources/in/ItemWithMutableQueues.java @@ -0,0 +1,39 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import pl.com.labaj.autorecord.AutoRecord; +import pl.com.labaj.autorecord.collections.ImmutableDeque; +import pl.com.labaj.autorecord.extension.arice.Mutable; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Queue; + +@AutoRecord +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension") +interface ItemWithMutableQueues { + Queue queue(); + Deque deque(); + ArrayDeque arrayDeque(); + ImmutableDeque immutableDeque(); + @Mutable Queue mutableQueue(); + @Mutable Deque mutableDeque(); + @Mutable + ArrayDeque mutableArrayDeque(); + @Mutable ImmutableDeque mutableImmutableDeque(); +} diff --git a/arice/tests/src/test/resources/in/ItemWithMutableSets.java b/arice/tests/src/test/resources/in/ItemWithMutableSets.java new file mode 100644 index 0000000..3f13c1a --- /dev/null +++ b/arice/tests/src/test/resources/in/ItemWithMutableSets.java @@ -0,0 +1,63 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.google.common.collect.ImmutableSet; +import pl.com.labaj.autorecord.AutoRecord; +import pl.com.labaj.autorecord.extension.arice.Mutable; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.NavigableSet; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +@AutoRecord +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension") +interface ItemWithMutableSets> { + Set set(); + HashSet hashSet(); + LinkedHashSet linkedHashSet(); + SortedSet sortedSet(); + UserCollections.SortedSetImpl sortedSetImpl(); + NavigableSet navigableSet(); + TreeSet treeSet(); + UserCollections.UserSet userSet(); + UserCollections.UserSetImpl userSetImpl(); + UserCollections.UserSortedSet userSortedSet(); + UserCollections.UserSortedSetImpl userSortedSetImpl(); + UserCollections.UserNavigableSet userNavigableSet(); + UserCollections.UserNavigableSetImpl userNavigableSetImpl(); + ImmutableSet immutableSet(); + @Mutable Set mutableSet(); + @Mutable HashSet mutableHashSet(); + @Mutable LinkedHashSet mutableLinkedHashSet(); + @Mutable SortedSet mutableSortedSet(); + @Mutable UserCollections.SortedSetImpl mutableSortedSetImpl(); + @Mutable NavigableSet mutableNavigableSet(); + @Mutable TreeSet mutableTreeSet(); + @Mutable UserCollections.UserSet mutableUserSet(); + @Mutable UserCollections.UserSetImpl mutableUserSetImpl(); + @Mutable UserCollections.UserSortedSet mutableUserSortedSet(); + @Mutable UserCollections.UserSortedSetImpl mutableUserSortedSetImpl(); + @Mutable UserCollections.UserNavigableSet mutableUserNavigableSet(); + @Mutable UserCollections.UserNavigableSetImpl mutableUserNavigableSetImpl(); + @Mutable + ImmutableSet mutableImmutableSet(); +} diff --git a/arice/tests/src/test/resources/in/ItemWithObject.java b/arice/tests/src/test/resources/in/ItemWithObject.java new file mode 100644 index 0000000..9e46a88 --- /dev/null +++ b/arice/tests/src/test/resources/in/ItemWithObject.java @@ -0,0 +1,28 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import pl.com.labaj.autorecord.AutoRecord; + +import javax.annotation.Nullable; + +@AutoRecord +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension") +interface ItemWithObject { + Object object(); + @Nullable Object nullableObject(); +} \ No newline at end of file diff --git a/arice/tests/src/test/resources/in/ItemWithQueues.java b/arice/tests/src/test/resources/in/ItemWithQueues.java new file mode 100644 index 0000000..b359762 --- /dev/null +++ b/arice/tests/src/test/resources/in/ItemWithQueues.java @@ -0,0 +1,38 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import pl.com.labaj.autorecord.AutoRecord; +import pl.com.labaj.autorecord.collections.ImmutableDeque; + +import javax.annotation.Nullable; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Queue; + +@AutoRecord +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension") +interface ItemWithQueues { + Queue queue(); + Deque deque(); + ArrayDeque arrayDeque(); + ImmutableDeque immutableDeque(); + @Nullable QueuenullableQueue(); + @Nullable Deque nullableDeque(); + @Nullable ArrayDeque nullableArrayDeque(); + @Nullable ImmutableDeque nullableImmutableDeque(); +} diff --git a/arice/tests/src/test/resources/in/ItemWithQueuesCustomTypes.java b/arice/tests/src/test/resources/in/ItemWithQueuesCustomTypes.java new file mode 100644 index 0000000..d8f0d88 --- /dev/null +++ b/arice/tests/src/test/resources/in/ItemWithQueuesCustomTypes.java @@ -0,0 +1,53 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import pl.com.labaj.autorecord.AutoRecord; +import pl.com.labaj.autorecord.collections.ImmutableDeque; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +import javax.annotation.Nullable; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Queue; + +@AutoRecord +@AutoRecord.Extension( + extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + parameters = { + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserQueue", + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserDeque" + } +) +interface ItemWithQueuesCustomTypes { + Queue queue(); + Deque deque(); + ArrayDeque arrayDeque(); + UserCollections.UserQueue userQueue(); + UserCollections.UserDeque userDeque(); + UserCollections.UserQueueImpl userQueueImpl(); + UserCollections.UserDequeImpl userDequeImpl(); + ImmutableDeque immutableDeque(); + @Nullable QueuenullableQueue(); + @Nullable Deque nullableDeque(); + @Nullable ArrayDeque nullableArrayDeque(); + @Nullable UserCollections.UserQueue nullableUserQueue(); + @Nullable UserCollections.UserDeque nullableUserDeque(); + @Nullable UserCollections.UserQueueImpl nullableUserQueueImpl(); + @Nullable UserCollections.UserDequeImpl nullableUserDequeImpl(); + @Nullable ImmutableDeque nullableImmutableDeque(); +} diff --git a/arice/tests/src/test/resources/in/ItemWithSets.java b/arice/tests/src/test/resources/in/ItemWithSets.java new file mode 100644 index 0000000..b121cab --- /dev/null +++ b/arice/tests/src/test/resources/in/ItemWithSets.java @@ -0,0 +1,62 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.google.common.collect.ImmutableSet; +import pl.com.labaj.autorecord.AutoRecord; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +import javax.annotation.Nullable; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.NavigableSet; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +@AutoRecord +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension") +interface ItemWithSets> { + Set set(); + HashSet hashSet(); + LinkedHashSet linkedHashSet(); + SortedSet sortedSet(); + UserCollections.SortedSetImpl sortedSetImpl(); + NavigableSet navigableSet(); + TreeSet treeSet(); + UserCollections.UserSet userSet(); + UserCollections.UserSetImpl userSetImpl(); + UserCollections.UserSortedSet userSortedSet(); + UserCollections.UserSortedSetImpl userSortedSetImpl(); + UserCollections.UserNavigableSet userNavigableSet(); + UserCollections.UserNavigableSetImpl userNavigableSetImpl(); + ImmutableSet immutableSet(); + @Nullable Set nullableSet(); + @Nullable HashSet nullableHashSet(); + @Nullable LinkedHashSet nullableLinkedHashSet(); + @Nullable SortedSet nullableSortedSet(); + @Nullable UserCollections.SortedSetImpl nullableSortedSetImpl(); + @Nullable NavigableSet nullableNavigableSet(); + @Nullable TreeSet nullableTreeSet(); + @Nullable UserCollections.UserSet nullableUserSet(); + @Nullable UserCollections.UserSetImpl nullableUserSetImpl(); + @Nullable UserCollections.UserSortedSet nullableUserSortedSet(); + @Nullable UserCollections.UserSortedSetImpl nullableUserSortedSetImpl(); + @Nullable UserCollections.UserNavigableSet nullableUserNavigableSet(); + @Nullable UserCollections.UserNavigableSetImpl nullableUserNavigableSetImpl(); + @Nullable ImmutableSet nullableImmutableSet(); +} diff --git a/arice/tests/src/test/resources/in/ItemWithSetsCustomTypes.java b/arice/tests/src/test/resources/in/ItemWithSetsCustomTypes.java new file mode 100644 index 0000000..92a8202 --- /dev/null +++ b/arice/tests/src/test/resources/in/ItemWithSetsCustomTypes.java @@ -0,0 +1,69 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.google.common.collect.ImmutableSet; +import pl.com.labaj.autorecord.AutoRecord; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +import javax.annotation.Nullable; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.NavigableSet; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +@AutoRecord +@AutoRecord.Extension( + extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + parameters = { + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserSet", + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserSortedSet", + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserNavigableSet" + }) +interface ItemWithSetsCustomTypes> { + Set set(); + HashSet hashSet(); + LinkedHashSet linkedHashSet(); + SortedSet sortedSet(); + UserCollections.SortedSetImpl sortedSetImpl(); + NavigableSet navigableSet(); + TreeSet treeSet(); + UserCollections.UserSet userSet(); + UserCollections.UserSetImpl userSetImpl(); + UserCollections.UserSortedSet userSortedSet(); + UserCollections.UserSortedSetImpl userSortedSetImpl(); + UserCollections.UserNavigableSet userNavigableSet(); + UserCollections.UserNavigableSetImpl userNavigableSetImpl(); + ImmutableSet immutableSet(); + @Nullable Set nullableSet(); + @Nullable HashSet nullableHashSet(); + @Nullable LinkedHashSet nullableLinkedHashSet(); + @Nullable SortedSet nullableSortedSet(); + @Nullable UserCollections.SortedSetImpl nullableSortedSetImpl(); + @Nullable NavigableSet nullableNavigableSet(); + @Nullable TreeSet nullableTreeSet(); + @Nullable UserCollections.UserSet nullableUserSet(); + @Nullable UserCollections.UserSetImpl nullableUserSetImpl(); + @Nullable UserCollections.UserSortedSet nullableUserSortedSet(); + @Nullable UserCollections.UserSortedSetImpl nullableUserSortedSetImpl(); + @Nullable UserCollections.UserNavigableSet nullableUserNavigableSet(); + @Nullable + UserCollections.UserNavigableSetImpl nullableUserNavigableSetImpl(); + @Nullable ImmutableSet nullableImmutableSet(); +} diff --git a/arice/tests/src/test/resources/in/UserCollections.java b/arice/tests/src/test/resources/in/UserCollections.java new file mode 100644 index 0000000..e015d90 --- /dev/null +++ b/arice/tests/src/test/resources/in/UserCollections.java @@ -0,0 +1,110 @@ +package pl.com.labaj.autorecord.testcase.user; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import java.util.AbstractList; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.ArrayDeque; +import java.util.Comparator; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.Queue; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeSet; + +public class UserCollections { + + public interface UserSet extends Set {} + + public interface UserNavigableSet extends NavigableSet {} + + public interface UserSortedSet extends SortedSet {} + public interface UserList extends List {} + public interface UserMap extends Map {} + public interface UserSortedMap extends SortedMap {} + public interface UserNavigableMap extends NavigableMap {} + public interface UserQueue extends Queue {} + public interface UserDeque extends Deque {} + + public static class UserSetImpl extends AbstractSet implements UserSet { + @Override public Iterator iterator() {return null;} + @Override public int size() {return 0;} + } + public static class SortedSetImpl extends AbstractSet implements SortedSet { + @Override public Iterator iterator() {return null;} + @Override public int size() {return 0;} + @Override public Comparator comparator() {return null;} + @Override public SortedSet subSet(E fromElement, E toElement) {return null;} + @Override public SortedSet headSet(E toElement) {return null;} + @Override public SortedSet tailSet(E fromElement) {return null;} + @Override public E first() {return null;} + + @Override public E last() {return null;} + + } + public static class UserSortedSetImpl extends SortedSetImpl implements UserSortedSet {} + public static class UserNavigableSetImpl extends TreeSet implements UserNavigableSet {} + public static class UserListImpl extends AbstractList implements UserList{ + @Override public E get(int index) {return null;} + @Override public int size() {return 0;} + + } + public static class UserMapImpl extends AbstractMap implements UserMap { + @Override public Set> entrySet() {return null;} + + } + public static class UserSortedMapImpl extends UserMapImpl implements UserSortedMap { + @Override public Comparator comparator() {return null;} + @Override public SortedMap subMap(K fromKey, K toKey) {return null;} + @Override public SortedMap headMap(K toKey) {return null;} + @Override public SortedMap tailMap(K fromKey) {return null;} + @Override public K firstKey() {return null;} + @Override public K lastKey() {return null;} + + } + public static class UserNavigableMapImpl extends UserSortedMapImpl implements UserNavigableMap { + @Override public Entry lowerEntry(K key) {return null;} + @Override public K lowerKey(K key) {return null;} + @Override public Entry floorEntry(K key) {return null;} + @Override public K floorKey(K key) {return null;} + @Override public Entry ceilingEntry(K key) {return null;} + @Override public K ceilingKey(K key) {return null;} + @Override public Entry higherEntry(K key) {return null;} + @Override public K higherKey(K key) {return null;} + @Override public Entry firstEntry() {return null;} + @Override public Entry lastEntry() {return null;} + @Override public Entry pollFirstEntry() {return null;} + @Override public Entry pollLastEntry() {return null;} + @Override public NavigableMap descendingMap() {return null;} + @Override public NavigableSet navigableKeySet() {return null;} + @Override public NavigableSet descendingKeySet() {return null;} + @Override public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {return null;} + @Override public NavigableMap headMap(K toKey, boolean inclusive) {return null;} + @Override public NavigableMap tailMap(K fromKey, boolean inclusive) {return null;} + } + + public static class UserQueueImpl extends ArrayDeque implements UserQueue {} + + public static class UserDequeImpl extends ArrayDeque implements UserDeque {} +} diff --git a/arice/tests/src/test/resources/out/ARICE.java b/arice/tests/src/test/resources/out/ARICE.java new file mode 100644 index 0000000..09635a5 --- /dev/null +++ b/arice/tests/src/test/resources/out/ARICE.java @@ -0,0 +1,247 @@ +package pl.com.labaj.autorecord.extension.arice; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableMultiset; +import com.google.common.collect.ImmutableRangeMap; +import com.google.common.collect.ImmutableRangeSet; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.ImmutableSortedMultiset; +import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.ImmutableTable; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multiset; +import com.google.common.collect.RangeMap; +import com.google.common.collect.RangeSet; +import com.google.common.collect.SetMultimap; +import com.google.common.collect.SortedMultiset; +import com.google.common.collect.Table; +import java.lang.Object; +import java.lang.SuppressWarnings; +import java.util.Collection; +import java.util.Deque; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.Queue; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; +import pl.com.labaj.autorecord.collections.ImmutableCollection; +import pl.com.labaj.autorecord.collections.ImmutableDeque; + +/** + * Class providing methods to copy collections to their corresponding immutable versions + */ +@Generated( + value = { + "pl.com.labaj.autorecord.processor.AutoRecordProcessor", + "pl.com.labaj.autorecord.extension.arice.ARICEUtilitiesProcessor" + }, + comments = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension" +) +@GeneratedWithAutoRecord +public final class ARICE { + private ARICE() { + } + + @SuppressWarnings("unchecked") + public static Object immutable(Object object) { + if (object instanceof Collection collection) { + return immutable(collection); + } + if (object instanceof Map map) { + return immutable(map); + } + if (object instanceof Multimap multimap) { + return immutable(multimap); + } + if (object instanceof RangeSet rangeSet) { + return immutable(rangeSet); + } + if (object instanceof RangeMap rangeMap) { + return immutable(rangeMap); + } + if (object instanceof Table table) { + return immutable(table); + } + return object; + } + + public static Collection immutable(Collection collection) { + if (collection instanceof ImmutableCollection) { + return collection; + } + if (collection instanceof Set set) { + return immutable(set); + } + if (collection instanceof List list) { + return immutable(list); + } + if (collection instanceof Queue queue) { + return immutable(queue); + } + if (collection instanceof Multiset multiset) { + return immutable(multiset); + } + return ImmutableCollection.copyOfCollection(collection); + } + + public static Set immutable(Set set) { + if (set instanceof ImmutableSet) { + return set; + } + if (set instanceof SortedSet sortedSet) { + return immutable(sortedSet); + } + return ImmutableSet.copyOf(set); + } + + public static SortedSet immutable(SortedSet sortedSet) { + if (sortedSet instanceof NavigableSet navigableSet) { + return immutable(navigableSet); + } + return ImmutableSortedSet.copyOfSorted(sortedSet); + } + + public static NavigableSet immutable(NavigableSet navigableSet) { + if (navigableSet instanceof ImmutableSortedSet) { + return navigableSet; + } + return ImmutableSortedSet.copyOfSorted(navigableSet); + } + + public static List immutable(List list) { + if (list instanceof ImmutableList) { + return list; + } + return ImmutableList.copyOf(list); + } + + public static Queue immutable(Queue queue) { + if (queue instanceof Deque deque) { + return immutable(deque); + } + return ImmutableDeque.copyOfQueue(queue); + } + + public static Deque immutable(Deque deque) { + if (deque instanceof ImmutableDeque) { + return deque; + } + return ImmutableDeque.copyOfQueue(deque); + } + + public static Map immutable(Map map) { + if (map instanceof ImmutableMap) { + return map; + } + if (map instanceof SortedMap sortedMap) { + return immutable(sortedMap); + } + return ImmutableMap.copyOf(map); + } + + public static SortedMap immutable(SortedMap sortedMap) { + if (sortedMap instanceof NavigableMap navigableMap) { + return immutable(navigableMap); + } + return ImmutableSortedMap.copyOfSorted(sortedMap); + } + + public static NavigableMap immutable(NavigableMap navigableMap) { + if (navigableMap instanceof ImmutableSortedMap) { + return navigableMap; + } + return ImmutableSortedMap.copyOfSorted(navigableMap); + } + + public static Multiset immutable(Multiset multiset) { + if (multiset instanceof ImmutableMultiset) { + return multiset; + } + if (multiset instanceof SortedMultiset sortedMultiset) { + return immutable(sortedMultiset); + } + return ImmutableMultiset.copyOf(multiset); + } + + public static SortedMultiset immutable(SortedMultiset sortedMultiset) { + if (sortedMultiset instanceof ImmutableSortedMultiset) { + return sortedMultiset; + } + return ImmutableSortedMultiset.copyOfSorted(sortedMultiset); + } + + public static Multimap immutable(Multimap multimap) { + if (multimap instanceof ImmutableMultimap) { + return multimap; + } + if (multimap instanceof SetMultimap setMultimap) { + return immutable(setMultimap); + } + if (multimap instanceof ListMultimap listMultimap) { + return immutable(listMultimap); + } + return ImmutableMultimap.copyOf(multimap); + } + + public static SetMultimap immutable(SetMultimap setMultimap) { + if (setMultimap instanceof ImmutableSetMultimap) { + return setMultimap; + } + return ImmutableSetMultimap.copyOf(setMultimap); + } + + public static ListMultimap immutable(ListMultimap listMultimap) { + if (listMultimap instanceof ImmutableListMultimap) { + return listMultimap; + } + return ImmutableListMultimap.copyOf(listMultimap); + } + + public static > RangeSet immutable(RangeSet rangeSet) { + if (rangeSet instanceof ImmutableRangeSet) { + return rangeSet; + } + return ImmutableRangeSet.copyOf(rangeSet); + } + + public static , V> RangeMap immutable(RangeMap rangeMap) { + if (rangeMap instanceof ImmutableRangeMap) { + return rangeMap; + } + return ImmutableRangeMap.copyOf(rangeMap); + } + + public static Table immutable(Table table) { + if (table instanceof ImmutableTable) { + return table; + } + return ImmutableTable.copyOf(table); + } +} \ No newline at end of file diff --git a/arice/tests/src/test/resources/out/ItemWithGuavaCollectionsRecord.java b/arice/tests/src/test/resources/out/ItemWithGuavaCollectionsRecord.java new file mode 100644 index 0000000..a43c821 --- /dev/null +++ b/arice/tests/src/test/resources/out/ItemWithGuavaCollectionsRecord.java @@ -0,0 +1,73 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multiset; +import com.google.common.collect.RangeMap; +import com.google.common.collect.RangeSet; +import com.google.common.collect.SetMultimap; +import com.google.common.collect.SortedMultiset; +import com.google.common.collect.SortedSetMultimap; +import com.google.common.collect.Table; +import java.lang.Comparable; +import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; +import pl.com.labaj.autorecord.extension.arice.ARICE; +import pl.com.labaj.autorecord.extension.arice.ARICEUtilities; + +@Generated( + comments = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + value = "pl.com.labaj.autorecord.AutoRecord" +) +@GeneratedWithAutoRecord +@ARICEUtilities(className = "pl.com.labaj.autorecord.extension.arice.ARICE") +record ItemWithGuavaCollectionsRecord>(Multiset multiset, + SortedMultiset sortedMultiset, + Multimap multimap, + SetMultimap setMultiMap, + SortedSetMultimap sortedSetMultimap, + ListMultimap listMultiMap, + RangeSet rangeSet, + RangeMap rangeMap, + Table table) implements ItemWithGuavaCollections { + ItemWithGuavaCollectionsRecord { + // pl.com.labaj.autorecord.processor.AutoRecordProcessor + requireNonNull(multiset, "multiset must not be null"); + requireNonNull(sortedMultiset, "sortedMultiset must not be null"); + requireNonNull(multimap, "multimap must not be null"); + requireNonNull(setMultiMap, "setMultiMap must not be null"); + requireNonNull(sortedSetMultimap, "sortedSetMultimap must not be null"); + requireNonNull(listMultiMap, "listMultiMap must not be null"); + requireNonNull(rangeSet, "rangeSet must not be null"); + requireNonNull(rangeMap, "rangeMap must not be null"); + requireNonNull(table, "table must not be null"); + + // pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension + multiset = ARICE.immutable(multiset); + sortedMultiset = ARICE.immutable(sortedMultiset); + multimap = ARICE.immutable(multimap); + setMultiMap = ARICE.immutable(setMultiMap); + listMultiMap = ARICE.immutable(listMultiMap); + rangeSet = ARICE.immutable(rangeSet); + rangeMap = ARICE.immutable(rangeMap); + table = ARICE.immutable(table); + } +} \ No newline at end of file diff --git a/arice/tests/src/test/resources/out/ItemWithListsCustomTypesRecord.java b/arice/tests/src/test/resources/out/ItemWithListsCustomTypesRecord.java new file mode 100644 index 0000000..69ffe84 --- /dev/null +++ b/arice/tests/src/test/resources/out/ItemWithListsCustomTypesRecord.java @@ -0,0 +1,63 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ImmutableList; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; +import pl.com.labaj.autorecord.extension.arice.ARICEUtilities; +import pl.com.labaj.autorecord.extension.arice.ARICE_JKSTKLE; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +@Generated( + comments = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + value = "pl.com.labaj.autorecord.AutoRecord" +) +@GeneratedWithAutoRecord +@ARICEUtilities( + immutableTypes = "pl.com.labaj.autorecord.testcase.user.UserCollections.UserList", + className = "pl.com.labaj.autorecord.extension.arice.ARICE_JKSTKLE" +) + record ItemWithListsCustomTypesRecord(List list, LinkedList linkedList, + ArrayList arrayList, UserCollections.UserList userList, + UserCollections.UserListImpl userListImpl, ImmutableList immutableList, + @Nullable List nullableList, @Nullable LinkedList nullableLinkedList, + @Nullable ArrayList nullableArrayList, + @Nullable UserCollections.UserList nullableUserList, + @Nullable UserCollections.UserListImpl nullableUserListImpl, + @Nullable ImmutableList nullableImmutableList) implements ItemWithListsCustomTypes { + ItemWithListsCustomTypesRecord { + // pl.com.labaj.autorecord.processor.AutoRecordProcessor + requireNonNull(list, "list must not be null"); + requireNonNull(linkedList, "linkedList must not be null"); + requireNonNull(arrayList, "arrayList must not be null"); + requireNonNull(userList, "userList must not be null"); + requireNonNull(userListImpl, "userListImpl must not be null"); + requireNonNull(immutableList, "immutableList must not be null"); + + // pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension + list = ARICE_JKSTKLE.immutable(list); + nullableList = isNull(nullableList) ? null : ARICE_JKSTKLE.immutable(nullableList); + } +} \ No newline at end of file diff --git a/arice/tests/src/test/resources/out/ItemWithListsNoUserDefinedCollectionsRecord.java b/arice/tests/src/test/resources/out/ItemWithListsNoUserDefinedCollectionsRecord.java new file mode 100644 index 0000000..1eca9b6 --- /dev/null +++ b/arice/tests/src/test/resources/out/ItemWithListsNoUserDefinedCollectionsRecord.java @@ -0,0 +1,64 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ImmutableList; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; + import pl.com.labaj.autorecord.extension.arice.ARICE; +import pl.com.labaj.autorecord.extension.arice.ARICEUtilities; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +@Generated( + comments = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + value = "pl.com.labaj.autorecord.AutoRecord" +) +@GeneratedWithAutoRecord +@ARICEUtilities(className = "pl.com.labaj.autorecord.extension.arice.ARICE") +record ItemWithListsNoUserDefinedCollectionsRecord(List list, + LinkedList linkedList, + ArrayList arrayList, + UserCollections.UserList userList, + UserCollections.UserListImpl userListImpl, + ImmutableList immutableList, + @Nullable List nullableList, + @Nullable LinkedList nullableLinkedList, + @Nullable ArrayList nullableArrayList, + @Nullable UserCollections.UserList nullableUserList, + @Nullable UserCollections.UserListImpl nullableUserListImpl, + @Nullable ImmutableList nullableImmutableList) implements ItemWithListsNoUserDefinedCollections { + ItemWithListsNoUserDefinedCollectionsRecord { + // pl.com.labaj.autorecord.processor.AutoRecordProcessor + requireNonNull(list, "list must not be null"); + requireNonNull(linkedList, "linkedList must not be null"); + requireNonNull(arrayList, "arrayList must not be null"); + requireNonNull(userList, "userList must not be null"); + requireNonNull(userListImpl, "userListImpl must not be null"); + requireNonNull(immutableList, "immutableList must not be null"); + + // pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension + list = ARICE.immutable(list); + nullableList = isNull(nullableList) ? null : ARICE.immutable(nullableList); + } +} \ No newline at end of file diff --git a/arice/tests/src/test/resources/out/ItemWithListsRecord.java b/arice/tests/src/test/resources/out/ItemWithListsRecord.java new file mode 100644 index 0000000..73ef690 --- /dev/null +++ b/arice/tests/src/test/resources/out/ItemWithListsRecord.java @@ -0,0 +1,64 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ImmutableList; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; +import pl.com.labaj.autorecord.extension.arice.ARICE; +import pl.com.labaj.autorecord.extension.arice.ARICEUtilities; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +@Generated( + comments = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + value = "pl.com.labaj.autorecord.AutoRecord" +) +@GeneratedWithAutoRecord +@ARICEUtilities(className = "pl.com.labaj.autorecord.extension.arice.ARICE") +record ItemWithListsRecord(List list, + LinkedList linkedList, + ArrayList arrayList, + UserCollections.UserList userList, + UserCollections.UserListImpl userListImpl, + ImmutableList immutableList, + @Nullable List nullableList, + @Nullable LinkedList nullableLinkedList, + @Nullable ArrayList nullableArrayList, + @Nullable UserCollections.UserList nullableUserList, + @Nullable UserCollections.UserListImpl nullableUserListImpl, + @Nullable ImmutableList nullableImmutableList) implements ItemWithLists { + ItemWithListsRecord { + // pl.com.labaj.autorecord.processor.AutoRecordProcessor + requireNonNull(list, "list must not be null"); + requireNonNull(linkedList, "linkedList must not be null"); + requireNonNull(arrayList, "arrayList must not be null"); + requireNonNull(userList, "userList must not be null"); + requireNonNull(userListImpl, "userListImpl must not be null"); + requireNonNull(immutableList, "immutableList must not be null"); + + // pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension + list = ARICE.immutable(list); + nullableList = isNull(nullableList) ? null : ARICE.immutable(nullableList); + } +} \ No newline at end of file diff --git a/arice/tests/src/test/resources/out/ItemWithMapsCustomTypesRecord.java b/arice/tests/src/test/resources/out/ItemWithMapsCustomTypesRecord.java new file mode 100644 index 0000000..6df51fa --- /dev/null +++ b/arice/tests/src/test/resources/out/ItemWithMapsCustomTypesRecord.java @@ -0,0 +1,95 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ImmutableMap; +import java.util.HashMap; +import java.util.Map; +import java.util.NavigableMap; +import java.util.SortedMap; +import java.util.TreeMap; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; +import pl.com.labaj.autorecord.extension.arice.ARICEUtilities; +import pl.com.labaj.autorecord.extension.arice.ARICE_KMBEBUW; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +@Generated( + comments = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + value = "pl.com.labaj.autorecord.AutoRecord" +) +@GeneratedWithAutoRecord +@ARICEUtilities( + immutableTypes = { + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserSortedMap", + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserNavigableMap", + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserMap" + }, + className = "pl.com.labaj.autorecord.extension.arice.ARICE_KMBEBUW" +) +record ItemWithMapsCustomTypesRecord(Map set, + HashMap hashMap, + TreeMap treeMap, + SortedMap sortedMap, + NavigableMap navigableMap, + UserCollections.UserMap userMap, + UserCollections.UserMapImpl userMapImpl, + UserCollections.UserSortedMap userSortedMap, + UserCollections.UserSortedMapImpl userSortedMapImpl, + UserCollections.UserNavigableMap userNavigableMap, + UserCollections.UserNavigableMapImpl userNavigableMapImpl, + ImmutableMap immutableMap, + @Nullable Map nullableSet, + @Nullable HashMap nullableHashMap, + @Nullable TreeMap nullableTreeMap, + @Nullable SortedMap nullableSortedMap, + @Nullable NavigableMap nullableNavigableMap, + @Nullable UserCollections.UserMap nullableUserMap, + @Nullable UserCollections.UserMapImpl nullableUserMapImpl, + @Nullable UserCollections.UserSortedMap nullableUserSortedMap, + @Nullable UserCollections.UserSortedMapImpl nullableUserSortedMapImpl, + @Nullable UserCollections.UserNavigableMap nullableUserNavigableMap, + @Nullable UserCollections.UserNavigableMapImpl nullableUserNavigableMapImpl, + @Nullable ImmutableMap nullableImmutableMap) implements ItemWithMapsCustomTypes { + ItemWithMapsCustomTypesRecord { + // pl.com.labaj.autorecord.processor.AutoRecordProcessor + requireNonNull(set, "set must not be null"); + requireNonNull(hashMap, "hashMap must not be null"); + requireNonNull(treeMap, "treeMap must not be null"); + requireNonNull(sortedMap, "sortedMap must not be null"); + requireNonNull(navigableMap, "navigableMap must not be null"); + requireNonNull(userMap, "userMap must not be null"); + requireNonNull(userMapImpl, "userMapImpl must not be null"); + requireNonNull(userSortedMap, "userSortedMap must not be null"); + requireNonNull(userSortedMapImpl, "userSortedMapImpl must not be null"); + requireNonNull(userNavigableMap, "userNavigableMap must not be null"); + requireNonNull(userNavigableMapImpl, "userNavigableMapImpl must not be null"); + requireNonNull(immutableMap, "immutableMap must not be null"); + + // pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension + set = ARICE_KMBEBUW.immutable(set); + sortedMap = ARICE_KMBEBUW.immutable(sortedMap); + navigableMap = ARICE_KMBEBUW.immutable(navigableMap); + nullableSet = isNull(nullableSet) ? null : ARICE_KMBEBUW.immutable(nullableSet); + nullableSortedMap = isNull(nullableSortedMap) ? null : ARICE_KMBEBUW.immutable(nullableSortedMap); + nullableNavigableMap = isNull(nullableNavigableMap) ? null : ARICE_KMBEBUW.immutable(nullableNavigableMap); + } +} \ No newline at end of file diff --git a/arice/tests/src/test/resources/out/ItemWithMapsRecord.java b/arice/tests/src/test/resources/out/ItemWithMapsRecord.java new file mode 100644 index 0000000..4d4d874 --- /dev/null +++ b/arice/tests/src/test/resources/out/ItemWithMapsRecord.java @@ -0,0 +1,69 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ImmutableMap; +import java.util.HashMap; +import java.util.Map; +import java.util.NavigableMap; +import java.util.SortedMap; +import java.util.TreeMap; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; + import pl.com.labaj.autorecord.extension.arice.ARICE; +import pl.com.labaj.autorecord.extension.arice.ARICEUtilities; + +@Generated( + comments = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + value = "pl.com.labaj.autorecord.AutoRecord" +) +@GeneratedWithAutoRecord +@ARICEUtilities(className = "pl.com.labaj.autorecord.extension.arice.ARICE") +record ItemWithMapsRecord(Map set, + HashMap hashMap, + TreeMap treeMap, + SortedMap sortedMap, + NavigableMap navigableMap, + ImmutableMap immutableMap, + @Nullable Map nullableSet, + @Nullable HashMap nullableHashMap, + @Nullable TreeMap nullableTreeMap, + @Nullable SortedMap nullableSortedMap, + @Nullable NavigableMap nullableNavigableMap, + @Nullable ImmutableMap nullableImmutableMap) implements ItemWithMaps { + ItemWithMapsRecord { + // pl.com.labaj.autorecord.processor.AutoRecordProcessor + requireNonNull(set, "set must not be null"); + requireNonNull(hashMap, "hashMap must not be null"); + requireNonNull(treeMap, "treeMap must not be null"); + requireNonNull(sortedMap, "sortedMap must not be null"); + requireNonNull(navigableMap, "navigableMap must not be null"); + requireNonNull(immutableMap, "immutableMap must not be null"); + + // pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension + set = ARICE.immutable(set); + sortedMap = ARICE.immutable(sortedMap); + navigableMap = ARICE.immutable(navigableMap); + nullableSet = isNull(nullableSet) ? null : ARICE.immutable(nullableSet); + nullableSortedMap = isNull(nullableSortedMap) ? null : ARICE.immutable(nullableSortedMap); + nullableNavigableMap = isNull(nullableNavigableMap) ? null : ARICE.immutable(nullableNavigableMap); + } +} \ No newline at end of file diff --git a/arice/tests/src/test/resources/out/ItemWithMutableListsRecord.java b/arice/tests/src/test/resources/out/ItemWithMutableListsRecord.java new file mode 100644 index 0000000..0ff90f6 --- /dev/null +++ b/arice/tests/src/test/resources/out/ItemWithMutableListsRecord.java @@ -0,0 +1,70 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + + import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ImmutableList; + import java.util.ArrayList; + import java.util.LinkedList; + import java.util.List; + import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; +import pl.com.labaj.autorecord.extension.arice.ARICE; +import pl.com.labaj.autorecord.extension.arice.ARICEUtilities; +import pl.com.labaj.autorecord.extension.arice.Mutable; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +@Generated( + comments = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + value = "pl.com.labaj.autorecord.AutoRecord" +) +@GeneratedWithAutoRecord +@ARICEUtilities( + className = "pl.com.labaj.autorecord.extension.arice.ARICE" +) +record ItemWithMutableListsRecord(List list, + LinkedList linkedList, + ArrayList arrayList, + UserCollections.UserList userList, + UserCollections.UserListImpl userListImpl, + ImmutableList immutableList, + @Mutable List mutableList, + @Mutable LinkedList mutableLinkedList, + @Mutable ArrayList mutableArrayList, + @Mutable UserCollections.UserList mutableUserList, + @Mutable UserCollections.UserListImpl mutableUserListImpl, + @Mutable ImmutableList mutableImmutableList) implements ItemWithMutableLists { + ItemWithMutableListsRecord { + // pl.com.labaj.autorecord.processor.AutoRecordProcessor + requireNonNull(list, "list must not be null"); + requireNonNull(linkedList, "linkedList must not be null"); + requireNonNull(arrayList, "arrayList must not be null"); + requireNonNull(userList, "userList must not be null"); + requireNonNull(userListImpl, "userListImpl must not be null"); + requireNonNull(immutableList, "immutableList must not be null"); + requireNonNull(mutableList, "mutableList must not be null"); + requireNonNull(mutableLinkedList, "mutableLinkedList must not be null"); + requireNonNull(mutableArrayList, "mutableArrayList must not be null"); + requireNonNull(mutableUserList, "mutableUserList must not be null"); + requireNonNull(mutableUserListImpl, "mutableUserListImpl must not be null"); + requireNonNull(mutableImmutableList, "mutableImmutableList must not be null"); + + // pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension + list = ARICE.immutable(list); + } +} \ No newline at end of file diff --git a/arice/tests/src/test/resources/out/ItemWithMutableQueuesRecord.java b/arice/tests/src/test/resources/out/ItemWithMutableQueuesRecord.java new file mode 100644 index 0000000..a2e009d --- /dev/null +++ b/arice/tests/src/test/resources/out/ItemWithMutableQueuesRecord.java @@ -0,0 +1,60 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import static java.util.Objects.requireNonNull; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Queue; +import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; +import pl.com.labaj.autorecord.collections.ImmutableDeque; +import pl.com.labaj.autorecord.extension.arice.ARICE; +import pl.com.labaj.autorecord.extension.arice.ARICEUtilities; +import pl.com.labaj.autorecord.extension.arice.Mutable; + +@Generated( + comments = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + value = "pl.com.labaj.autorecord.AutoRecord" +) +@GeneratedWithAutoRecord +@ARICEUtilities(className = "pl.com.labaj.autorecord.extension.arice.ARICE") +record ItemWithMutableQueuesRecord(Queue queue, + Deque deque, + ArrayDeque arrayDeque, + ImmutableDeque immutableDeque, + @Mutable Queue mutableQueue, + @Mutable Deque mutableDeque, + @Mutable ArrayDeque mutableArrayDeque, + @Mutable ImmutableDeque mutableImmutableDeque) implements ItemWithMutableQueues { + ItemWithMutableQueuesRecord { + // pl.com.labaj.autorecord.processor.AutoRecordProcessor + requireNonNull(queue, "queue must not be null"); + requireNonNull(deque, "deque must not be null"); + requireNonNull(arrayDeque, "arrayDeque must not be null"); + requireNonNull(immutableDeque, "immutableDeque must not be null"); + requireNonNull(mutableQueue, "mutableQueue must not be null"); + requireNonNull(mutableDeque, "mutableDeque must not be null"); + requireNonNull(mutableArrayDeque, "mutableArrayDeque must not be null"); + requireNonNull(mutableImmutableDeque, "mutableImmutableDeque must not be null"); + + // pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension + queue = ARICE.immutable(queue); + deque = ARICE.immutable(deque); + } +} \ No newline at end of file diff --git a/arice/tests/src/test/resources/out/ItemWithMutableSetsRecord.java b/arice/tests/src/test/resources/out/ItemWithMutableSetsRecord.java new file mode 100644 index 0000000..f983b53 --- /dev/null +++ b/arice/tests/src/test/resources/out/ItemWithMutableSetsRecord.java @@ -0,0 +1,97 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + + import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ImmutableSet; +import java.lang.Enum; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.NavigableSet; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; +import pl.com.labaj.autorecord.extension.arice.ARICE; +import pl.com.labaj.autorecord.extension.arice.ARICEUtilities; +import pl.com.labaj.autorecord.extension.arice.Mutable; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +@Generated( + comments = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + value = "pl.com.labaj.autorecord.AutoRecord" +) +@GeneratedWithAutoRecord +@ARICEUtilities(className = "pl.com.labaj.autorecord.extension.arice.ARICE") +record ItemWithMutableSetsRecord>(Set set, HashSet hashSet, + LinkedHashSet linkedHashSet, SortedSet sortedSet, + UserCollections.SortedSetImpl sortedSetImpl, NavigableSet navigableSet, + TreeSet treeSet, UserCollections.UserSet userSet, + UserCollections.UserSetImpl userSetImpl, UserCollections.UserSortedSet userSortedSet, + UserCollections.UserSortedSetImpl userSortedSetImpl, + UserCollections.UserNavigableSet userNavigableSet, + UserCollections.UserNavigableSetImpl userNavigableSetImpl, ImmutableSet immutableSet, + @Mutable Set mutableSet, @Mutable HashSet mutableHashSet, + @Mutable LinkedHashSet mutableLinkedHashSet, @Mutable SortedSet mutableSortedSet, + @Mutable UserCollections.SortedSetImpl mutableSortedSetImpl, + @Mutable NavigableSet mutableNavigableSet, @Mutable TreeSet mutableTreeSet, + @Mutable UserCollections.UserSet mutableUserSet, + @Mutable UserCollections.UserSetImpl mutableUserSetImpl, + @Mutable UserCollections.UserSortedSet mutableUserSortedSet, + @Mutable UserCollections.UserSortedSetImpl mutableUserSortedSetImpl, + @Mutable UserCollections.UserNavigableSet mutableUserNavigableSet, + @Mutable UserCollections.UserNavigableSetImpl mutableUserNavigableSetImpl, + @Mutable ImmutableSet mutableImmutableSet) implements ItemWithMutableSets { + ItemWithMutableSetsRecord { + // pl.com.labaj.autorecord.processor.AutoRecordProcessor + requireNonNull(set, "set must not be null"); + requireNonNull(hashSet, "hashSet must not be null"); + requireNonNull(linkedHashSet, "linkedHashSet must not be null"); + requireNonNull(sortedSet, "sortedSet must not be null"); + requireNonNull(sortedSetImpl, "sortedSetImpl must not be null"); + requireNonNull(navigableSet, "navigableSet must not be null"); + requireNonNull(treeSet, "treeSet must not be null"); + requireNonNull(userSet, "userSet must not be null"); + requireNonNull(userSetImpl, "userSetImpl must not be null"); + requireNonNull(userSortedSet, "userSortedSet must not be null"); + requireNonNull(userSortedSetImpl, "userSortedSetImpl must not be null"); + requireNonNull(userNavigableSet, "userNavigableSet must not be null"); + requireNonNull(userNavigableSetImpl, "userNavigableSetImpl must not be null"); + requireNonNull(immutableSet, "immutableSet must not be null"); + requireNonNull(mutableSet, "mutableSet must not be null"); + requireNonNull(mutableHashSet, "mutableHashSet must not be null"); + requireNonNull(mutableLinkedHashSet, "mutableLinkedHashSet must not be null"); + requireNonNull(mutableSortedSet, "mutableSortedSet must not be null"); + requireNonNull(mutableSortedSetImpl, "mutableSortedSetImpl must not be null"); + requireNonNull(mutableNavigableSet, "mutableNavigableSet must not be null"); + requireNonNull(mutableTreeSet, "mutableTreeSet must not be null"); + requireNonNull(mutableUserSet, "mutableUserSet must not be null"); + requireNonNull(mutableUserSetImpl, "mutableUserSetImpl must not be null"); + requireNonNull(mutableUserSortedSet, "mutableUserSortedSet must not be null"); + requireNonNull(mutableUserSortedSetImpl, "mutableUserSortedSetImpl must not be null"); + requireNonNull(mutableUserNavigableSet, "mutableUserNavigableSet must not be null"); + requireNonNull(mutableUserNavigableSetImpl, "mutableUserNavigableSetImpl must not be null"); + requireNonNull(mutableImmutableSet, "mutableImmutableSet must not be null"); + + // pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension + set = ARICE.immutable(set); + sortedSet = ARICE.immutable(sortedSet); + navigableSet = ARICE.immutable(navigableSet); + } +} \ No newline at end of file diff --git a/arice/tests/src/test/resources/out/ItemWithObjectRecord.java b/arice/tests/src/test/resources/out/ItemWithObjectRecord.java new file mode 100644 index 0000000..5b6cd17 --- /dev/null +++ b/arice/tests/src/test/resources/out/ItemWithObjectRecord.java @@ -0,0 +1,44 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; + +import java.lang.Object; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; +import pl.com.labaj.autorecord.extension.arice.ARICE; +import pl.com.labaj.autorecord.extension.arice.ARICEUtilities; + +@Generated( + comments = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + value = "pl.com.labaj.autorecord.AutoRecord" +) +@GeneratedWithAutoRecord +@ARICEUtilities(className = "pl.com.labaj.autorecord.extension.arice.ARICE") +record ItemWithObjectRecord(Object object, @Nullable Object nullableObject) implements ItemWithObject { + ItemWithObjectRecord { + // pl.com.labaj.autorecord.processor.AutoRecordProcessor + requireNonNull(object, "object must not be null"); + + // pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension + object = ARICE.immutable(object); + nullableObject = isNull(nullableObject) ? null : ARICE.immutable(nullableObject); + } +} \ No newline at end of file diff --git a/arice/tests/src/test/resources/out/ItemWithQueuesCustomTypesRecord.java b/arice/tests/src/test/resources/out/ItemWithQueuesCustomTypesRecord.java new file mode 100644 index 0000000..d2aeb4c --- /dev/null +++ b/arice/tests/src/test/resources/out/ItemWithQueuesCustomTypesRecord.java @@ -0,0 +1,78 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + + import static java.util.Objects.isNull; + import static java.util.Objects.requireNonNull; + + import java.util.ArrayDeque; + import java.util.Deque; + import java.util.Queue; + import javax.annotation.Nullable; + import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; +import pl.com.labaj.autorecord.collections.ImmutableDeque; +import pl.com.labaj.autorecord.extension.arice.ARICEUtilities; +import pl.com.labaj.autorecord.extension.arice.ARICE_GWFVCQG; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +@Generated( + comments = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + value = "pl.com.labaj.autorecord.AutoRecord" +) +@GeneratedWithAutoRecord +@ARICEUtilities( + immutableTypes = { + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserQueue", + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserDeque" + }, + className = "pl.com.labaj.autorecord.extension.arice.ARICE_GWFVCQG" +) +record ItemWithQueuesCustomTypesRecord(Queue queue, + Deque deque, + ArrayDeque arrayDeque, + UserCollections.UserQueue userQueue, + UserCollections.UserDeque userDeque, + UserCollections.UserQueueImpl userQueueImpl, + UserCollections.UserDequeImpl userDequeImpl, + ImmutableDeque immutableDeque, + @Nullable Queue nullableQueue, + @Nullable Deque nullableDeque, + @Nullable ArrayDeque nullableArrayDeque, + @Nullable UserCollections.UserQueue nullableUserQueue, + @Nullable UserCollections.UserDeque nullableUserDeque, + @Nullable UserCollections.UserQueueImpl nullableUserQueueImpl, + @Nullable UserCollections.UserDequeImpl nullableUserDequeImpl, + @Nullable ImmutableDeque nullableImmutableDeque) implements ItemWithQueuesCustomTypes { + ItemWithQueuesCustomTypesRecord { + // pl.com.labaj.autorecord.processor.AutoRecordProcessor + requireNonNull(queue, "queue must not be null"); + requireNonNull(deque, "deque must not be null"); + requireNonNull(arrayDeque, "arrayDeque must not be null"); + requireNonNull(userQueue, "userQueue must not be null"); + requireNonNull(userDeque, "userDeque must not be null"); + requireNonNull(userQueueImpl, "userQueueImpl must not be null"); + requireNonNull(userDequeImpl, "userDequeImpl must not be null"); + requireNonNull(immutableDeque, "immutableDeque must not be null"); + + // pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension + queue = ARICE_GWFVCQG.immutable(queue); + deque = ARICE_GWFVCQG.immutable(deque); + nullableQueue = isNull(nullableQueue) ? null : ARICE_GWFVCQG.immutable(nullableQueue); + nullableDeque = isNull(nullableDeque) ? null : ARICE_GWFVCQG.immutable(nullableDeque); + } +} \ No newline at end of file diff --git a/arice/tests/src/test/resources/out/ItemWithQueuesRecord.java b/arice/tests/src/test/resources/out/ItemWithQueuesRecord.java new file mode 100644 index 0000000..cedefc0 --- /dev/null +++ b/arice/tests/src/test/resources/out/ItemWithQueuesRecord.java @@ -0,0 +1,59 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Queue; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; +import pl.com.labaj.autorecord.collections.ImmutableDeque; +import pl.com.labaj.autorecord.extension.arice.ARICE; +import pl.com.labaj.autorecord.extension.arice.ARICEUtilities; + +@Generated( + comments = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + value = "pl.com.labaj.autorecord.AutoRecord" +) +@GeneratedWithAutoRecord +@ARICEUtilities(className = "pl.com.labaj.autorecord.extension.arice.ARICE") +record ItemWithQueuesRecord(Queue queue, + Deque deque, + ArrayDeque arrayDeque, + ImmutableDeque immutableDeque, + @Nullable Queue nullableQueue, + @Nullable Deque nullableDeque, + @Nullable ArrayDeque nullableArrayDeque, + @Nullable ImmutableDeque nullableImmutableDeque) implements ItemWithQueues { + ItemWithQueuesRecord { + // pl.com.labaj.autorecord.processor.AutoRecordProcessor + requireNonNull(queue, "queue must not be null"); + requireNonNull(deque, "deque must not be null"); + requireNonNull(arrayDeque, "arrayDeque must not be null"); + requireNonNull(immutableDeque, "immutableDeque must not be null"); + + // pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension + queue = ARICE.immutable(queue); + deque = ARICE.immutable(deque); + nullableQueue = isNull(nullableQueue) ? null : ARICE.immutable(nullableQueue); + nullableDeque = isNull(nullableDeque) ? null : ARICE.immutable(nullableDeque); + } +} \ No newline at end of file diff --git a/arice/tests/src/test/resources/out/ItemWithSetsCustomTypesRecord.java b/arice/tests/src/test/resources/out/ItemWithSetsCustomTypesRecord.java new file mode 100644 index 0000000..d3405b5 --- /dev/null +++ b/arice/tests/src/test/resources/out/ItemWithSetsCustomTypesRecord.java @@ -0,0 +1,103 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ImmutableSet; +import java.lang.Enum; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.NavigableSet; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; +import pl.com.labaj.autorecord.extension.arice.ARICEUtilities; +import pl.com.labaj.autorecord.extension.arice.ARICE_IVDASUG; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +@Generated( + comments = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + value = "pl.com.labaj.autorecord.AutoRecord" +) +@GeneratedWithAutoRecord +@ARICEUtilities( + immutableTypes = { + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserNavigableSet", + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserSortedSet", + "pl.com.labaj.autorecord.testcase.user.UserCollections.UserSet" + }, + className = "pl.com.labaj.autorecord.extension.arice.ARICE_IVDASUG" +) +record ItemWithSetsCustomTypesRecord>(Set set, + HashSet hashSet, + LinkedHashSet linkedHashSet, + SortedSet sortedSet, + UserCollections.SortedSetImpl sortedSetImpl, + NavigableSet navigableSet, + TreeSet treeSet, + UserCollections.UserSet userSet, + UserCollections.UserSetImpl userSetImpl, + UserCollections.UserSortedSet userSortedSet, + UserCollections.UserSortedSetImpl userSortedSetImpl, + UserCollections.UserNavigableSet userNavigableSet, + UserCollections.UserNavigableSetImpl userNavigableSetImpl, + ImmutableSet immutableSet, + @Nullable Set nullableSet, + @Nullable HashSet nullableHashSet, + @Nullable LinkedHashSet nullableLinkedHashSet, + @Nullable SortedSet nullableSortedSet, + @Nullable UserCollections.SortedSetImpl nullableSortedSetImpl, + @Nullable NavigableSet nullableNavigableSet, + @Nullable TreeSet nullableTreeSet, + @Nullable UserCollections.UserSet nullableUserSet, + @Nullable UserCollections.UserSetImpl nullableUserSetImpl, + @Nullable UserCollections.UserSortedSet nullableUserSortedSet, + @Nullable UserCollections.UserSortedSetImpl nullableUserSortedSetImpl, + @Nullable UserCollections.UserNavigableSet nullableUserNavigableSet, + @Nullable UserCollections.UserNavigableSetImpl nullableUserNavigableSetImpl, + @Nullable ImmutableSet nullableImmutableSet) implements ItemWithSetsCustomTypes { + ItemWithSetsCustomTypesRecord { + // pl.com.labaj.autorecord.processor.AutoRecordProcessor + requireNonNull(set, "set must not be null"); + requireNonNull(hashSet, "hashSet must not be null"); + requireNonNull(linkedHashSet, "linkedHashSet must not be null"); + requireNonNull(sortedSet, "sortedSet must not be null"); + requireNonNull(sortedSetImpl, "sortedSetImpl must not be null"); + requireNonNull(navigableSet, "navigableSet must not be null"); + requireNonNull(treeSet, "treeSet must not be null"); + requireNonNull(userSet, "userSet must not be null"); + requireNonNull(userSetImpl, "userSetImpl must not be null"); + requireNonNull(userSortedSet, "userSortedSet must not be null"); + requireNonNull(userSortedSetImpl, "userSortedSetImpl must not be null"); + requireNonNull(userNavigableSet, "userNavigableSet must not be null"); + requireNonNull(userNavigableSetImpl, "userNavigableSetImpl must not be null"); + requireNonNull(immutableSet, "immutableSet must not be null"); + + // pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension + set = ARICE_IVDASUG.immutable(set); + sortedSet = ARICE_IVDASUG.immutable(sortedSet); + navigableSet = ARICE_IVDASUG.immutable(navigableSet); + nullableSet = isNull(nullableSet) ? null : ARICE_IVDASUG.immutable(nullableSet); + nullableSortedSet = isNull(nullableSortedSet) ? null : ARICE_IVDASUG.immutable(nullableSortedSet); + nullableNavigableSet = isNull(nullableNavigableSet) ? null : ARICE_IVDASUG.immutable(nullableNavigableSet); + } +} \ No newline at end of file diff --git a/arice/tests/src/test/resources/out/ItemWithSetsNoUserDefinedCollectionsRecord.java b/arice/tests/src/test/resources/out/ItemWithSetsNoUserDefinedCollectionsRecord.java new file mode 100644 index 0000000..88a2452 --- /dev/null +++ b/arice/tests/src/test/resources/out/ItemWithSetsNoUserDefinedCollectionsRecord.java @@ -0,0 +1,96 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ImmutableSet; +import java.lang.Enum; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.NavigableSet; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; +import pl.com.labaj.autorecord.extension.arice.ARICE; +import pl.com.labaj.autorecord.extension.arice.ARICEUtilities; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +@Generated( + comments = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + value = "pl.com.labaj.autorecord.AutoRecord" +) +@GeneratedWithAutoRecord +@ARICEUtilities(className = "pl.com.labaj.autorecord.extension.arice.ARICE") +record ItemWithSetsNoUserDefinedCollectionsRecord>(Set set, + HashSet hashSet, + LinkedHashSet linkedHashSet, + SortedSet sortedSet, + UserCollections.SortedSetImpl sortedSetImpl, + NavigableSet navigableSet, + TreeSet treeSet, + UserCollections.UserSet userSet, + UserCollections.UserSetImpl userSetImpl, + UserCollections.UserSortedSet userSortedSet, + UserCollections.UserSortedSetImpl userSortedSetImpl, + UserCollections.UserNavigableSet userNavigableSet, + UserCollections.UserNavigableSetImpl userNavigableSetImpl, + ImmutableSet immutableSet, + @Nullable Set nullableSet, + @Nullable HashSet nullableHashSet, + @Nullable LinkedHashSet nullableLinkedHashSet, + @Nullable SortedSet nullableSortedSet, + @Nullable UserCollections.SortedSetImpl nullableSortedSetImpl, + @Nullable NavigableSet nullableNavigableSet, + @Nullable TreeSet nullableTreeSet, + @Nullable UserCollections.UserSet nullableUserSet, + @Nullable UserCollections.UserSetImpl nullableUserSetImpl, + @Nullable UserCollections.UserSortedSet nullableUserSortedSet, + @Nullable UserCollections.UserSortedSetImpl nullableUserSortedSetImpl, + @Nullable UserCollections.UserNavigableSet nullableUserNavigableSet, + @Nullable UserCollections.UserNavigableSetImpl nullableUserNavigableSetImpl, + @Nullable ImmutableSet nullableImmutableSet) implements ItemWithSetsNoUserDefinedCollections { + ItemWithSetsNoUserDefinedCollectionsRecord { + // pl.com.labaj.autorecord.processor.AutoRecordProcessor + requireNonNull(set, "set must not be null"); + requireNonNull(hashSet, "hashSet must not be null"); + requireNonNull(linkedHashSet, "linkedHashSet must not be null"); + requireNonNull(sortedSet, "sortedSet must not be null"); + requireNonNull(sortedSetImpl, "sortedSetImpl must not be null"); + requireNonNull(navigableSet, "navigableSet must not be null"); + requireNonNull(treeSet, "treeSet must not be null"); + requireNonNull(userSet, "userSet must not be null"); + requireNonNull(userSetImpl, "userSetImpl must not be null"); + requireNonNull(userSortedSet, "userSortedSet must not be null"); + requireNonNull(userSortedSetImpl, "userSortedSetImpl must not be null"); + requireNonNull(userNavigableSet, "userNavigableSet must not be null"); + requireNonNull(userNavigableSetImpl, "userNavigableSetImpl must not be null"); + requireNonNull(immutableSet, "immutableSet must not be null"); + + // pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension + set = ARICE.immutable(set); + sortedSet = ARICE.immutable(sortedSet); + navigableSet = ARICE.immutable(navigableSet); + nullableSet = isNull(nullableSet) ? null : ARICE.immutable(nullableSet); + nullableSortedSet = isNull(nullableSortedSet) ? null : ARICE.immutable(nullableSortedSet); + nullableNavigableSet = isNull(nullableNavigableSet) ? null : ARICE.immutable(nullableNavigableSet); + } +} \ No newline at end of file diff --git a/arice/tests/src/test/resources/out/ItemWithSetsRecord.java b/arice/tests/src/test/resources/out/ItemWithSetsRecord.java new file mode 100644 index 0000000..111972b --- /dev/null +++ b/arice/tests/src/test/resources/out/ItemWithSetsRecord.java @@ -0,0 +1,98 @@ +package pl.com.labaj.autorecord.testcase; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ImmutableSet; +import java.lang.Enum; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.NavigableSet; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; +import pl.com.labaj.autorecord.extension.arice.ARICE; +import pl.com.labaj.autorecord.extension.arice.ARICEUtilities; +import pl.com.labaj.autorecord.testcase.user.UserCollections; + +@Generated( + comments = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + value = "pl.com.labaj.autorecord.AutoRecord" +) +@GeneratedWithAutoRecord +@ARICEUtilities( + className = "pl.com.labaj.autorecord.extension.arice.ARICE" +) +record ItemWithSetsRecord>(Set set, + HashSet hashSet, + LinkedHashSet linkedHashSet, + SortedSet sortedSet, + UserCollections.SortedSetImpl sortedSetImpl, + NavigableSet navigableSet, + TreeSet treeSet, + UserCollections.UserSet userSet, + UserCollections.UserSetImpl userSetImpl, + UserCollections.UserSortedSet userSortedSet, + UserCollections.UserSortedSetImpl userSortedSetImpl, + UserCollections.UserNavigableSet userNavigableSet, + UserCollections.UserNavigableSetImpl userNavigableSetImpl, + ImmutableSet immutableSet, + @Nullable Set nullableSet, + @Nullable HashSet nullableHashSet, + @Nullable LinkedHashSet nullableLinkedHashSet, + @Nullable SortedSet nullableSortedSet, + @Nullable UserCollections.SortedSetImpl nullableSortedSetImpl, + @Nullable NavigableSet nullableNavigableSet, + @Nullable TreeSet nullableTreeSet, + @Nullable UserCollections.UserSet nullableUserSet, + @Nullable UserCollections.UserSetImpl nullableUserSetImpl, + @Nullable UserCollections.UserSortedSet nullableUserSortedSet, + @Nullable UserCollections.UserSortedSetImpl nullableUserSortedSetImpl, + @Nullable UserCollections.UserNavigableSet nullableUserNavigableSet, + @Nullable UserCollections.UserNavigableSetImpl nullableUserNavigableSetImpl, + @Nullable ImmutableSet nullableImmutableSet) implements ItemWithSets { + ItemWithSetsRecord { + // pl.com.labaj.autorecord.processor.AutoRecordProcessor + requireNonNull(set, "set must not be null"); + requireNonNull(hashSet, "hashSet must not be null"); + requireNonNull(linkedHashSet, "linkedHashSet must not be null"); + requireNonNull(sortedSet, "sortedSet must not be null"); + requireNonNull(sortedSetImpl, "sortedSetImpl must not be null"); + requireNonNull(navigableSet, "navigableSet must not be null"); + requireNonNull(treeSet, "treeSet must not be null"); + requireNonNull(userSet, "userSet must not be null"); + requireNonNull(userSetImpl, "userSetImpl must not be null"); + requireNonNull(userSortedSet, "userSortedSet must not be null"); + requireNonNull(userSortedSetImpl, "userSortedSetImpl must not be null"); + requireNonNull(userNavigableSet, "userNavigableSet must not be null"); + requireNonNull(userNavigableSetImpl, "userNavigableSetImpl must not be null"); + requireNonNull(immutableSet, "immutableSet must not be null"); + + // pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension + set = ARICE.immutable(set); + sortedSet = ARICE.immutable(sortedSet); + navigableSet = ARICE.immutable(navigableSet); + nullableSet = isNull(nullableSet) ? null : ARICE.immutable(nullableSet); + nullableSortedSet = isNull(nullableSortedSet) ? null : ARICE.immutable(nullableSortedSet); + nullableNavigableSet = isNull(nullableNavigableSet) ? null : ARICE.immutable(nullableNavigableSet); + } +} \ No newline at end of file diff --git a/arice/utils/README.md b/arice/utils/README.md new file mode 100644 index 0000000..c1b4d00 --- /dev/null +++ b/arice/utils/README.md @@ -0,0 +1,34 @@ +# ARICE Utilities + +Provides classes used copy collections to their appropriate immutable versions during generating record. + +Please, see [WIKI](https://github.com/pawellabaj/auto-record/wiki/ARICE) for information. + +## Usage + +If you use annotation processing in your project, the library will be provided as a dependency of [ARICE](https://github.com/pawellabaj/auto-record/tree/main/arice/extension). + +The classes in this package can also be used independently to add memoization capabilities to your own classes. +In such case, import `arice-utils` directly. + +### Maven + +Add the following dependency to your `pom.xml` file: + +```xml + + + pl.com.labaj.autorecord + arice-utils + ${arice.version} + +``` + +### Gradle + +Declare the following dependency in your `build.gradle` script: + +```groovy +dependencies { + annotationProcessor 'pl.com.labaj.autorecord:arice-utils:${ariceVersion}' +} \ No newline at end of file diff --git a/arice/utils/pom.xml b/arice/utils/pom.xml new file mode 100644 index 0000000..c64b344 --- /dev/null +++ b/arice/utils/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + pl.com.labaj.autorecord + arice-project + 1.0.0-SNAPSHOT + ../project/pom.xml + + + ARICE Utilities + Auto Record Immutable Collections Extension + arice-utils + + + ${project.basedir}/../../.build/lic-header.txt + + + + + com.google.guava + guava + provided + + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + test + + + \ No newline at end of file diff --git a/arice/utils/src/main/java/pl/com/labaj/autorecord/collections/Collectors.java b/arice/utils/src/main/java/pl/com/labaj/autorecord/collections/Collectors.java new file mode 100644 index 0000000..a007e90 --- /dev/null +++ b/arice/utils/src/main/java/pl/com/labaj/autorecord/collections/Collectors.java @@ -0,0 +1,79 @@ +package pl.com.labaj.autorecord.collections; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.google.common.collect.ImmutableList; +import org.apiguardian.api.API; + +import java.util.ArrayDeque; +import java.util.stream.Collector; + +import static org.apiguardian.api.API.Status.STABLE; + +/** + * Utility class that provides custom {@linkplain Collector}s for creating immutable collections. + *

+ * The collectors provided by this class allow you to create both {@link ImmutableCollection} + * and {@link ImmutableDeque} instances. + */ + +@API(status = STABLE) +public final class Collectors { + static final Collector> TO_IMMUTABLE_COLLECTION = Collector.of( + ImmutableList::builder, + ImmutableList.Builder::add, + (builder1, builder2) -> builder1.addAll(builder2.build()), + builder -> new ImmutableCollection<>(builder.build()) + ); + private static final Collector> TO_IMMUTABLE_DEQUE = Collector.of( + ArrayDeque::new, + ArrayDeque::add, + (deque1, deque2) -> { + deque1.addAll(deque2); + return deque1; + }, + ImmutableDeque::new + ); + + private Collectors() {} + + /** + * A collector that accumulates elements into an {@link ImmutableCollection}. + * The resulting collection can either allow duplicates or not, depending on the provided flag. + * + * @param the type of elements to be collected + * @return a collector that accumulates elements into an immutable collection + * @see ImmutableCollection#toImmutableCollection() + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static Collector> toImmutableCollection() { + return (Collector) TO_IMMUTABLE_COLLECTION; + } + + /** + * A collector that accumulates elements into an {@link ImmutableDeque}. + * The resulting deque will be immutable. + * + * @param the type of elements to be collected + * @return a collector that accumulates elements into an immutable deque + * @see ImmutableDeque#toImmutableDeque() + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static Collector> toImmutableDeque() { + return (Collector) TO_IMMUTABLE_DEQUE; + } +} diff --git a/arice/utils/src/main/java/pl/com/labaj/autorecord/collections/ImmutableCollection.java b/arice/utils/src/main/java/pl/com/labaj/autorecord/collections/ImmutableCollection.java new file mode 100644 index 0000000..af5479c --- /dev/null +++ b/arice/utils/src/main/java/pl/com/labaj/autorecord/collections/ImmutableCollection.java @@ -0,0 +1,204 @@ +package pl.com.labaj.autorecord.collections; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.google.common.collect.ImmutableList; +import org.apiguardian.api.API; + +import java.util.Collection; +import java.util.Iterator; +import java.util.stream.Collector; + +import static org.apiguardian.api.API.Status.STABLE; + +/** + * An implementation of {@link Collection} that represents an immutable collection. This class is backed by + * Google Guava's {@link com.google.common.collect.ImmutableCollection}, which ensures that the collection cannot be + * modified after creation. + *

+ * The {@link ImmutableCollection} can be created using the static factory method {@link #copyOfCollection(Collection)} + * or by using the {@link #toImmutableCollection()} collector. + * + * @param the type of elements in the collection + */ +@API(status = STABLE) +public class ImmutableCollection implements Collection { + + private final com.google.common.collect.ImmutableCollection delegate; + + /** + * Returns an immutable copy of the given collection. If the input collection is already an instance of + * {@link ImmutableCollection}, it is returned as is. Otherwise, a new {@link ImmutableCollection} is created + * based on whether duplicates are allowed in the input collection or not. + * + * @param collection the input collection to copy + * @param the type of elements in the collection + * @return an immutable collection that is a copy of the input collection + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public static ImmutableCollection copyOfCollection(Collection collection) { + if (collection instanceof ImmutableCollection immutableCollection) { + return immutableCollection; + } + + return new ImmutableCollection<>(collection); + } + + /** + * A collector that accumulates elements into an {@link ImmutableCollection}. + * The resulting collection can either allow duplicates or not, depending on the provided flag. + * + * @param the type of elements to be collected + * @return a collector that accumulates elements into an immutable collection + */ + public static Collector> toImmutableCollection() { + return Collectors.toImmutableCollection(); + } + + ImmutableCollection(Collection collection) { + delegate = ImmutableList.copyOf(collection); + } + + /** + * {@inheritDoc} + */ + @Override + public int size() { + return delegate.size(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(Object o) { + return delegate.contains(o); + } + + /** + * {@inheritDoc} + */ + @Override + public Iterator iterator() { + return delegate.iterator(); + } + + /** + * {@inheritDoc} + */ + @Override + public Object[] toArray() { + return delegate.toArray(); + } + + /** + * {@inheritDoc} + */ + @Override + public T[] toArray(T[] a) { + return delegate.toArray(a); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final boolean add(E e) { + throw new UnsupportedOperationException("add"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final boolean remove(Object o) { + throw new UnsupportedOperationException("remove"); + } + + /** + * {@inheritDoc} + */ + @Override + public final boolean containsAll(Collection c) { + return delegate.containsAll(c); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final boolean addAll(Collection c) { + throw new UnsupportedOperationException("addAll"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final boolean removeAll(Collection c) { + throw new UnsupportedOperationException("removeAll"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final boolean retainAll(Collection c) { + throw new UnsupportedOperationException("retainAll"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + + @Override + @Deprecated + public final void clear() { + throw new UnsupportedOperationException("clear"); + } +} diff --git a/arice/utils/src/main/java/pl/com/labaj/autorecord/collections/ImmutableDeque.java b/arice/utils/src/main/java/pl/com/labaj/autorecord/collections/ImmutableDeque.java new file mode 100644 index 0000000..916f88e --- /dev/null +++ b/arice/utils/src/main/java/pl/com/labaj/autorecord/collections/ImmutableDeque.java @@ -0,0 +1,452 @@ +package pl.com.labaj.autorecord.collections; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import org.apiguardian.api.API; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Deque; +import java.util.Iterator; +import java.util.Queue; +import java.util.stream.Collector; + +import static java.util.Objects.requireNonNull; +import static org.apiguardian.api.API.Status.STABLE; + +/** + * An immutable implementation of the {@link Deque} interface that wraps a delegate deque. + * The elements in the Deque cannot be modified after creation, and attempts to modify the Deque will result in + * {@link UnsupportedOperationException} being thrown. + *

+ * The class provides various static factory methods to create instances of ImmutableDeque. + * + * @param the type of elements in the deque + */ +@API(status = STABLE) +public class ImmutableDeque implements Deque { + + private final Deque delegate; + + /** + * Creates an immutable copy of the given queue. The elements in the deque will be in the order + * given by the collection's iterator. + * + * @param queue the collection whose elements are to be placed into the Deque + * @param the type of elements in the deque + * @return an immutable copy of the given queue + * @throws NullPointerException if the specified collection is null + */ + public static ImmutableDeque copyOfQueue(Queue queue) { + requireNonNull(queue, "queue must not be null"); + return new ImmutableDeque<>(queue); + } + + /** + * Creates an immutable copy of the given collection. The elements in the deque will be in the order + * given by the collection's iterator. + * + * @param collection the collection whose elements are to be placed into the Deque + * @param the type of elements in the deque + * @return an immutable copy of the given collection + * @throws NullPointerException if the specified collection is null + */ + public static ImmutableDeque copyOf(Collection collection) { + requireNonNull(collection, "collection must not be null"); + return new ImmutableDeque<>(collection); + } + + /** + * A collector that accumulates elements into an {@link ImmutableDeque}. + * The resulting deque will be immutable. + * + * @param the type of elements to be collected + * @return a collector that accumulates elements into an immutable deque + * @see ImmutableDeque#toImmutableDeque() + */ + public static Collector> toImmutableDeque() { + return Collectors.toImmutableDeque(); + } + + ImmutableDeque(Collection collection) { + delegate = new ArrayDeque<>(collection); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final void addFirst(E e) { + throw new UnsupportedOperationException("addFirst"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final void addLast(E e) { + throw new UnsupportedOperationException("addLast"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final boolean offerFirst(E e) { + throw new UnsupportedOperationException("offerFirst"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final boolean offerLast(E e) { + throw new UnsupportedOperationException("offerLast"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final E removeFirst() { + throw new UnsupportedOperationException("removeFirst"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final E removeLast() { + throw new UnsupportedOperationException("removeLast"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final E pollFirst() { + throw new UnsupportedOperationException("pollFirst"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final E pollLast() { + throw new UnsupportedOperationException("pollLast"); + } + + /** + * {@inheritDoc} + */ + @Override + public E getFirst() { + return delegate.getFirst(); + } + + /** + * {@inheritDoc} + */ + @Override + public E getLast() { + return delegate.getLast(); + } + + /** + * {@inheritDoc} + */ + @Override + public E peekFirst() { + return delegate.peekFirst(); + } + + /** + * {@inheritDoc} + */ + @Override + public E peekLast() { + return delegate.getLast(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final boolean removeFirstOccurrence(Object o) { + throw new UnsupportedOperationException("removeFirstOccurrence"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final boolean removeLastOccurrence(Object o) { + throw new UnsupportedOperationException("removeLastOccurrence"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final boolean add(E e) { + throw new UnsupportedOperationException("add"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final boolean offer(E e) { + throw new UnsupportedOperationException("offer"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final E remove() { + throw new UnsupportedOperationException("remove"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final E poll() { + throw new UnsupportedOperationException("poll"); + } + + /** + * {@inheritDoc} + */ + @Override + public E element() { + return delegate.element(); + } + + /** + * {@inheritDoc} + */ + @Override + public E peek() { + return delegate.peek(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final boolean addAll(Collection c) { + throw new UnsupportedOperationException("addAll"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final boolean removeAll(Collection c) { + throw new UnsupportedOperationException("removeAll"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final boolean retainAll(Collection c) { + throw new UnsupportedOperationException("retainAll"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final void clear() { + throw new UnsupportedOperationException("clear"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final void push(E e) { + throw new UnsupportedOperationException("push"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final E pop() { + throw new UnsupportedOperationException("pop"); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final boolean remove(Object o) { + throw new UnsupportedOperationException("remove"); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean containsAll(Collection c) { + return delegate.containsAll(c); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(Object o) { + return delegate.contains(o); + } + + /** + * {@inheritDoc} + */ + @Override + public int size() { + return delegate.size(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + /** + * {@inheritDoc} + */ + @Override + public Iterator iterator() { + return new ImmutableIterator<>(delegate.iterator()); + } + + /** + * {@inheritDoc} + */ + @Override + public Object[] toArray() { + return delegate.toArray(); + } + + /** + * {@inheritDoc} + */ + @Override + public T[] toArray(T[] a) { + return delegate.toArray(a); + } + + /** + * {@inheritDoc} + */ + @Override + public Iterator descendingIterator() { + return new ImmutableIterator<>(delegate.descendingIterator()); + } +} diff --git a/arice/utils/src/main/java/pl/com/labaj/autorecord/collections/ImmutableIterator.java b/arice/utils/src/main/java/pl/com/labaj/autorecord/collections/ImmutableIterator.java new file mode 100644 index 0000000..45e2b19 --- /dev/null +++ b/arice/utils/src/main/java/pl/com/labaj/autorecord/collections/ImmutableIterator.java @@ -0,0 +1,73 @@ +package pl.com.labaj.autorecord.collections; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import org.apiguardian.api.API; + +import java.util.Iterator; + +import static org.apiguardian.api.API.Status.STABLE; + +/** + * An immutable wrapper around an {@link Iterator} that prevents modifications to the underlying collection. + * The class implements the Iterator interface and delegates the actual iteration operations to the provided + * delegate Iterator. Any attempt to call the {@link #remove()} method will result in an + * {@link UnsupportedOperationException} being thrown, as the Iterator is immutable. + * + * @param the type of elements returned by this iterator + */ +@API(status = STABLE) +public class ImmutableIterator implements Iterator { + private final Iterator delegate; + + /** + * Creates an immutable Iterator that wraps the provided delegate Iterator. + * + * @param iterator the delegate Iterator to be wrapped + */ + public ImmutableIterator(Iterator iterator) { + delegate = iterator; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasNext() { + return delegate.hasNext(); + } + + /** + * {@inheritDoc} + */ + @Override + public E next() { + return delegate.next(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Override + @Deprecated + public final void remove() { + throw new UnsupportedOperationException("remove"); + } +} diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/generator/package-info.java b/arice/utils/src/main/java/pl/com/labaj/autorecord/collections/package-info.java similarity index 65% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/generator/package-info.java rename to arice/utils/src/main/java/pl/com/labaj/autorecord/collections/package-info.java index b49c218..00e7b53 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/generator/package-info.java +++ b/arice/utils/src/main/java/pl/com/labaj/autorecord/collections/package-info.java @@ -1,5 +1,3 @@ -@API(status = INTERNAL) -package pl.com.labaj.autorecord.processor.generator; /*- * Copyright © 2023 Auto Record @@ -16,7 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import org.apiguardian.api.API; - -import static org.apiguardian.api.API.Status.INTERNAL; \ No newline at end of file +/** + * Provides classes used copy collections to their appropriate immutable versions during generating record. + *

+ * The classes in this package can also be used independently + * + * @see Wiki + */ +package pl.com.labaj.autorecord.collections; \ No newline at end of file diff --git a/arice/utils/src/test/java/pl/com/labaj/autorecord/collections/ImmutableCollectionTest.java b/arice/utils/src/test/java/pl/com/labaj/autorecord/collections/ImmutableCollectionTest.java new file mode 100644 index 0000000..cc76627 --- /dev/null +++ b/arice/utils/src/test/java/pl/com/labaj/autorecord/collections/ImmutableCollectionTest.java @@ -0,0 +1,87 @@ +package pl.com.labaj.autorecord.collections; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static pl.com.labaj.autorecord.collections.ImmutableCollection.toImmutableCollection; + +class ImmutableCollectionTest { + + public static Stream testData() { + return Stream.of( + new TestArgument<>( + () -> List.of("A", "B", "A", "B"), + ImmutableCollection::copyOfCollection), + new TestArgument<>( + () -> Set.of("A", "B"), + ImmutableCollection::copyOfCollection), + new TestArgument<>( + () -> List.of("A", "B", "A", "B"), + ImmutableCollection::copyOfCollection), + new TestArgument<>( + () -> List.of("A", "B", "A", "B"), + collection -> collection.stream().collect(toImmutableCollection())), + new TestArgument<>( + () -> List.of("A", "B", "A", "B"), + list -> list.stream().collect(toImmutableCollection())) + ); + } + + @SuppressWarnings({"rawtypes", "unchecked", "Convert2MethodRef", "deprecation"}) + @ParameterizedTest + @MethodSource("testData") + void shouldReurnImmutableCollection(Supplier collectionSupplier, Function icFunction) { + //given + var collection = collectionSupplier.get(); + + //when + var immutableCollection = icFunction.apply(collection); + + //then + assertAll( + () -> assertThat(immutableCollection).hasSameElementsAs(collection), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableCollection.add("A")), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableCollection.remove("A")), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableCollection.addAll(List.of("A", "B"))), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableCollection.removeAll(List.of("A", "B"))), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableCollection.retainAll(List.of("A", "B"))), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableCollection.clear()), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableCollection.iterator().remove()) + ); + } + + record TestArgument(Supplier> collectionSupplier, Function, ImmutableCollection> icFunction) implements Arguments { + + @Override + public Object[] get() { + return new Object[] {collectionSupplier, icFunction}; + } + } +} \ No newline at end of file diff --git a/arice/utils/src/test/java/pl/com/labaj/autorecord/collections/ImmutableDequeTest.java b/arice/utils/src/test/java/pl/com/labaj/autorecord/collections/ImmutableDequeTest.java new file mode 100644 index 0000000..fc524d4 --- /dev/null +++ b/arice/utils/src/test/java/pl/com/labaj/autorecord/collections/ImmutableDequeTest.java @@ -0,0 +1,116 @@ +package pl.com.labaj.autorecord.collections; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static pl.com.labaj.autorecord.collections.ImmutableDeque.toImmutableDeque; + +class ImmutableDequeTest { + + @SuppressWarnings("unchecked") + public static Stream testData() { + return Stream.of( + new TestArgument<>( + () -> List.of("A", "B", "A", "B"), + ImmutableDeque::copyOf), + new TestArgument<>( + () -> { + var arrayDeque = new ArrayDeque(); + arrayDeque.add("A"); + arrayDeque.add("B"); + arrayDeque.add("A"); + arrayDeque.add("B"); + return arrayDeque; + }, + ImmutableDeque::copyOf), + new TestArgument<>( + () -> { + var arrayDeque = new ArrayDeque(); + arrayDeque.add("A"); + arrayDeque.add("B"); + arrayDeque.add("A"); + arrayDeque.add("B"); + return arrayDeque; + }, + deque -> ImmutableDeque.copyOfQueue((ArrayDeque) deque)), + new TestArgument<>( + () -> List.of("A", "B", "A", "B"), + list -> list.stream().collect(toImmutableDeque())) + ); + } + + @SuppressWarnings({"rawtypes", "unchecked", "Convert2MethodRef", "deprecation"}) + @ParameterizedTest + @MethodSource("testData") + void shouldReurnImmutableCollection(Supplier collectionSupplier, Function idFunction) { + //given + var collection = collectionSupplier.get(); + + //when + var immutableDeque = idFunction.apply(collection); + + //then + assertAll( + () -> assertThat(immutableDeque).hasSameElementsAs(collection), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.addFirst("A")), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.addLast("A")), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.offerFirst("A")), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.offerLast("A")), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.removeFirst()), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.removeLast()), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.pollFirst()), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.pollLast()), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.removeFirstOccurrence("A")), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.removeLastOccurrence("A")), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.add("A")), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.offer("A")), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.remove("A")), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.remove()), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.poll()), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.addAll(List.of("A", "B"))), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.removeAll(List.of("A", "B"))), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.retainAll(List.of("A", "B"))), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.clear()), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.pop()), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.push("A")), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.remove("A")), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.iterator().remove()), + () -> assertThrows(UnsupportedOperationException.class, () -> immutableDeque.descendingIterator().remove()) + ); + } + + record TestArgument(Supplier> collectionSupplier, Function, ImmutableDeque> idFunction) implements Arguments { + + @Override + public Object[] get() { + return new Object[] {collectionSupplier, idFunction}; + } + } +} \ No newline at end of file diff --git a/modules/auto-record-api/README.md b/modules/auto-record-api/README.md new file mode 100644 index 0000000..aec5c80 --- /dev/null +++ b/modules/auto-record-api/README.md @@ -0,0 +1,34 @@ +# Auto Record API + +Annotations to mark interfaces to be processed. + +Please, see [WIKI](https://github.com/pawellabaj/auto-record/wiki) for information. + +## Usage + +If you use annotation processing in your project, the library will be provided as a dependency of [Auto Record](https://github.com/pawellabaj/auto-record/tree/main/modules/auto-record). + +If you just want to mark your sources (ieg. annotations are being processed in other project or build step), +use `auto-record-api` directly. + +### Maven + +Add the following dependency to your `pom.xml` file: + +```xml + + + pl.com.labaj.autorecord + auto-record-api + ${auto-record.version} + +``` + +### Gradle + +Declare the following dependency in your `build.gradle` script: + +```groovy +dependencies { + annotationProcessor 'pl.com.labaj.autorecord:auto-record-api:${autoRecordVersion}' +} \ No newline at end of file diff --git a/modules/auto-record-api/pom.xml b/modules/auto-record-api/pom.xml new file mode 100644 index 0000000..c4b50a0 --- /dev/null +++ b/modules/auto-record-api/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + pl.com.labaj.autorecord + auto-record-project + 2.1.1-SNAPSHOT + ../../pom.xml + + + Auto Record API + auto-record-api + + + + io.soabase.record-builder + record-builder-core + provided + + + net.fabricmc + javapoet + provided + + + \ No newline at end of file diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/AutoRecord.java b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/AutoRecord.java similarity index 86% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/AutoRecord.java rename to modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/AutoRecord.java index 48b8f1a..d79ef4f 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/AutoRecord.java +++ b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/AutoRecord.java @@ -17,9 +17,9 @@ */ import io.soabase.recordbuilder.core.RecordBuilder; +import org.apiguardian.api.API; import pl.com.labaj.autorecord.extension.AutoRecordExtension; import pl.com.labaj.autorecord.extension.AutoRecordExtensions; -import pl.com.labaj.autorecord.processor.AutoRecordProcessor; import java.lang.annotation.Inherited; import java.lang.annotation.Repeatable; @@ -30,6 +30,7 @@ import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.CLASS; import static java.lang.annotation.RetentionPolicy.SOURCE; +import static org.apiguardian.api.API.Status.STABLE; /** * AutoRecord is a code generator that helps you easily generate Java records. @@ -44,10 +45,13 @@ @Retention(SOURCE) @Target(TYPE) @Inherited +@API(status = STABLE) public @interface AutoRecord { /** * Specifies options for the {@link AutoRecord} annotation. + *

+ * Compatibility Note: Methods may be added to this annotation in future releases of the library. * * @see Customization Wiki */ @@ -81,12 +85,14 @@ } /** - * Used to specify an extension for the {@link AutoRecordProcessor} used during a record generation. + * Used to specify an extension for the {@link AutoRecord} Processor used during a record generation. + *

+ * Note: Class specified in {@link #extensionClass()} must be already present on classpath during the annotation processing. * * @see AutoRecordExtension * @see Template#extensions() * @see Extensions Wiki - * @since 2.1.0 + * @since 3.0.0 */ @Retention(SOURCE) @Target({ANNOTATION_TYPE, TYPE}) @@ -94,17 +100,17 @@ @Repeatable(AutoRecordExtensions.class) @interface Extension { /** - * The class of the custom extension. It needs to implement the {@link AutoRecordExtension} interface. + * The FQCN of the custom extension. The class needs to implement the {@link AutoRecordExtension} interface. * - * @return the class of the custom extension. + * @return the class name of the custom extension. */ - Class extensionClass(); + String extensionClass(); /** * Additional parameters for the custom extension. * * @return an array of {@link String} objects representing the additional parameters for the custom extension. - * @see AutoRecordExtension#setParameters(String[]) + * @see AutoRecordExtension#init(javax.annotation.processing.ProcessingEnvironment, String[]) */ String[] parameters() default {}; } diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/AutoRecordWithBuilder.java b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/AutoRecordWithBuilder.java similarity index 92% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/AutoRecordWithBuilder.java rename to modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/AutoRecordWithBuilder.java index 677c2ab..530e5a4 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/AutoRecordWithBuilder.java +++ b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/AutoRecordWithBuilder.java @@ -16,11 +16,14 @@ * limitations under the License. */ +import org.apiguardian.api.API; + import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.SOURCE; +import static org.apiguardian.api.API.Status.STABLE; /** * Custom annotation used to mark interface for annotation processing. @@ -34,5 +37,6 @@ @Retention(SOURCE) @Target(TYPE) @AutoRecord.Template(recordOptions = @AutoRecord.Options(withBuilder = true)) +@API(status = STABLE) public @interface AutoRecordWithBuilder { } diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/GeneratedWithAutoRecord.java b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/GeneratedWithAutoRecord.java similarity index 93% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/GeneratedWithAutoRecord.java rename to modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/GeneratedWithAutoRecord.java index 8e7bb50..975b7db 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/GeneratedWithAutoRecord.java +++ b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/GeneratedWithAutoRecord.java @@ -16,6 +16,8 @@ * limitations under the License. */ +import org.apiguardian.api.API; + import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -24,6 +26,7 @@ import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.CLASS; +import static org.apiguardian.api.API.Status.STABLE; /** * Starting from JaCoCo 0.8.2, classes and methods annotated with annotation @@ -39,4 +42,5 @@ @Documented @Retention(CLASS) @Target({TYPE, METHOD, CONSTRUCTOR}) +@API(status = STABLE) public @interface GeneratedWithAutoRecord {} diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/Ignored.java b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/Ignored.java similarity index 81% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/Ignored.java rename to modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/Ignored.java index 2aebf1b..6caa6ef 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/Ignored.java +++ b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/Ignored.java @@ -16,6 +16,8 @@ * limitations under the License. */ +import org.apiguardian.api.API; + import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -24,9 +26,10 @@ import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE_PARAMETER; import static java.lang.annotation.RetentionPolicy.SOURCE; +import static org.apiguardian.api.API.Status.STABLE; /** - * Annotates methods in {@link AutoRecord @AutoRecord} class for which the corresponding components in generated record are not relevant for + * Annotates methods in {@link AutoRecord @AutoRecord} annotated interface for which the corresponding components in generated record are not relevant for * equality checks - they are ignored in {@code hashCode()} and {@code toString()} methods. * * @see Ignored Components Wiki @@ -34,5 +37,5 @@ @Retention(SOURCE) @Target({METHOD, TYPE_PARAMETER, PARAMETER}) @Inherited -public @interface Ignored { -} +@API(status = STABLE) +public @interface Ignored {} diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/Memoized.java b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/Memoized.java similarity index 76% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/Memoized.java rename to modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/Memoized.java index 2310037..7c39287 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/Memoized.java +++ b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/Memoized.java @@ -16,15 +16,18 @@ * limitations under the License. */ +import org.apiguardian.api.API; + import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.SOURCE; +import static org.apiguardian.api.API.Status.STABLE; /** - * Annotates methods in {@link AutoRecord @AutoRecord} class for which the generated record will memoize + * Annotates methods in {@link AutoRecord @AutoRecord} annotated interface for which the generated record will memoize * the returned value. * * @see Memoization Wiki @@ -32,5 +35,5 @@ @Retention(SOURCE) @Target(METHOD) @Inherited -public @interface Memoized { -} +@API(status = STABLE) +public @interface Memoized {} diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/Context.java b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/context/Context.java similarity index 94% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/Context.java rename to modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/context/Context.java index 4c810f7..ff0d3ad 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/Context.java +++ b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/context/Context.java @@ -17,19 +17,24 @@ */ import io.soabase.recordbuilder.core.RecordBuilder; +import org.apiguardian.api.API; import pl.com.labaj.autorecord.AutoRecord; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.type.TypeMirror; import java.util.List; +import static org.apiguardian.api.API.Status.STABLE; + /** * Represents the context of the {@link AutoRecord} annotation processing. * *

This interface provides access to various information and options needed during generation of a record class. + *

Compatibility Note: Methods may be added to this interface in future releases of the library. * * @since 2.1.0 */ +@API(status = STABLE) public interface Context { /** * Gets the package name of the annotated interface. diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/Logger.java b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/context/Logger.java similarity index 94% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/Logger.java rename to modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/context/Logger.java index e105036..a9bd9f9 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/Logger.java +++ b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/context/Logger.java @@ -16,11 +16,15 @@ * limitations under the License. */ +import org.apiguardian.api.API; + import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.tools.Diagnostic.Kind; +import static org.apiguardian.api.API.Status.STABLE; + /** * A Logger object is used to log messages during annotation processing with specified {@linkplain Kind levels}. * It uses {@link Messager} provided by {@link ProcessingEnvironment} @@ -29,6 +33,8 @@ * @see Kind * @since 2.1.0 */ + +@API(status = STABLE) public interface Logger { /** @@ -40,7 +46,7 @@ public interface Logger { boolean isDebugEnabled(); /** - * Log a message at the {@link Kind#NOTE} level with {@code "[DEBUG] "} prefix + * Log a message at the {@link Kind#NOTE} level with {@code "[AR] "} prefix * when {@link #isDebugEnabled()} returns {@code true} * * @param message the message to be logged @@ -53,7 +59,7 @@ public interface Logger { * * @param message the message to be logged */ - void info(String message); + void note(String message); /** * Log a message at the {@link Kind#WARNING} level. diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/RecordComponent.java b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/context/RecordComponent.java similarity index 95% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/RecordComponent.java rename to modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/context/RecordComponent.java index 556c9c4..b10aa52 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/RecordComponent.java +++ b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/context/RecordComponent.java @@ -16,11 +16,15 @@ * limitations under the License. */ +import org.apiguardian.api.API; + import javax.lang.model.element.AnnotationMirror; import javax.lang.model.type.TypeMirror; import java.lang.annotation.Annotation; import java.util.List; +import static org.apiguardian.api.API.Status.STABLE; + /** * Represents a component of the generated record. *

@@ -28,6 +32,7 @@ * * @since 2.1.0 */ +@API(status = STABLE) public interface RecordComponent { /** * Gets the type of the record component. diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/StaticImports.java b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/context/StaticImports.java similarity index 93% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/StaticImports.java rename to modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/context/StaticImports.java index 9afc33d..15482c0 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/StaticImports.java +++ b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/context/StaticImports.java @@ -17,12 +17,16 @@ */ import com.squareup.javapoet.ClassName; +import org.apiguardian.api.API; + +import static org.apiguardian.api.API.Status.STABLE; /** * Collects {@code static import} statements that will be added into generated record. * * @since 2.1.0 */ +@API(status = STABLE) public interface StaticImports { /** * Adds {@code static import} statement diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/package-info.java b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/context/package-info.java similarity index 74% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/package-info.java rename to modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/context/package-info.java index d369528..939ee1d 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/package-info.java +++ b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/context/package-info.java @@ -14,16 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - /** - * Provides classes representing annotation process context used by {@linkplain pl.com.labaj.autorecord.processor.AutoRecordProcessor AutoRecordProcessor} and + * Provides classes representing annotation process context used by AutoRecordProcessor and * {@linkplain pl.com.labaj.autorecord.extension.AutoRecordExtension AutoRecordExtension} * * @since 2.1.0 */ -@API(status = STABLE, since = "2.1.0") -package pl.com.labaj.autorecord.context; - -import org.apiguardian.api.API; - -import static org.apiguardian.api.API.Status.STABLE; \ No newline at end of file +package pl.com.labaj.autorecord.context; \ No newline at end of file diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/AutoRecordExtension.java b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/extension/AutoRecordExtension.java similarity index 65% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/AutoRecordExtension.java rename to modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/extension/AutoRecordExtension.java index 7e553cd..ec7f5ef 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/AutoRecordExtension.java +++ b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/extension/AutoRecordExtension.java @@ -16,11 +16,15 @@ * limitations under the License. */ +import org.apiguardian.api.API; import pl.com.labaj.autorecord.AutoRecord; -import pl.com.labaj.autorecord.processor.AutoRecordProcessor; + +import javax.annotation.processing.ProcessingEnvironment; + +import static org.apiguardian.api.API.Status.STABLE; /** - * Represents an extension for the {@link AutoRecordProcessor} that can be used + * Represents an extension for the {@link AutoRecord} Processor that can be used * to customize the record generation process. *

* To create a custom extension, implement this interface and override any specific methods as needed. @@ -29,18 +33,25 @@ *

    *
  • {@link CompactConstructorExtension}
  • *
+ *

+ * Compatibility Note: Methods may be added to this interface in future releases of the library. * * @see Extensions Wiki * @since 2.1.0 */ +@API(status = STABLE) public interface AutoRecordExtension { /** * Sets the parameters for the extension. *

* This method can be overridden by the extension to receive any custom parameters that may be needed during record generation. + *

+ * Note: The method is called before any other methods. * - * @param parameters array of {@link String} objects representing the additional parameters for the custom extension. + * @param processingEnv environment to access facilities the tool framework provides to the processor + * @param parameters array of {@link String} objects representing the additional parameters for the custom extension. * @see AutoRecord.Extension#parameters() + * @since 3.0.0 */ - default void setParameters(String[] parameters) {} + default void init(ProcessingEnvironment processingEnv, String[] parameters) {} } diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/AutoRecordExtensions.java b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/extension/AutoRecordExtensions.java similarity index 93% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/AutoRecordExtensions.java rename to modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/extension/AutoRecordExtensions.java index fa0954b..8165cc6 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/AutoRecordExtensions.java +++ b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/extension/AutoRecordExtensions.java @@ -16,6 +16,7 @@ * limitations under the License. */ +import org.apiguardian.api.API; import pl.com.labaj.autorecord.AutoRecord; import java.lang.annotation.Inherited; @@ -26,6 +27,7 @@ import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.SOURCE; +import static org.apiguardian.api.API.Status.STABLE; /** * An annotation used to mark the {@link AutoRecord.Extension} annotation as repeatable. @@ -37,6 +39,7 @@ @Retention(SOURCE) @Target({ANNOTATION_TYPE, TYPE}) @Inherited +@API(status = STABLE) public @interface AutoRecordExtensions { /** * An array of {@link AutoRecord.Extension} annotations representing the extensions to be used during record generation. diff --git a/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/extension/CompactConstructorExtension.java b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/extension/CompactConstructorExtension.java new file mode 100644 index 0000000..6ae6dce --- /dev/null +++ b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/extension/CompactConstructorExtension.java @@ -0,0 +1,125 @@ +package pl.com.labaj.autorecord.extension; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import com.squareup.javapoet.AnnotationSpec; +import com.squareup.javapoet.CodeBlock; +import com.squareup.javapoet.MethodSpec; +import org.apiguardian.api.API; +import pl.com.labaj.autorecord.AutoRecord; +import pl.com.labaj.autorecord.context.Context; +import pl.com.labaj.autorecord.context.StaticImports; + +import java.util.List; + +import static org.apiguardian.api.API.Status.STABLE; + +/** + * Represents an extension of the {@link AutoRecord} Processor for customizing the compact constructor generation process. + *

+ * Implement this interface to provide custom behavior for compact constructor generation. + *

Compatibility Note: Methods may be added to this interface in future releases of the library. + * + * @since 2.1.0 + */ +@API(status = STABLE) +public interface CompactConstructorExtension extends AutoRecordExtension { + /** + * Determines whether the extension should generate the compact constructor content. + * + * @param isGeneratedByProcessor a flag indicating if the compact constructor is generated by the + * {@link AutoRecord} Processor. + * If the flag is {@code false}, it means that compact constructor won't be generated without extensions. + * @param context the {@link Context} object containing of the annotation processing. + * @return {@code} if the compact constructor should be generated by the extension, {@code false} otherwise. + */ + default boolean shouldGenerateCompactConstructor(boolean isGeneratedByProcessor, Context context) { + return isGeneratedByProcessor; + } + + /** + * Provides a code to be inserted before the current content of the compact constructor. + *

+ * Note: The method is called when {@link #shouldGenerateCompactConstructor(boolean, Context)} returns {@code true}. + * + * @param context the {@link Context} object containing of the annotation processing. + * @param staticImports the {@link StaticImports} object {@code static import} to statements that will be added into + * generated record. + * @return a {@link CodeBlock} representing the code to be inserted before the current content of the compact constructor. + * @see CodeBlock + */ + default CodeBlock prefixCompactConstructorContent(Context context, StaticImports staticImports) { + return null; + } + + /** + * Provides a code to be inserted after the current content of the compact constructor. + *

+ * Note: The method is called when {@link #shouldGenerateCompactConstructor(boolean, Context)} returns {@code true}. + * + * @param context the {@link Context} object containing of the annotation processing. + * @param staticImports the {@link StaticImports} object {@code static import} to statements that will be added into + * generated record. + * @return a {@link CodeBlock} representing the code to be inserted after the current content of the compact constructor. + * @see CodeBlock + */ + default CodeBlock suffixCompactConstructorContent(Context context, StaticImports staticImports) { + return null; + } + + /** + * Provides list of additional methods that needs to be added to generated record. + *

+ * Notes: + *

    + *
  • The method is called when {@link #shouldGenerateCompactConstructor(boolean, Context)} returns {@code true}.
  • + *
  • The method is called after {@link #prefixCompactConstructorContent(Context, StaticImports)} + * and {@link #suffixCompactConstructorContent(Context, StaticImports)} methods.
  • + *
+ * + * @param context the {@link Context} object containing of the annotation processing. + * @param staticImports the {@link StaticImports} object {@code static import} to statements that will be added into + * generated record. + * @return a list of {@link MethodSpec} representing additional methods that needs to be added to generated record + * @see MethodSpec + * @since 3.0.0 + */ + default List additionalMethodsToSupportCompactConstructor(Context context, StaticImports staticImports) { + return List.of(); + } + + /** + * Provides list of annotations that the generated record needs to be annotated with. + *

+ * Notes: + *

    + *
  • The method is called when {@link #shouldGenerateCompactConstructor(boolean, Context)} returns {@code true}.
  • + *
  • The method is called after {@link #prefixCompactConstructorContent(Context, StaticImports)} + * and {@link #suffixCompactConstructorContent(Context, StaticImports)} methods.
  • + *
+ * + * @param context the {@link Context} object containing of the annotation processing. + * @param staticImports the {@link StaticImports} object {@code static import} to statements that will be added into + * generated record. + * @return a list of {@link AnnotationSpec} representing additional methods that needs to be added to generated record + * @see AnnotationSpec + * @since 3.0.0 + */ + default List annotationsToSupportCompactConstructor(Context context, StaticImports staticImports) { + return List.of(); + } +} diff --git a/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/extension/package-info.java b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/extension/package-info.java new file mode 100644 index 0000000..aef2f2c --- /dev/null +++ b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/extension/package-info.java @@ -0,0 +1,43 @@ +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +/** + * Provides annotations and interfaces needed to process extensions. + * + *

{@link pl.com.labaj.autorecord.extension.AutoRecordExtension} interface has to be implemented by all extensions. + * + * @see Extensions Wiki + * @since 2.1.0 + */ + +package pl.com.labaj.autorecord.extension; + +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/package-info.java b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/package-info.java similarity index 87% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/package-info.java rename to modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/package-info.java index aa717ee..0567fe8 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/package-info.java +++ b/modules/auto-record-api/src/main/java/pl/com/labaj/autorecord/package-info.java @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - /** * Provides annotations used to annotate interfaces to generate a record for them. * @@ -24,9 +23,4 @@ * * @see Wiki */ -@API(status = STABLE) -package pl.com.labaj.autorecord; - -import org.apiguardian.api.API; - -import static org.apiguardian.api.API.Status.STABLE; \ No newline at end of file +package pl.com.labaj.autorecord; \ No newline at end of file diff --git a/modules/auto-record-tests/pom.xml b/modules/auto-record-tests/pom.xml index 945fc22..36e7a5a 100644 --- a/modules/auto-record-tests/pom.xml +++ b/modules/auto-record-tests/pom.xml @@ -4,9 +4,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - pl.com.labaj + pl.com.labaj.autorecord auto-record-project - 2.0.1-SNAPSHOT + 2.1.1-SNAPSHOT ../../pom.xml @@ -18,9 +18,9 @@ - pl.com.labaj + pl.com.labaj.autorecord auto-record - ${project.parent.version} + 2.1.1-SNAPSHOT @@ -56,21 +56,10 @@ release - - - - org.sonatype.plugins - nexus-staging-maven-plugin - true - - - default-deploy - none - - - - - + + true + none + \ No newline at end of file diff --git a/modules/auto-record-tests/src/main/java/pl/com/labaj/autorecord/test/extension/compact/AlwaysCompactConstructorExtension.java b/modules/auto-record-tests/src/main/java/pl/com/labaj/autorecord/test/extension/compact/AlwaysCompactConstructorExtension.java index 5587526..30124c0 100644 --- a/modules/auto-record-tests/src/main/java/pl/com/labaj/autorecord/test/extension/compact/AlwaysCompactConstructorExtension.java +++ b/modules/auto-record-tests/src/main/java/pl/com/labaj/autorecord/test/extension/compact/AlwaysCompactConstructorExtension.java @@ -21,18 +21,19 @@ import pl.com.labaj.autorecord.context.StaticImports; import pl.com.labaj.autorecord.extension.CompactConstructorExtension; +import javax.annotation.processing.ProcessingEnvironment; import java.util.Arrays; public class AlwaysCompactConstructorExtension implements CompactConstructorExtension { private String[] parameters; @Override - public void setParameters(String[] parameters) { + public void init(ProcessingEnvironment processingEnv, String[] parameters) { this.parameters = parameters; } @Override - public boolean shouldGenerate(boolean isGeneratedByProcessor, Context context) { + public boolean shouldGenerateCompactConstructor(boolean isGeneratedByProcessor, Context context) { return true; } diff --git a/modules/auto-record-tests/src/main/java/pl/com/labaj/autorecord/test/extension/compact/IsPersonAdultVerifierExtension.java b/modules/auto-record-tests/src/main/java/pl/com/labaj/autorecord/test/extension/compact/IsPersonAdultVerifierExtension.java index 6e60eec..252e21e 100644 --- a/modules/auto-record-tests/src/main/java/pl/com/labaj/autorecord/test/extension/compact/IsPersonAdultVerifierExtension.java +++ b/modules/auto-record-tests/src/main/java/pl/com/labaj/autorecord/test/extension/compact/IsPersonAdultVerifierExtension.java @@ -22,6 +22,8 @@ import pl.com.labaj.autorecord.extension.CompactConstructorExtension; import pl.com.labaj.autorecord.processor.AutoRecordProcessorException; +import javax.annotation.processing.ProcessingEnvironment; + public class IsPersonAdultVerifierExtension implements CompactConstructorExtension { private static final Class RUNTIME_EXCEPTION_CLASS = RuntimeException.class; @@ -29,7 +31,7 @@ public class IsPersonAdultVerifierExtension implements CompactConstructorExtensi @SuppressWarnings("java:S112") @Override - public void setParameters(String[] parameters) { + public void init(ProcessingEnvironment processingEnv, String[] parameters) { if (parameters.length < 1) { exceptionClass = RUNTIME_EXCEPTION_CLASS; } else { diff --git a/modules/auto-record-tests/src/main/java/pl/com/labaj/autorecord/test/extension/compact/LoggingExtension.java b/modules/auto-record-tests/src/main/java/pl/com/labaj/autorecord/test/extension/compact/LoggingExtension.java index fa44502..8064d5b 100644 --- a/modules/auto-record-tests/src/main/java/pl/com/labaj/autorecord/test/extension/compact/LoggingExtension.java +++ b/modules/auto-record-tests/src/main/java/pl/com/labaj/autorecord/test/extension/compact/LoggingExtension.java @@ -22,6 +22,7 @@ import pl.com.labaj.autorecord.context.StaticImports; import pl.com.labaj.autorecord.extension.CompactConstructorExtension; +import javax.annotation.processing.ProcessingEnvironment; import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -33,7 +34,7 @@ public class LoggingExtension implements CompactConstructorExtension { private Level level; @Override - public void setParameters(String[] parameters) { + public void init(ProcessingEnvironment processingEnv, String[] parameters) { if (parameters.length < 1) { level = FINE; } else { @@ -42,7 +43,7 @@ public void setParameters(String[] parameters) { } @Override - public boolean shouldGenerate(boolean isGeneratedByProcessor, Context context) { + public boolean shouldGenerateCompactConstructor(boolean isGeneratedByProcessor, Context context) { return true; } diff --git a/modules/auto-record-tests/src/main/java/pl/com/labaj/autorecord/test/extension/compact/WhenProcessorCompactConstructorExtension.java b/modules/auto-record-tests/src/main/java/pl/com/labaj/autorecord/test/extension/compact/WhenProcessorCompactConstructorExtension.java index f2b8ada..2ae1e2c 100644 --- a/modules/auto-record-tests/src/main/java/pl/com/labaj/autorecord/test/extension/compact/WhenProcessorCompactConstructorExtension.java +++ b/modules/auto-record-tests/src/main/java/pl/com/labaj/autorecord/test/extension/compact/WhenProcessorCompactConstructorExtension.java @@ -24,7 +24,7 @@ public class WhenProcessorCompactConstructorExtension implements CompactConstructorExtension { @Override - public boolean shouldGenerate(boolean isGeneratedByProcessor, Context context) { + public boolean shouldGenerateCompactConstructor(boolean isGeneratedByProcessor, Context context) { return isGeneratedByProcessor; } diff --git a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/TemplateWithExtension.java b/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/TemplateWithExtension.java index 46693aa..d545b4d 100644 --- a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/TemplateWithExtension.java +++ b/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/TemplateWithExtension.java @@ -16,9 +16,6 @@ * limitations under the License. */ -import pl.com.labaj.autorecord.test.extension.compact.AlwaysCompactConstructorExtension; -import pl.com.labaj.autorecord.test.extension.compact.WhenProcessorCompactConstructorExtension; - import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -30,8 +27,9 @@ @AutoRecord.Template( recordOptions = @AutoRecord.Options(memoizedHashCode = true), extensions = { - @AutoRecord.Extension(extensionClass = WhenProcessorCompactConstructorExtension.class), - @AutoRecord.Extension(extensionClass = AlwaysCompactConstructorExtension.class, parameters = "From template"), + @AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.test.extension.compact.WhenProcessorCompactConstructorExtension"), + @AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.test.extension.compact.AlwaysCompactConstructorExtension", + parameters = "From template"), } ) public @interface TemplateWithExtension { diff --git a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/test/extension/GenerationWithExtensionsTest.java b/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/extension/GenerationWithExtensionsTest.java similarity index 98% rename from modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/test/extension/GenerationWithExtensionsTest.java rename to modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/extension/GenerationWithExtensionsTest.java index 372baa1..b3d23c4 100644 --- a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/test/extension/GenerationWithExtensionsTest.java +++ b/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/extension/GenerationWithExtensionsTest.java @@ -1,4 +1,4 @@ -package pl.com.labaj.autorecord.test.extension; +package pl.com.labaj.autorecord.extension; /*- * Copyright © 2023 Auto Record diff --git a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/test/extension/PersonWithExtensions.java b/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/extension/PersonWithExtensions.java similarity index 65% rename from modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/test/extension/PersonWithExtensions.java rename to modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/extension/PersonWithExtensions.java index e4169b6..0cf35fb 100644 --- a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/test/extension/PersonWithExtensions.java +++ b/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/extension/PersonWithExtensions.java @@ -1,4 +1,4 @@ -package pl.com.labaj.autorecord.test.extension; +package pl.com.labaj.autorecord.extension; /*- * Copyright © 2023 Auto Record * @@ -16,14 +16,13 @@ */ import pl.com.labaj.autorecord.AutoRecord; -import pl.com.labaj.autorecord.test.extension.compact.IsPersonAdultVerifierExtension; -import pl.com.labaj.autorecord.test.extension.compact.LoggingExtension; import javax.annotation.Nullable; @AutoRecord -@AutoRecord.Extension(extensionClass = IsPersonAdultVerifierExtension.class, parameters = "java.lang.IllegalStateException") -@AutoRecord.Extension(extensionClass = LoggingExtension.class, parameters = "info") +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.test.extension.compact.IsPersonAdultVerifierExtension", + parameters = "java.lang.IllegalStateException") +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.test.extension.compact.LoggingExtension", parameters = "info") interface PersonWithExtensions { String name(); diff --git a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/test/extension/PersonWithExtensionsTest.java b/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/extension/PersonWithExtensionsTest.java similarity index 97% rename from modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/test/extension/PersonWithExtensionsTest.java rename to modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/extension/PersonWithExtensionsTest.java index 7823d8a..b4079f6 100644 --- a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/test/extension/PersonWithExtensionsTest.java +++ b/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/extension/PersonWithExtensionsTest.java @@ -1,4 +1,4 @@ -package pl.com.labaj.autorecord.test.extension; +package pl.com.labaj.autorecord.extension; /*- * Copyright © 2023 Auto Record * diff --git a/modules/auto-record-tests/src/test/resources/in/NestedInterfaceWithExtension.java b/modules/auto-record-tests/src/test/resources/in/NestedInterfaceWithExtension.java index 9836708..c0b4e36 100644 --- a/modules/auto-record-tests/src/test/resources/in/NestedInterfaceWithExtension.java +++ b/modules/auto-record-tests/src/test/resources/in/NestedInterfaceWithExtension.java @@ -18,14 +18,13 @@ import pl.com.labaj.autorecord.AutoRecord; import pl.com.labaj.autorecord.Memoized; -import pl.com.labaj.autorecord.test.extension.compact.LoggingExtension; public class NestedInterfaceWithExtension { static class Intermediate { @AutoRecord @AutoRecord.Options(memoizedHashCode = true) - @AutoRecord.Extension(extensionClass = LoggingExtension.class) + @AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.test.extension.compact.LoggingExtension") public interface Internal { String name(); diff --git a/modules/auto-record-tests/src/test/resources/in/WithCompactConstructorExtensions.java b/modules/auto-record-tests/src/test/resources/in/WithCompactConstructorExtensions.java index 84a3dc2..08c86a8 100644 --- a/modules/auto-record-tests/src/test/resources/in/WithCompactConstructorExtensions.java +++ b/modules/auto-record-tests/src/test/resources/in/WithCompactConstructorExtensions.java @@ -17,13 +17,11 @@ */ import pl.com.labaj.autorecord.AutoRecord; -import pl.com.labaj.autorecord.test.extension.compact.AlwaysCompactConstructorExtension; -import pl.com.labaj.autorecord.test.extension.compact.WhenProcessorCompactConstructorExtension; @AutoRecord @AutoRecord.Options(memoizedToString = true) -@AutoRecord.Extension(extensionClass = AlwaysCompactConstructorExtension.class, parameters = "Alpha") -@AutoRecord.Extension(extensionClass = WhenProcessorCompactConstructorExtension.class) +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.test.extension.compact.AlwaysCompactConstructorExtension", parameters = "Alpha") +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.test.extension.compact.WhenProcessorCompactConstructorExtension") interface WithCompactConstructorExtensions { String property(); } \ No newline at end of file diff --git a/modules/auto-record-tests/src/test/resources/in/WithOnlyCompactConstructorExtension.java b/modules/auto-record-tests/src/test/resources/in/WithOnlyCompactConstructorExtension.java index c9fd669..9713ab3 100644 --- a/modules/auto-record-tests/src/test/resources/in/WithOnlyCompactConstructorExtension.java +++ b/modules/auto-record-tests/src/test/resources/in/WithOnlyCompactConstructorExtension.java @@ -17,14 +17,12 @@ */ import pl.com.labaj.autorecord.AutoRecord; -import pl.com.labaj.autorecord.test.extension.compact.AlwaysCompactConstructorExtension; -import pl.com.labaj.autorecord.test.extension.compact.WhenProcessorCompactConstructorExtension; import javax.annotation.Nullable; @AutoRecord -@AutoRecord.Extension(extensionClass = AlwaysCompactConstructorExtension.class, parameters = {"Alpha", "Bravo"}) -@AutoRecord.Extension(extensionClass = WhenProcessorCompactConstructorExtension.class) +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.test.extension.compact.AlwaysCompactConstructorExtension", parameters = {"Alpha", "Bravo"}) +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.test.extension.compact.WhenProcessorCompactConstructorExtension") interface WithOnlyCompactConstructorExtension { @Nullable String property(); diff --git a/modules/auto-record-utils/README.md b/modules/auto-record-utils/README.md new file mode 100644 index 0000000..68c8f01 --- /dev/null +++ b/modules/auto-record-utils/README.md @@ -0,0 +1,34 @@ +# Auto Record Utilities + +Provides classes used for memoization of methods in generated record. + +Please, see [WIKI](https://github.com/pawellabaj/auto-record/wiki) for information. + +## Usage + +If you use annotation processing in your project, the library will be provided as a dependency of [Auto Record](https://github.com/pawellabaj/auto-record/tree/main/modules/auto-record). + +The classes in this package can also be used independently to add memoization capabilities to your own classes. +In such case, use `auto-record-utils` directly. + +### Maven + +Add the following dependency to your `pom.xml` file: + +```xml + + + pl.com.labaj.autorecord + auto-record-utils + ${auto-record.version} + +``` + +### Gradle + +Declare the following dependency in your `build.gradle` script: + +```groovy +dependencies { + annotationProcessor 'pl.com.labaj.autorecord:auto-record-utils:${autoRecordVersion}' +} \ No newline at end of file diff --git a/modules/auto-record-utils/pom.xml b/modules/auto-record-utils/pom.xml new file mode 100644 index 0000000..8e78610 --- /dev/null +++ b/modules/auto-record-utils/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + pl.com.labaj.autorecord + auto-record-project + 2.1.1-SNAPSHOT + ../../pom.xml + + + Auto Record Utilities + auto-record-utils + + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + test + + + \ No newline at end of file diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/BooleanMemoizer.java b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/BooleanMemoizer.java similarity index 94% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/BooleanMemoizer.java rename to modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/BooleanMemoizer.java index a19c9b5..5f43e38 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/BooleanMemoizer.java +++ b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/BooleanMemoizer.java @@ -16,8 +16,12 @@ * limitations under the License. */ +import org.apiguardian.api.API; + import java.util.function.BooleanSupplier; +import static org.apiguardian.api.API.Status.STABLE; + /** * Memoizer is a thread-safe utility class that allows for the memoization of a single value using the double-check idiom. * @@ -27,6 +31,7 @@ * * @see Memoization Wiki */ +@API(status = STABLE) public final class BooleanMemoizer { /** diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/ByteMemoizer.java b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/ByteMemoizer.java similarity index 94% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/ByteMemoizer.java rename to modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/ByteMemoizer.java index dfbab24..0808f80 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/ByteMemoizer.java +++ b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/ByteMemoizer.java @@ -16,6 +16,10 @@ * limitations under the License. */ +import org.apiguardian.api.API; + +import static org.apiguardian.api.API.Status.STABLE; + /** * Memoizer is a thread-safe utility class that allows for the memoization of a single value using the double-check idiom. * @@ -25,6 +29,7 @@ * * @see Memoization Wiki */ +@API(status = STABLE) public final class ByteMemoizer { /** diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/ByteSupplier.java b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/ByteSupplier.java similarity index 92% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/ByteSupplier.java rename to modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/ByteSupplier.java index 5b1b051..40947a3 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/ByteSupplier.java +++ b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/ByteSupplier.java @@ -16,8 +16,12 @@ * limitations under the License. */ +import org.apiguardian.api.API; + import java.util.function.Supplier; +import static org.apiguardian.api.API.Status.STABLE; + /** * Represents a supplier of {@code byte}-valued results. This is the * {@code byte}-producing primitive specialization of {@link Supplier}. @@ -32,6 +36,7 @@ * @since 1.8 */ @FunctionalInterface +@API(status = STABLE) public interface ByteSupplier { /** * Gets a {@code byte}-valued result. diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/CharMemoizer.java b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/CharMemoizer.java similarity index 94% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/CharMemoizer.java rename to modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/CharMemoizer.java index 677a909..f72a909 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/CharMemoizer.java +++ b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/CharMemoizer.java @@ -16,6 +16,10 @@ * limitations under the License. */ +import org.apiguardian.api.API; + +import static org.apiguardian.api.API.Status.STABLE; + /** * Memoizer is a thread-safe utility class that allows for the memoization of a single value using the double-check idiom. * @@ -25,6 +29,7 @@ * * @see Memoization Wiki */ +@API(status = STABLE) public final class CharMemoizer { /** diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/CharSupplier.java b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/CharSupplier.java similarity index 92% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/CharSupplier.java rename to modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/CharSupplier.java index 9d9efa9..6e91e5b 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/CharSupplier.java +++ b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/CharSupplier.java @@ -16,8 +16,12 @@ * limitations under the License. */ +import org.apiguardian.api.API; + import java.util.function.Supplier; +import static org.apiguardian.api.API.Status.STABLE; + /** * Represents a supplier of {@code char}-valued results. This is the * {@code char}-producing primitive specialization of {@link Supplier}. @@ -32,6 +36,7 @@ * @since 1.8 */ @FunctionalInterface +@API(status = STABLE) public interface CharSupplier { /** * Gets a {@code char}-valued result. diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/DoubleMemoizer.java b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/DoubleMemoizer.java similarity index 94% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/DoubleMemoizer.java rename to modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/DoubleMemoizer.java index e703675..34ef12e 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/DoubleMemoizer.java +++ b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/DoubleMemoizer.java @@ -16,8 +16,12 @@ * limitations under the License. */ +import org.apiguardian.api.API; + import java.util.function.DoubleSupplier; +import static org.apiguardian.api.API.Status.STABLE; + /** * Memoizer is a thread-safe utility class that allows for the memoization of a single value using the double-check idiom. * @@ -27,6 +31,7 @@ * * @see Memoization Wiki */ +@API(status = STABLE) public final class DoubleMemoizer { /** diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/FloatMemoizer.java b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/FloatMemoizer.java similarity index 94% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/FloatMemoizer.java rename to modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/FloatMemoizer.java index 265e531..960c590 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/FloatMemoizer.java +++ b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/FloatMemoizer.java @@ -16,6 +16,10 @@ * limitations under the License. */ +import org.apiguardian.api.API; + +import static org.apiguardian.api.API.Status.STABLE; + /** * Memoizer is a thread-safe utility class that allows for the memoization of a single value using the double-check idiom. * @@ -25,6 +29,7 @@ * * @see Memoization Wiki */ +@API(status = STABLE) public final class FloatMemoizer { /** diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/FloatSupplier.java b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/FloatSupplier.java similarity index 92% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/FloatSupplier.java rename to modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/FloatSupplier.java index 10804b9..55e1b94 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/FloatSupplier.java +++ b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/FloatSupplier.java @@ -16,8 +16,12 @@ * limitations under the License. */ +import org.apiguardian.api.API; + import java.util.function.Supplier; +import static org.apiguardian.api.API.Status.STABLE; + /** * Represents a supplier of {@code float}-valued results. This is the * {@code float}-producing primitive specialization of {@link Supplier}. @@ -32,6 +36,7 @@ * @since 1.8 */ @FunctionalInterface +@API(status = STABLE) public interface FloatSupplier { /** * Gets a {@code float}-valued result. diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/IntMemoizer.java b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/IntMemoizer.java similarity index 94% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/IntMemoizer.java rename to modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/IntMemoizer.java index 2a975dc..d0b035d 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/IntMemoizer.java +++ b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/IntMemoizer.java @@ -16,8 +16,12 @@ * limitations under the License. */ +import org.apiguardian.api.API; + import java.util.function.IntSupplier; +import static org.apiguardian.api.API.Status.STABLE; + /** * Memoizer is a thread-safe utility class that allows for the memoization of a single value using the double-check idiom. * @@ -27,6 +31,7 @@ * * @see Memoization Wiki */ +@API(status = STABLE) public final class IntMemoizer { /** diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/LongMemoizer.java b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/LongMemoizer.java similarity index 94% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/LongMemoizer.java rename to modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/LongMemoizer.java index 0e81a32..beb46c3 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/LongMemoizer.java +++ b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/LongMemoizer.java @@ -16,8 +16,12 @@ * limitations under the License. */ +import org.apiguardian.api.API; + import java.util.function.LongSupplier; +import static org.apiguardian.api.API.Status.STABLE; + /** * Memoizer is a thread-safe utility class that allows for the memoization of a single value using the double-check idiom. * @@ -27,6 +31,7 @@ * * @see Memoization Wiki */ +@API(status = STABLE) public final class LongMemoizer { /** diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/Memoizer.java b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/Memoizer.java similarity index 94% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/Memoizer.java rename to modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/Memoizer.java index e0d84fc..fc58efd 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/Memoizer.java +++ b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/Memoizer.java @@ -16,8 +16,12 @@ * limitations under the License. */ +import org.apiguardian.api.API; + import java.util.function.Supplier; +import static org.apiguardian.api.API.Status.STABLE; + /** * Memoizer is a thread-safe utility class that allows for the memoization of a single value using the double-check idiom. * @@ -27,6 +31,7 @@ * @param the type of value being memoized * @see Memoization Wiki */ +@API(status = STABLE) public final class Memoizer { /** * Indicates whether the memoized value has already been computed diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/ShortMemoizer.java b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/ShortMemoizer.java similarity index 94% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/ShortMemoizer.java rename to modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/ShortMemoizer.java index 6ff7e63..71493b0 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/ShortMemoizer.java +++ b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/ShortMemoizer.java @@ -16,6 +16,10 @@ * limitations under the License. */ +import org.apiguardian.api.API; + +import static org.apiguardian.api.API.Status.STABLE; + /** * Memoizer is a thread-safe utility class that allows for the memoization of a single value using the double-check idiom. * @@ -25,6 +29,7 @@ * * @see Memoization Wiki */ +@API(status = STABLE) public final class ShortMemoizer { /** diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/ShortSupplier.java b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/ShortSupplier.java similarity index 92% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/ShortSupplier.java rename to modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/ShortSupplier.java index c54e09a..005623e 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/ShortSupplier.java +++ b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/ShortSupplier.java @@ -16,8 +16,12 @@ * limitations under the License. */ +import org.apiguardian.api.API; + import java.util.function.Supplier; +import static org.apiguardian.api.API.Status.STABLE; + /** * Represents a supplier of {@code short}-valued results. This is the * {@code short}-producing primitive specialization of {@link Supplier}. @@ -32,6 +36,7 @@ * @since 1.8 */ @FunctionalInterface +@API(status = STABLE) public interface ShortSupplier { /** * Gets a {@code short}-valued result. diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/package-info.java b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/package-info.java similarity index 85% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/package-info.java rename to modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/package-info.java index cb915cc..7e97f8c 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/memoizer/package-info.java +++ b/modules/auto-record-utils/src/main/java/pl/com/labaj/autorecord/memoizer/package-info.java @@ -21,9 +21,4 @@ * * @see Wiki */ -@API(status = STABLE) -package pl.com.labaj.autorecord.memoizer; - -import org.apiguardian.api.API; - -import static org.apiguardian.api.API.Status.STABLE; \ No newline at end of file +package pl.com.labaj.autorecord.memoizer; \ No newline at end of file diff --git a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/BooleanMemoizerTest.java b/modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/BooleanMemoizerTest.java similarity index 100% rename from modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/BooleanMemoizerTest.java rename to modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/BooleanMemoizerTest.java diff --git a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/ByteMemoizerTest.java b/modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/ByteMemoizerTest.java similarity index 100% rename from modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/ByteMemoizerTest.java rename to modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/ByteMemoizerTest.java diff --git a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/CharMemoizerTest.java b/modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/CharMemoizerTest.java similarity index 100% rename from modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/CharMemoizerTest.java rename to modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/CharMemoizerTest.java diff --git a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/DoubleMemoizerTest.java b/modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/DoubleMemoizerTest.java similarity index 100% rename from modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/DoubleMemoizerTest.java rename to modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/DoubleMemoizerTest.java diff --git a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/FloatMemoizerTest.java b/modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/FloatMemoizerTest.java similarity index 100% rename from modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/FloatMemoizerTest.java rename to modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/FloatMemoizerTest.java diff --git a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/IntMemoizerTest.java b/modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/IntMemoizerTest.java similarity index 100% rename from modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/IntMemoizerTest.java rename to modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/IntMemoizerTest.java diff --git a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/LongMemoizerTest.java b/modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/LongMemoizerTest.java similarity index 100% rename from modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/LongMemoizerTest.java rename to modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/LongMemoizerTest.java diff --git a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/MemoizerTest.java b/modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/MemoizerTest.java similarity index 100% rename from modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/MemoizerTest.java rename to modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/MemoizerTest.java diff --git a/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/ShortMemoizerTest.java b/modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/ShortMemoizerTest.java similarity index 100% rename from modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/memoizer/ShortMemoizerTest.java rename to modules/auto-record-utils/src/test/java/pl/com/labaj/autorecord/memoizer/ShortMemoizerTest.java diff --git a/modules/auto-record/.skip-annotation-processing b/modules/auto-record/.skip-annotation-processing new file mode 100644 index 0000000..e69de29 diff --git a/modules/auto-record/README.md b/modules/auto-record/README.md new file mode 100644 index 0000000..a214a27 --- /dev/null +++ b/modules/auto-record/README.md @@ -0,0 +1,5 @@ +# Auto Record + +Annotation processor to generate records. + +Please, see [main project](https://github.com/pawellabaj/auto-record/tree/main) or [WIKI](https://github.com/pawellabaj/auto-record/wiki) for information. \ No newline at end of file diff --git a/modules/auto-record/pom.xml b/modules/auto-record/pom.xml index 9bc79fa..1d48260 100644 --- a/modules/auto-record/pom.xml +++ b/modules/auto-record/pom.xml @@ -4,19 +4,27 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - pl.com.labaj + pl.com.labaj.autorecord auto-record-project - 2.0.1-SNAPSHOT + 2.1.1-SNAPSHOT ../../pom.xml + Auto Record auto-record - - - false - + https://github.com/pawellabaj/auto-record + + pl.com.labaj.autorecord + auto-record-api + 2.1.1-SNAPSHOT + + + pl.com.labaj.autorecord + auto-record-utils + 2.1.1-SNAPSHOT + net.fabricmc javapoet @@ -25,10 +33,6 @@ io.soabase.record-builder record-builder-processor - - io.soabase.record-builder - record-builder-core - org.apache.commons commons-lang3 @@ -37,9 +41,5 @@ com.google.code.findbugs jsr305 - - org.apiguardian - apiguardian-api - \ No newline at end of file diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/CompactConstructorExtension.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/CompactConstructorExtension.java deleted file mode 100644 index fdfa12e..0000000 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/CompactConstructorExtension.java +++ /dev/null @@ -1,70 +0,0 @@ -package pl.com.labaj.autorecord.extension; - -/*- - * Copyright © 2023 Auto Record - * - * 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. - */ - -import com.squareup.javapoet.CodeBlock; -import pl.com.labaj.autorecord.context.Context; -import pl.com.labaj.autorecord.context.StaticImports; -import pl.com.labaj.autorecord.processor.AutoRecordProcessor; - -/** - * Represents an extension of the {@link AutoRecordProcessor} for customizing the compact constructor generation process. - *

- * Implement this interface to provide custom behavior for compact constructor generation. - * - * @since 2.1.0 - */ -public interface CompactConstructorExtension extends AutoRecordExtension { - /** - * Determines whether the extension should generate the compact constructor content. - * - * @param isGeneratedByProcessor a flag indicating if the compact constructor is generated by the - * {@link AutoRecordProcessor}. - * If the flag is {@code false}, it means that compact constructor won't be generated without extensions. - * @param context the {@link Context} object containing of the annotation processing. - * @return {@code} if the compact constructor should be generated by the extension, {@code false} otherwise. - */ - default boolean shouldGenerate(boolean isGeneratedByProcessor, Context context) { - return isGeneratedByProcessor; - } - - /** - * Provides a code to be inserted before the current content of the compact constructor. - * - * @param context the {@link Context} object containing of the annotation processing. - * @param staticImports the {@link StaticImports} object {@code static import} to statements that will be added into - * generated record. - * @return a {@link CodeBlock} representing the code to be inserted before the current content of the compact constructor. - * @see CodeBlock - */ - default CodeBlock prefixCompactConstructorContent(Context context, StaticImports staticImports) { - return null; - } - - /** - * Provides a code to be inserted after the current content of the compact constructor. - * - * @param context the {@link Context} object containing of the annotation processing. - * @param staticImports the {@link StaticImports} object {@code static import} to statements that will be added into - * generated record. - * @return a {@link CodeBlock} representing the code to be inserted after the current content of the compact constructor. - * @see CodeBlock - */ - default CodeBlock suffixCompactConstructorContent(Context context, StaticImports staticImports) { - return null; - } -} diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/AutoRecordProcessor.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/AutoRecordProcessor.java index 3fa2618..e409a67 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/AutoRecordProcessor.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/AutoRecordProcessor.java @@ -19,6 +19,7 @@ import io.soabase.recordbuilder.core.RecordBuilder; import org.apiguardian.api.API; import pl.com.labaj.autorecord.AutoRecord; +import pl.com.labaj.autorecord.context.Logger; import pl.com.labaj.autorecord.processor.context.ContextBuilder; import pl.com.labaj.autorecord.processor.context.MessagerLogger; @@ -64,8 +65,8 @@ public class AutoRecordProcessor extends AbstractProcessor { public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); - extensionsInitializer = new ExtensionsInitializer(); - contextBuilder = new ContextBuilder(processingEnv.getElementUtils()); + extensionsInitializer = new ExtensionsInitializer(processingEnv); + contextBuilder = new ContextBuilder(processingEnv); recordGenerator = new RecordJavaFileBuilder(); } @@ -151,10 +152,10 @@ private void processElement(TypeElement sourceInterface, } } - private void logStartEnd(String prefix, TypeElement sourceInterface, MessagerLogger logger) { + private void logStartEnd(String prefix, TypeElement sourceInterface, Logger logger) { if (logger.isDebugEnabled()) { var message = rightPad(prefix + sourceInterface.getQualifiedName() + " ", 100, "-"); - logger.info(message); + logger.note(message); } } } diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/ExtensionsInitializer.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/ExtensionsInitializer.java index 56d2afb..29f05fc 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/ExtensionsInitializer.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/ExtensionsInitializer.java @@ -20,58 +20,55 @@ import pl.com.labaj.autorecord.context.Logger; import pl.com.labaj.autorecord.extension.AutoRecordExtension; -import javax.lang.model.type.MirroredTypeException; +import javax.annotation.processing.ProcessingEnvironment; import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.TreeSet; class ExtensionsInitializer { + private final ProcessingEnvironment processingEnv; + private final Map extensions = new HashMap<>(); + + ExtensionsInitializer(ProcessingEnvironment processingEnv) { + this.processingEnv = processingEnv; + } List initExtensions(List extensionAnnotations, Logger logger) { return extensionAnnotations.stream() - .map(extendWith -> initExtension(extendWith, logger)) + .map(extensionAnnotation -> initExtension(extensionAnnotation, logger)) .toList(); } private AutoRecordExtension initExtension(AutoRecord.Extension extensionAnnotation, Logger logger) { - var extensionClass = loadExtensionClass(extensionAnnotation, logger); - var extension = instantiateExtension(extensionClass); - extension.setParameters(extensionAnnotation.parameters()); - - return extension; - } + var className = extensionAnnotation.extensionClass(); + var parameters = extensionAnnotation.parameters(); - private Class loadExtensionClass(AutoRecord.Extension extensionAnnotation, Logger logger) { - Class extensionClass; - try { - extensionClass = extensionAnnotation.extensionClass(); - logger.debug("Class from annotation: " + extensionClass); - } catch (MirroredTypeException e) { - var className = e.getTypeMirror().toString(); - logger.debug("Class name from typeMirror: " + className); + logger.debug("Init " + className + " with " + Arrays.toString(parameters)); - try { - var aClass = Class.forName(className); - extensionClass = getExtensionClass(aClass); - } catch (ClassNotFoundException ex) { - throw new AutoRecordProcessorException("Cannot load class " + className, ex); - } - } + return extensions.computeIfAbsent(extensionKey(className, parameters), key -> { + var extension = instantiateExtension(className); + extension.init(processingEnv, parameters); - return extensionClass; + return extension; + }); } - @SuppressWarnings("unchecked") - private Class getExtensionClass(Class aClass) { - if (AutoRecordExtension.class.isAssignableFrom(aClass)) { - return (Class) aClass; - } - throw new AutoRecordProcessorException("Class " + aClass.getName() + " does not extend " + AutoRecordExtension.class.getName()); + private int extensionKey(String className, String[] parameters) { + var sortedParameters = new TreeSet<>(Arrays.asList(parameters)); + return Objects.hash(className, sortedParameters); } - private AutoRecordExtension instantiateExtension(Class extensionClass) { + private AutoRecordExtension instantiateExtension(String className) { try { - return extensionClass.getDeclaredConstructor().newInstance(); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + var extensionClass = Class.forName(className); + var constructor = extensionClass.getDeclaredConstructor(); + + return (AutoRecordExtension) constructor.newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e) { throw new AutoRecordProcessorException("Can't instantiate extension " + e.getLocalizedMessage()); } } diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/RecordJavaFileBuilder.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/RecordJavaFileBuilder.java index 5c1f9ac..d6fabec 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/RecordJavaFileBuilder.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/RecordJavaFileBuilder.java @@ -18,16 +18,14 @@ import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.TypeSpec; -import org.apiguardian.api.API; import pl.com.labaj.autorecord.extension.AutoRecordExtension; import pl.com.labaj.autorecord.processor.context.ProcessorContext; +import pl.com.labaj.autorecord.processor.context.StaticImportsCollectors; import java.util.List; -import static org.apiguardian.api.API.Status.INTERNAL; import static pl.com.labaj.autorecord.processor.generator.Generators.generators; -@API(status = INTERNAL) class RecordJavaFileBuilder { JavaFile buildJavaFile(ProcessorContext context, List extensions) { diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/ComponentsFinder.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/ComponentsFinder.java index 68c8dec..a2d3a26 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/ComponentsFinder.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/ComponentsFinder.java @@ -17,6 +17,7 @@ */ import pl.com.labaj.autorecord.context.RecordComponent; +import pl.com.labaj.autorecord.processor.AutoRecordProcessorException; import pl.com.labaj.autorecord.processor.utils.Methods; import javax.lang.model.element.ExecutableElement; @@ -24,9 +25,13 @@ import java.util.function.Predicate; import static java.lang.annotation.ElementType.TYPE_PARAMETER; +import static javax.lang.model.type.TypeKind.ERROR; import static pl.com.labaj.autorecord.processor.utils.Annotations.annotationsAllowedFor; class ComponentsFinder { + + public static final String ERROR_INDICATOR = ""; + List getComponents(List allMethods, Predicate isNotSpecial) { return allMethods.stream() .filter(Methods::isAbstract) @@ -39,7 +44,15 @@ List getComponents(List allMethods, Predicat } private RecordComponent toRecordComponent(ExecutableElement method) { - var type = method.getReturnType(); + var returnType = method.getReturnType(); + + if (returnType.getKind() == ERROR && returnType.toString().equals(ERROR_INDICATOR)) { + throw new AutoRecordProcessorException("Cannot infer type of " + method.getSimpleName() + "() method. " + + "Probably it is generic and not in classpath or sourcepath yet. " + + "Try to move the type class into classpath or remove generic clause from " + method.getSimpleName() + "() method."); + } + + var type = returnType; var name = method.getSimpleName().toString(); var annotations = annotationsAllowedFor(method.getAnnotationMirrors(), TYPE_PARAMETER); diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/ContextBuilder.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/ContextBuilder.java index 1f7fd7d..bff56c8 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/ContextBuilder.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/ContextBuilder.java @@ -22,10 +22,10 @@ import pl.com.labaj.autorecord.processor.utils.Methods; import javax.annotation.Nullable; +import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; -import javax.lang.model.util.Elements; import java.util.List; import java.util.Map; @@ -33,7 +33,7 @@ import static pl.com.labaj.autorecord.processor.utils.Annotations.createAnnotationIfNeeded; public class ContextBuilder { - private static final String IMMUTABLE_COLLECTIONS_EXTENSION = "pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension"; + private static final String IMMUTABLE_COLLECTIONS_EXTENSION = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension"; private static final Map DEFAULT_BUILDER_OPTIONS_ENFORCED_VALUES = Map.of("addClassRetainedGenerated", true); private static final Map WITH_IMMUTABLE_COLLECTIONS_BUILDER_OPTIONS_ENFORCED_VALUES = Map.of( "addClassRetainedGenerated", true, @@ -41,13 +41,13 @@ public class ContextBuilder { "useUnmodifiableCollections", false ); - private final Elements elementUtils; + private final ProcessingEnvironment processingEnv; private final MemoizationFinder memoizationFinder = new MemoizationFinder(); private final SpecialMethodsFinder specialMethodsFinder = new SpecialMethodsFinder(); private final ComponentsFinder componentsFinder = new ComponentsFinder(); - public ContextBuilder(Elements elementUtils) { - this.elementUtils = elementUtils; + public ContextBuilder(ProcessingEnvironment processingEnv) { + this.processingEnv = processingEnv; } public ProcessorContext buildContext(TypeElement sourceInterface, @@ -58,7 +58,7 @@ public ProcessorContext buildContext(TypeElement sourceInterface, var nonNullRecordOptions = createAnnotationIfNeeded(recordOptions, AutoRecord.Options.class); var nonNullBuilderOptions = createAnnotationIfNeeded(builderOptions, RecordBuilder.Options.class, getBuilderOptionsEnforcedValues(extensions)); - var allMethods = elementUtils.getAllMembers(sourceInterface).stream() + var allMethods = processingEnv.getElementUtils().getAllMembers(sourceInterface).stream() .filter(Methods::isMethod) .map(ExecutableElement.class::cast) .toList(); @@ -70,7 +70,8 @@ public ProcessorContext buildContext(TypeElement sourceInterface, var memoizationItems = memoizationFinder.findMemoizationItems(allMethods, nonNullRecordOptions, specialMethodsFinder::isSpecial); var components = componentsFinder.getComponents(allMethods, specialMethodsFinder::isNotSpecial); - return new ProcessorContext(getPackageName(sourceInterface), + return new ProcessorContext(processingEnv, + getPackageName(sourceInterface), nonNullRecordOptions, nonNullBuilderOptions, isPublic, @@ -100,7 +101,7 @@ private List getTypeParameters(TypeElement sourceInterface } private String getPackageName(TypeElement sourceInterface) { - return elementUtils.getPackageOf(sourceInterface).getQualifiedName().toString(); + return processingEnv.getElementUtils().getPackageOf(sourceInterface).getQualifiedName().toString(); } private String getInterfaceName(TypeElement sourceInterface) { diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/MessagerLogger.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/MessagerLogger.java index 51a853d..97f9f81 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/MessagerLogger.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/MessagerLogger.java @@ -50,12 +50,12 @@ public boolean isDebugEnabled() { @Override public void debug(String message) { if (isDebugEnabled()) { - info("[DEBUG] " + message); + note("[DEBUG] " + message); } } @Override - public void info(String message) { + public void note(String message) { printMessage(NOTE, message); } diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/ProcessorContext.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/ProcessorContext.java index 0e5bb47..b213b02 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/ProcessorContext.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/ProcessorContext.java @@ -22,6 +22,7 @@ import pl.com.labaj.autorecord.context.Logger; import pl.com.labaj.autorecord.context.RecordComponent; +import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.type.TypeMirror; @@ -29,7 +30,8 @@ import java.util.Map; import java.util.Optional; -public record ProcessorContext(String packageName, +public record ProcessorContext(ProcessingEnvironment processingEnv, + String packageName, AutoRecord.Options recordOptions, RecordBuilder.Options builderOptions, boolean isRecordPublic, diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/StaticImportsCollectors.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/StaticImportsCollectors.java similarity index 92% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/StaticImportsCollectors.java rename to modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/StaticImportsCollectors.java index 68bf375..ff8ad21 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/StaticImportsCollectors.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/context/StaticImportsCollectors.java @@ -1,4 +1,4 @@ -package pl.com.labaj.autorecord.processor; +package pl.com.labaj.autorecord.processor.context; /*- * Copyright © 2023 Auto Record @@ -29,7 +29,7 @@ import static org.apiguardian.api.API.Status.INTERNAL; @API(status = INTERNAL) -class StaticImportsCollectors implements pl.com.labaj.autorecord.context.StaticImports { +public class StaticImportsCollectors implements pl.com.labaj.autorecord.context.StaticImports { final Map> statements = new HashMap<>(); diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/generator/CompactConstructorSubGenerator.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/generator/CompactConstructorSubGenerator.java index 7412612..295c3e2 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/generator/CompactConstructorSubGenerator.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/generator/CompactConstructorSubGenerator.java @@ -16,6 +16,7 @@ * limitations under the License. */ +import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeSpec; @@ -25,15 +26,19 @@ import pl.com.labaj.autorecord.processor.AutoRecordProcessor; import pl.com.labaj.autorecord.processor.context.MemoizerType; import pl.com.labaj.autorecord.processor.context.ProcessorContext; +import pl.com.labaj.autorecord.processor.utils.Annotations; import javax.annotation.Nullable; +import javax.annotation.processing.Generated; import javax.lang.model.element.Modifier; +import java.util.Collection; import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; +import java.util.stream.Stream; import static com.squareup.javapoet.MethodSpec.constructorBuilder; import static java.util.Objects.nonNull; @@ -63,7 +68,7 @@ void generate(TypeSpec.Builder recordBuilder, StaticImports staticImports) { var generatedByProcessor = !nonNullNames.isEmpty() || !memoization.isEmpty(); var filteredExtensions = extensions.stream() - .filter(extension -> extension.shouldGenerate(generatedByProcessor, context)) + .filter(extension -> extension.shouldGenerateCompactConstructor(generatedByProcessor, context)) .toList(); if (!generatedByProcessor && filteredExtensions.isEmpty()) { @@ -73,6 +78,37 @@ void generate(TypeSpec.Builder recordBuilder, StaticImports staticImports) { var compactConstructor = generateCompactConstructor(staticImports, generatedByProcessor, filteredExtensions, nonNullNames); recordBuilder.compactConstructor(compactConstructor); + + filteredExtensions.stream() + .flatMap(extension -> additionalMethods(extension, staticImports)) + .forEach(recordBuilder::addMethod); + + filteredExtensions.stream() + .map(extension -> extension.annotationsToSupportCompactConstructor(context, staticImports)) + .map(annotations -> { + if (recordBuilder.annotations.isEmpty()) { + return annotations; + } + + var currentAnnotations = List.copyOf(recordBuilder.annotations); + recordBuilder.annotations.clear(); + return Annotations.merge(currentAnnotations, annotations); + }) + .flatMap(Collection::stream) + .forEach(recordBuilder::addAnnotation); + } + + private Stream additionalMethods(CompactConstructorExtension extension, StaticImports staticImports) { + var annotation = AnnotationSpec.builder(Generated.class) + .addMember("value", "$S", extension.getClass().getName()) + .build(); + + var additionalMethods = extension.additionalMethodsToSupportCompactConstructor(context, staticImports); + + return additionalMethods.stream() + .map(methodSpec -> methodSpec.toBuilder() + .addAnnotation(annotation) + .build()); } private MethodSpec generateCompactConstructor(StaticImports staticImports, diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/generator/Generators.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/generator/Generators.java index ce2c11c..bf8d51b 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/generator/Generators.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/generator/Generators.java @@ -16,12 +16,16 @@ * limitations under the License. */ +import org.apiguardian.api.API; import pl.com.labaj.autorecord.extension.AutoRecordExtension; import pl.com.labaj.autorecord.processor.context.ProcessorContext; import java.util.List; import java.util.function.BiFunction; +import static org.apiguardian.api.API.Status.INTERNAL; + +@API(status = INTERNAL) public final class Generators { private static final List, RecordGenerator>> GENERATOR_CONSTRUCTORS = List.of( BasicGenerator::new, diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/utils/Annotations.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/utils/Annotations.java index 79f1748..324084b 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/utils/Annotations.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/utils/Annotations.java @@ -18,6 +18,9 @@ import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.CodeBlock; +import org.apiguardian.api.API; +import pl.com.labaj.autorecord.processor.AutoRecordProcessorException; import javax.annotation.Nullable; import javax.lang.model.element.AnnotationMirror; @@ -26,18 +29,24 @@ import javax.lang.model.type.DeclaredType; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; import java.lang.annotation.Target; import java.lang.reflect.Proxy; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Stream; import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; import static java.util.function.Predicate.not; +import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.toSet; +import static org.apiguardian.api.API.Status.INTERNAL; +@API(status = INTERNAL) public final class Annotations { private Annotations() {} @@ -123,4 +132,43 @@ private static boolean canAnnotateElementType(TypeElement annotation, ElementTyp .map(elementTypes -> elementTypes.anyMatch(elementType -> elementType == target)) .orElse(true); } + + public static List merge(List annotations1, List annotations2) { + var groupedBy = Stream.concat(annotations1.stream(), annotations2.stream()) + .collect(groupingBy( + annotationSpec -> annotationSpec.type.toString() + + )); + + return groupedBy.entrySet().stream() + .flatMap(entry -> { + try { + var annotationsClassName = entry.getKey(); + Class annotationlass = Class.forName(annotationsClassName); + var repetable = annotationlass.getAnnotation(Repeatable.class); + if (nonNull(repetable)) { + return entry.getValue().stream(); + } + + var annotationBuilder = AnnotationSpec.builder(annotationlass); + + record Member(String name, CodeBlock value){} + var members = entry.getValue().stream() + .map(annotationSpec -> annotationSpec.members) + .map(Map::entrySet) + .flatMap(Collection::stream) + .flatMap(en -> en.getValue() + .stream() + .map(value -> new Member(en.getKey(), value))) + .collect(groupingBy(Member::name, toSet())); + + members.forEach((k, v) -> v.forEach(value -> annotationBuilder.addMember(k, value.value))); + + return Stream.of(annotationBuilder.build()); + } catch (ClassNotFoundException e) { + throw new AutoRecordProcessorException("Cannot merge annptations", e); + } + }) + .toList(); + } } diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/utils/Extensions.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/utils/Extensions.java index 9dc24a7..a5aba6c 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/utils/Extensions.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/utils/Extensions.java @@ -16,10 +16,14 @@ * limitations under the License. */ +import org.apiguardian.api.API; import pl.com.labaj.autorecord.extension.AutoRecordExtension; import java.util.List; +import static org.apiguardian.api.API.Status.INTERNAL; + +@API(status = INTERNAL) public final class Extensions { private Extensions() {} diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/utils/Methods.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/utils/Methods.java index d6e29cd..74ca8f2 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/utils/Methods.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/utils/Methods.java @@ -16,6 +16,8 @@ * limitations under the License. */ +import org.apiguardian.api.API; + import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import java.lang.annotation.Annotation; @@ -23,8 +25,10 @@ import static javax.lang.model.element.ElementKind.METHOD; import static javax.lang.model.element.Modifier.ABSTRACT; import static javax.lang.model.type.TypeKind.VOID; +import static org.apiguardian.api.API.Status.INTERNAL; import static pl.com.labaj.autorecord.processor.utils.Annotations.getAnnotation; +@API(status = INTERNAL) public final class Methods { private Methods() {} diff --git a/modules/doc-examples/pom.xml b/modules/doc-examples/pom.xml index 73d94b9..694db13 100644 --- a/modules/doc-examples/pom.xml +++ b/modules/doc-examples/pom.xml @@ -4,9 +4,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - pl.com.labaj + pl.com.labaj.autorecord auto-record-project - 2.0.1-SNAPSHOT + 2.1.1-SNAPSHOT ../../pom.xml @@ -19,9 +19,21 @@ - pl.com.labaj + pl.com.labaj.autorecord auto-record - ${project.parent.version} + 2.1.1-SNAPSHOT + + + pl.com.labaj.autorecord + arice-extension + 1.0.0-SNAPSHOT + + + + com.google.guava + guava + ${guava.version} + test @@ -41,21 +53,10 @@ release - - - - org.sonatype.plugins - nexus-staging-maven-plugin - true - - - default-deploy - none - - - - - + + true + none + \ No newline at end of file diff --git a/modules/doc-examples/src/main/java/pl/com/labaj/autorecord/extension/compact/IsPersonAdultVerifierExtension.java b/modules/doc-examples/src/main/java/pl/com/labaj/autorecord/extension/compact/IsPersonAdultVerifierExtension.java index 2a82353..8b2a0d1 100644 --- a/modules/doc-examples/src/main/java/pl/com/labaj/autorecord/extension/compact/IsPersonAdultVerifierExtension.java +++ b/modules/doc-examples/src/main/java/pl/com/labaj/autorecord/extension/compact/IsPersonAdultVerifierExtension.java @@ -22,6 +22,8 @@ import pl.com.labaj.autorecord.extension.CompactConstructorExtension; import pl.com.labaj.autorecord.processor.AutoRecordProcessorException; +import javax.annotation.processing.ProcessingEnvironment; + public class IsPersonAdultVerifierExtension implements CompactConstructorExtension { private static final Class RUNTIME_EXCEPTION_CLASS = RuntimeException.class; @@ -29,7 +31,7 @@ public class IsPersonAdultVerifierExtension implements CompactConstructorExtensi @SuppressWarnings("java:S112") @Override - public void setParameters(String[] parameters) { + public void init(ProcessingEnvironment processingEnv, String[] parameters) { if (parameters.length < 1) { exceptionClass = RUNTIME_EXCEPTION_CLASS; } else { diff --git a/modules/doc-examples/src/main/java/pl/com/labaj/autorecord/extension/compact/LoggingExtension.java b/modules/doc-examples/src/main/java/pl/com/labaj/autorecord/extension/compact/LoggingExtension.java index a8a0439..1e676fb 100644 --- a/modules/doc-examples/src/main/java/pl/com/labaj/autorecord/extension/compact/LoggingExtension.java +++ b/modules/doc-examples/src/main/java/pl/com/labaj/autorecord/extension/compact/LoggingExtension.java @@ -22,6 +22,7 @@ import pl.com.labaj.autorecord.context.StaticImports; import pl.com.labaj.autorecord.extension.CompactConstructorExtension; +import javax.annotation.processing.ProcessingEnvironment; import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -33,7 +34,7 @@ public class LoggingExtension implements CompactConstructorExtension { private Level level; @Override - public void setParameters(String[] parameters) { + public void init(ProcessingEnvironment processingEnv, String[] parameters) { if (parameters.length < 1) { level = FINE; } else { @@ -42,7 +43,7 @@ public void setParameters(String[] parameters) { } @Override - public boolean shouldGenerate(boolean isGeneratedByProcessor, Context context) { + public boolean shouldGenerateCompactConstructor(boolean isGeneratedByProcessor, Context context) { return true; } diff --git a/modules/doc-examples/src/test/java/pl/com/labaj/autorecord/AutoRecordWithExtensions.java b/modules/doc-examples/src/test/java/pl/com/labaj/autorecord/AutoRecordWithExtensions.java index 19179e2..932bc70 100644 --- a/modules/doc-examples/src/test/java/pl/com/labaj/autorecord/AutoRecordWithExtensions.java +++ b/modules/doc-examples/src/test/java/pl/com/labaj/autorecord/AutoRecordWithExtensions.java @@ -17,8 +17,6 @@ */ import io.soabase.recordbuilder.core.RecordBuilder; -import pl.com.labaj.autorecord.extension.compact.IsPersonAdultVerifierExtension; -import pl.com.labaj.autorecord.extension.compact.LoggingExtension; import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -32,8 +30,8 @@ recordOptions = @AutoRecord.Options(withBuilder = true), builderOptions = @RecordBuilder.Options(useUnmodifiableCollections = true), extensions = { - @AutoRecord.Extension(extensionClass = LoggingExtension.class, parameters = "info"), - @AutoRecord.Extension(extensionClass = IsPersonAdultVerifierExtension.class) + @AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.compact.LoggingExtension", parameters = "info"), + @AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.compact.IsPersonAdultVerifierExtension") } ) public @interface AutoRecordWithExtensions {} \ No newline at end of file diff --git a/modules/doc-examples/src/test/java/pl/com/labaj/autorecord/CustomAutoRecord.java b/modules/doc-examples/src/test/java/pl/com/labaj/autorecord/CustomAutoRecord.java index ce9eb5a..142002b 100644 --- a/modules/doc-examples/src/test/java/pl/com/labaj/autorecord/CustomAutoRecord.java +++ b/modules/doc-examples/src/test/java/pl/com/labaj/autorecord/CustomAutoRecord.java @@ -17,7 +17,6 @@ */ import io.soabase.recordbuilder.core.RecordBuilder; -import pl.com.labaj.autorecord.extension.compact.LoggingExtension; import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -30,6 +29,6 @@ @AutoRecord.Template( recordOptions = @AutoRecord.Options(withBuilder = true), builderOptions = @RecordBuilder.Options(useUnmodifiableCollections = true), - extensions = @AutoRecord.Extension(extensionClass = LoggingExtension.class) + extensions = @AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.compact.LoggingExtension") ) public @interface CustomAutoRecord {} \ No newline at end of file diff --git a/modules/doc-examples/src/test/java/wiki/customization/singlerecord/Person.java b/modules/doc-examples/src/test/java/wiki/customization/singlerecord/Person.java index dc5d892..5bc76b3 100644 --- a/modules/doc-examples/src/test/java/wiki/customization/singlerecord/Person.java +++ b/modules/doc-examples/src/test/java/wiki/customization/singlerecord/Person.java @@ -18,15 +18,14 @@ import io.soabase.recordbuilder.core.RecordBuilder; import pl.com.labaj.autorecord.AutoRecord; -import pl.com.labaj.autorecord.extension.compact.IsPersonAdultVerifierExtension; -import pl.com.labaj.autorecord.extension.compact.LoggingExtension; @AutoRecord @AutoRecord.Options(withBuilder = true, memoizedHashCode = true, memoizedToString = true) @RecordBuilder.Options(addConcreteSettersForOptional = true) -@AutoRecord.Extension(extensionClass = LoggingExtension.class, parameters = "info") -@AutoRecord.Extension(extensionClass = IsPersonAdultVerifierExtension.class) +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.compact.LoggingExtension", parameters = "info") +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.compact.IsPersonAdultVerifierExtension") interface Person { String name(); + int age(); } \ No newline at end of file diff --git a/modules/doc-examples/src/test/java/wiki/extension/Person.java b/modules/doc-examples/src/test/java/wiki/extension/Person.java index 02cc61c..34d8289 100644 --- a/modules/doc-examples/src/test/java/wiki/extension/Person.java +++ b/modules/doc-examples/src/test/java/wiki/extension/Person.java @@ -16,12 +16,11 @@ */ import pl.com.labaj.autorecord.AutoRecord; -import pl.com.labaj.autorecord.extension.compact.LoggingExtension; import javax.annotation.Nullable; @AutoRecord -@AutoRecord.Extension(extensionClass = LoggingExtension.class, parameters = "info") +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.compact.LoggingExtension", parameters = "info") interface Person { String name(); diff --git a/modules/doc-examples/src/test/java/wiki/extension/arice/Item.java b/modules/doc-examples/src/test/java/wiki/extension/arice/Item.java new file mode 100644 index 0000000..6ce9e3e --- /dev/null +++ b/modules/doc-examples/src/test/java/wiki/extension/arice/Item.java @@ -0,0 +1,33 @@ +package wiki.extension.arice; +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import pl.com.labaj.autorecord.AutoRecord; + +import java.util.Set; + +@AutoRecord +@AutoRecord.Extension( + extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension", + parameters = "wiki.extension.arice.UserImmutableList" +) +interface Item { + String name(); + + Set properties(); + + UserImmutableList rates(); +} \ No newline at end of file diff --git a/modules/doc-examples/src/test/java/wiki/extension/arice/Person.java b/modules/doc-examples/src/test/java/wiki/extension/arice/Person.java new file mode 100644 index 0000000..5ce1604 --- /dev/null +++ b/modules/doc-examples/src/test/java/wiki/extension/arice/Person.java @@ -0,0 +1,41 @@ +package wiki.extension.arice; +/*- + * Copyright © 2023 Auto Record + * + * 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. + */ + +import pl.com.labaj.autorecord.AutoRecord; +import pl.com.labaj.autorecord.extension.arice.Mutable; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@AutoRecord +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension") +interface Person { + String name(); + + @Nullable + String surname(); + + Set strongFeatures(); + + @Nullable + List hobbies(); + + @Mutable + Map experience(); +} \ No newline at end of file diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/utils/package-info.java b/modules/doc-examples/src/test/java/wiki/extension/arice/UserImmutableList.java similarity index 79% rename from modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/utils/package-info.java rename to modules/doc-examples/src/test/java/wiki/extension/arice/UserImmutableList.java index 897d677..62c3455 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/utils/package-info.java +++ b/modules/doc-examples/src/test/java/wiki/extension/arice/UserImmutableList.java @@ -1,5 +1,4 @@ -@API(status = INTERNAL) -package pl.com.labaj.autorecord.processor.utils; +package wiki.extension.arice; /*- * Copyright © 2023 Auto Record @@ -17,6 +16,7 @@ * limitations under the License. */ -import org.apiguardian.api.API; +import java.util.List; -import static org.apiguardian.api.API.Status.INTERNAL; \ No newline at end of file +public interface UserImmutableList extends List { +} diff --git a/modules/doc-examples/src/test/java/wiki/home/Full.java b/modules/doc-examples/src/test/java/wiki/home/Full.java index 34033bf..8e62c1d 100644 --- a/modules/doc-examples/src/test/java/wiki/home/Full.java +++ b/modules/doc-examples/src/test/java/wiki/home/Full.java @@ -19,7 +19,6 @@ import pl.com.labaj.autorecord.AutoRecord; import pl.com.labaj.autorecord.Ignored; import pl.com.labaj.autorecord.Memoized; -import pl.com.labaj.autorecord.extension.compact.LoggingExtension; import javax.annotation.Nullable; import java.util.List; @@ -29,7 +28,7 @@ @AutoRecord @AutoRecord.Options(withBuilder = true, memoizedHashCode = true, memoizedToString = true) @RecordBuilder.Options(suffix = "_Builder") -@AutoRecord.Extension(extensionClass = LoggingExtension.class) +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.compact.LoggingExtension") interface Full> { String name(); diff --git a/modules/mvn-aggregation/pom.xml b/modules/mvn-aggregation/pom.xml index fa6bdec..0b95005 100644 --- a/modules/mvn-aggregation/pom.xml +++ b/modules/mvn-aggregation/pom.xml @@ -4,9 +4,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - pl.com.labaj + pl.com.labaj.autorecord auto-record-project - 2.0.1-SNAPSHOT + 2.1.1-SNAPSHOT ../../pom.xml @@ -23,15 +23,52 @@ - pl.com.labaj + pl.com.labaj.autorecord + auto-record-api + 2.1.1-SNAPSHOT + provided + + + pl.com.labaj.autorecord + auto-record-utils + 2.1.1-SNAPSHOT + provided + + + pl.com.labaj.autorecord auto-record - ${project.parent.version} + 2.1.1-SNAPSHOT provided - pl.com.labaj + pl.com.labaj.autorecord auto-record-tests - ${project.parent.version} + 2.1.1-SNAPSHOT + test + + + + pl.com.labaj.autorecord + arice-api + 1.0.0-SNAPSHOT + provided + + + pl.com.labaj.autorecord + arice-utils + 1.0.0-SNAPSHOT + provided + + + pl.com.labaj.autorecord + arice-extension + 1.0.0-SNAPSHOT + provided + + + pl.com.labaj.autorecord + arice-tests + 1.0.0-SNAPSHOT test @@ -92,5 +129,13 @@ + + release + + + true + deploy + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 87f0364..3114f1a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,19 +3,23 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - Auto Record Java record source generator https://github.com/pawellabaj/auto-record - pl.com.labaj + pl.com.labaj.autorecord auto-record-project - 2.0.1-SNAPSHOT + 2.1.1-SNAPSHOT pom 2023 + modules/auto-record-api + modules/auto-record-utils modules/auto-record modules/auto-record-tests + + arice/project + modules/doc-examples modules/mvn-aggregation @@ -24,7 +28,7 @@ 17 UTF-8 UTF-8 - 2023-07-03T11:23:03Z + 2023-07-21T11:15:27Z 4.2 3.3.1 @@ -34,6 +38,7 @@ 3.3.0 3.1.1 3.1.1 + 3.1.0 3.1.2 0.8.10 3.9.1.2184 @@ -41,6 +46,7 @@ 3.0.1 1.6.13 3.1.0 + 0.1.0 37 3.0.2 @@ -49,21 +55,30 @@ 0.21.0 3.13.0 1.1.2 + 32.1.1-jre --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED - true + + ${maven.multiModuleProjectDirectory}/.build/lic-header.txt + + + org.apiguardian + apiguardian-api + + + - pl.com.labaj - auto-record - ${project.version} + org.apiguardian + apiguardian-api + ${apiguardian-api.version} net.fabricmc @@ -98,9 +113,9 @@ ${commons-lang3.version} - org.apiguardian - apiguardian-api - ${apiguardian-api.version} + com.google.guava + guava + ${guava.version} @@ -134,8 +149,7 @@ - -

${maven.multiModuleProjectDirectory}/.build/lic-header.txt
+
${license-maven-plugin.header}
**/*.java @@ -154,6 +168,7 @@ + org.apache.maven.plugins maven-compiler-plugin @@ -161,33 +176,6 @@ ${java.version} ${java.version} - - - default-compile - compile - - compile - - - none - - -Xlint:unchecked - - - - - default-testCompile - test-compile - - testCompile - - - - -implicit:class - - - - org.apache.maven.plugins @@ -266,6 +254,11 @@ + + org.apache.maven.plugins + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + org.apache.maven.plugins maven-release-plugin @@ -291,11 +284,48 @@ maven-gpg-plugin ${maven-gpg-plugin.version} + + org.honton.chas + git-tag-maven-plugin + 0.0.4 + + + skip-annotation-processing + + false + + .skip-annotation-processing + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + + + + default-compile + + + -proc:none + + + + + + + + verify @@ -303,7 +333,8 @@ https://sonarcloud.io pawellabaj - ${maven.multiModuleProjectDirectory}/target/site/jacoco-aggregate/jacoco.xml + ${maven.multiModuleProjectDirectory}/target/site/jacoco-aggregate/jacoco.xml + ${argLine} @@ -315,6 +346,18 @@ + + org.apache.maven.plugins + maven-enforcer-plugin + + + + [17,) + + + + + org.jacoco jacoco-maven-plugin @@ -349,6 +392,7 @@ true + deploy @@ -371,24 +415,40 @@ true true - pl.com.labaj.autorecord - pl.com.labaj.autorecord.processor.* https://javadoc.io/doc/com.squareup/javapoet/latest https://javadoc.io/doc/io.soabase.record-builder/record-builder-core/${record-builder.version} https://javadoc.io/doc/org.apiguardian/apiguardian-api/${apiguardian-api.version} + https://javadoc.io/doc/com.google.guava/guava/${guava.version} + pl.com.labaj.autorecord + pl.com.labaj.autorecord.processor.*:pl.com.labaj.autorecord.test.* true false false - ${project.build.sourceDirectory} + true + false - attach-javadocs + attach-aggregated-javadocs - jar + aggregate-jar + package + + auto-record-tests,doc-examples,mvn-aggregation,arice-tests + + + Auto Record + pl.com.labaj.autorecord:pl.com.labaj.autorecord.memoizer:pl.com.labaj.autorecord.extension:pl.com.labaj.autorecord.processor:pl.com.labaj.autorecord.context + + + ARICE + pl.com.labaj.autorecord.extension.arice:pl.com.labaj.autorecord.collections + + + @@ -415,11 +475,20 @@ org.apache.maven.plugins maven-release-plugin + clean verify org.honton.chas:git-tag-maven-plugin:tag -Dmaven.test.skip=true [ci skip] v@{project.version} + + org.honton.chas + git-tag-maven-plugin + + true + v${project.version} + + org.apache.maven.plugins maven-deploy-plugin @@ -440,7 +509,7 @@ default-deploy - deploy + ${nexus-staging-maven-plugin.phase} deploy @@ -451,7 +520,6 @@ - Apache-2.0