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..a846e24 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ It provides an easy way to avoid writing repetitive boilerplate code. It generat * [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: diff --git a/extensions/immutable-collections/pom.xml b/extensions/immutable-collections/pom.xml new file mode 100644 index 0000000..815803e --- /dev/null +++ b/extensions/immutable-collections/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + + pl.com.labaj + auto-record-project + + 2.1.1-SNAPSHOT + ../../pom.xml + + + auto-record-immutable-collections + 1.0.0-SNAPSHOT + + + 32.1.1-jre + + ${project.basedir}/../../.build/lic-header.txt + + + + + pl.com.labaj + auto-record + + + + com.google.guava + guava + runtime + + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + test + + + com.google.testing.compile + compile-testing + test + + + + + + + pl.com.labaj + auto-record + ${project.parent.version} + + + com.google.guava + guava + ${guava.version} + + + + + + + + com.mycila + license-maven-plugin + + + +
${license-maven-plugin.header}
+
+
+
+
+
+
+ + + + verify + + + ${project.basedir}/../../target/site/jacoco-aggregate/jacoco.xml + + + +
\ No newline at end of file diff --git a/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/AdditionalMethodsGenerator.java b/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/AdditionalMethodsGenerator.java new file mode 100644 index 0000000..44a243d --- /dev/null +++ b/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/AdditionalMethodsGenerator.java @@ -0,0 +1,220 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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 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 javax.lang.model.element.TypeElement; +import javax.lang.model.util.Types; +import java.util.ArrayDeque; +import java.util.EnumMap; +import java.util.HashSet; +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.collectingAndThen; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toMap; +import static java.util.stream.Collectors.toSet; +import static javax.lang.model.element.Modifier.PRIVATE; +import static javax.lang.model.element.Modifier.STATIC; +import static pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension.PROCESSED_TYPES; +import static pl.com.labaj.autorecord.extension.compact.ProcessedType.allProcessedTypes; + +class AdditionalMethodsGenerator { + private static final EnumMap METHOD_GENERATORS = allProcessedTypes().stream() + .collect(toMap( + identity(), + AdditionalMethodsGenerator::builderFor, + (b1, b2) -> b1, + () -> new EnumMap<>(ProcessedType.class) + )); + + private final Types typeUtils; + + private final StaticImports staticImports; + private final Logger logger; + + AdditionalMethodsGenerator(Types typeUtils, + StaticImports staticImports, + Logger logger) { + this.typeUtils = typeUtils; + this.staticImports = staticImports; + this.logger = logger; + } + + List generateAdditionalMethods(List componentsToProcess, ProcessedTypesStructure structure) { + var pTypesToGenerate = collectPTypesToGenerate(componentsToProcess, structure); + + return ProcessedType.allProcessedTypes().stream() + .filter(pTypesToGenerate::contains) + .sorted(reverseOrder()) + .map(METHOD_GENERATORS::get) + .map(methodGenerator -> methodGenerator.generateMethod(typeUtils, structure, staticImports, logger)) + .toList(); + } + + private HashSet collectPTypesToGenerate(List componentsToProcess, ProcessedTypesStructure structure) { + var pTypesToCheck = componentsToProcess.stream() + .map(RecordComponent::pType) + .collect(collectingAndThen(toSet(), ArrayDeque::new)); + + var pTypesToGenerate = new HashSet(); + + while (!pTypesToCheck.isEmpty()) { + var pType = pTypesToCheck.removeFirst(); + if (structure.needsAdditionalMethod(pType)) { + pTypesToGenerate.add(pType); + } + + pType.directSubTypes().stream() + .filter(subType -> !pTypesToCheck.contains(subType) && !pTypesToGenerate.contains(subType)) + .forEach(pTypesToCheck::addLast); + } + + return pTypesToGenerate; + } + + private static MethodGenerator builderFor(ProcessedType pType) { + return (typeUtils, structure, staticImports, logger) -> { + var methodBuilder = getMethodBuilder(typeUtils, pType); + + immutableTypesBlock(structure, pType) + .ifPresent(methodBuilder::addCode); + subTypesBlocks(pType, structure) + .forEach(methodBuilder::addCode); + + var returnStatement = isNull(pType.factoryClassName()) + ? CodeBlock.of("return $L", pType.argumentName()) + : CodeBlock.of("return $T.$L($L)", pType.factoryClassName(), pType.factoryMethodName(), pType.argumentName()); + methodBuilder.addStatement(returnStatement); + + return methodBuilder.build(); + }; + } + + private static MethodSpec.Builder getMethodBuilder(Types typeUtils, ProcessedType pType) { + var typeVariableNames = pType.genericNames().stream() + .map(TypeVariableName::get) + .toList(); + var typeName = getTypeName(typeUtils, pType, typeVariableNames); + var parameterSpec = ParameterSpec.builder(typeName, pType.argumentName()).build(); + + var builder = MethodSpec.methodBuilder("_" + pType.factoryMethodName()) + .addModifiers(PRIVATE, STATIC) + .returns(typeName) + .addParameter(parameterSpec); + + typeVariableNames.forEach(builder::addTypeVariable); + + return builder; + } + + private static TypeName getTypeName(Types typeUtils, ProcessedType pType, List typeVariableNames) { + var collectionType = PROCESSED_TYPES.get(pType); + + if (typeVariableNames.isEmpty()) { + return TypeName.get(collectionType); + } + + var className = ClassName.get((TypeElement) typeUtils.asElement(collectionType)); + + return ParameterizedTypeName.get(className, typeVariableNames.toArray(TypeVariableName[]::new)); + } + + private static Optional immutableTypesBlock(ProcessedTypesStructure structure, ProcessedType pType) { + var immutableTypes = structure.getImmutableTypes(pType); + if (immutableTypes.isEmpty()) { + return Optional.empty(); + } + + var ifFormat = new StringBuilder("if ("); + var i = 0; + for (var iterator = immutableTypes.iterator(); iterator.hasNext(); i++) { + iterator.next(); + String name = pType.argumentName(); + + ifFormat.append(name).append(" instanceof $T"); + if (!pType.genericNames().isEmpty()) { + var genericClause = pType.genericNames().stream() + .collect(joining(",", "<", ">")); + ifFormat.append(genericClause); + } + + if (iterator.hasNext()) { + ifFormat.append(i == 0 ? "\n$>$>|| " : "\n|| "); + } + } + ifFormat.append(")"); + + var size = immutableTypes.size(); + var block = CodeBlock.builder() + .beginControlFlow(ifFormat.toString(), immutableTypes.toArray()) + .addStatement(size > 1 ? "$<$ subTypesBlocks(ProcessedType pType, ProcessedTypesStructure structure) { + return pType.directSubTypes().stream() + .filter(structure::needsAdditionalMethod) + .sorted(reverseOrder()) + .map(subPType -> subTypeBlock(subPType, pType, structure)) + .toList(); + } + + private static CodeBlock subTypeBlock(ProcessedType pType, ProcessedType parent, ProcessedTypesStructure structure) { + var argumentName = pType.argumentName(); + var factoryClassName = pType.factoryClassName(); + var factoryMethodName = pType.factoryMethodName(); + + var parentGenericNames = parent.genericNames(); + var genericClause = parentGenericNames.isEmpty() || !pType.checkGenericInInstanceOf() + ? "" + : parentGenericNames.stream().collect(joining(",", "<", ">")); + var statement = structure.needsAdditionalMethod(pType) + ? CodeBlock.of("return _$L($L)", factoryMethodName, argumentName) + : CodeBlock.of("return $T.$L($L)", factoryClassName, factoryMethodName, argumentName); + + return CodeBlock.builder() + .beginControlFlow("if ($L instanceof $T$L $L)", parent.argumentName(), PROCESSED_TYPES.get(pType), genericClause, argumentName) + .addStatement(statement) + .endControlFlow() + .build(); + } + + @FunctionalInterface + private interface MethodGenerator { + MethodSpec generateMethod(Types typeUtils, + ProcessedTypesStructure structure, + StaticImports staticImports, + Logger logger); + } +} diff --git a/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/ImmutableCollectionsExtension.java b/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/ImmutableCollectionsExtension.java new file mode 100644 index 0000000..4e96175 --- /dev/null +++ b/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/ImmutableCollectionsExtension.java @@ -0,0 +1,145 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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 com.squareup.javapoet.MethodSpec; +import pl.com.labaj.autorecord.context.Context; +import pl.com.labaj.autorecord.context.Logger; +import pl.com.labaj.autorecord.context.StaticImports; +import pl.com.labaj.autorecord.extension.CompactConstructorExtension; + +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.Arrays; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +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 javax.lang.model.type.TypeKind.ARRAY; +import static pl.com.labaj.autorecord.extension.compact.Names.PREDEFINED_IMMUTABLE_NAMES; +import static pl.com.labaj.autorecord.extension.compact.ProcessedType.allProcessedTypes; +import static pl.com.labaj.autorecord.extension.compact.RecordComponent.debugInfo; + +public class ImmutableCollectionsExtension implements CompactConstructorExtension { + + private static Types typeUtilsFromPreviousRun = null; + private static final Map IMMUTABLE_TYPES = new HashMap<>(); + static final EnumMap PROCESSED_TYPES = new EnumMap<>(ProcessedType.class); + + private Types typeUtils; + private Elements elementUtils; + private Logger logger; + private Set immutableTypes; + private List componentsToProcess; + private ProcessedTypesStructure structure; + + @Override + public void init(ProcessingEnvironment processingEnv, String[] parameters) { + typeUtils = processingEnv.getTypeUtils(); + elementUtils = processingEnv.getElementUtils(); + + if (!nonNull(typeUtilsFromPreviousRun) || typeUtilsFromPreviousRun != typeUtils) { + typeUtilsFromPreviousRun = typeUtils; + IMMUTABLE_TYPES.clear(); + PROCESSED_TYPES.clear(); + } + + var immutableNames = new HashSet(); + immutableNames.addAll(PREDEFINED_IMMUTABLE_NAMES); + immutableNames.addAll(Arrays.asList(parameters)); + + immutableNames.forEach(name -> IMMUTABLE_TYPES.computeIfAbsent(name, this::loadType)); + allProcessedTypes().forEach(name -> PROCESSED_TYPES.computeIfAbsent(name, this::loadType)); + + immutableTypes = immutableNames.stream() + .map(IMMUTABLE_TYPES::get) + .collect(toSet()); + + var structreBuilder = new ProcessedTypesStructure.Builder(typeUtils, immutableTypes); + structure = structreBuilder.buildStructure(); + } + + @Override + public boolean shouldGenerateCompactConstructor(boolean isGeneratedByProcessor, Context context) { + logger = context.logger(); + + var componentBuilder = new RecordComponent.Builder(typeUtils, immutableTypes, logger); + var declaredComponents = context.components() + .stream() + .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(); + } + + @Override + public CodeBlock suffixCompactConstructorContent(Context context, StaticImports staticImports) { + if (true) { //TODO: debugger info + logger.debug("Components to process:\n" + debugInfo(componentsToProcess)); + logger.note("Types structure:\n" + structure.debugInfo()); + } + + var codeBuilder = CodeBlock.builder(); + + var statementGenerator = new StatementGenerator(typeUtils, structure, staticImports, logger); + + componentsToProcess.stream() + .map(statementGenerator::generateStatement) + .forEach(codeBuilder::addStatement); + + return codeBuilder.build(); + } + + @Override + public List additionalMethodsToSupportCompactConstructor(Context context, StaticImports staticImports) { + var additionalMethodsGenerator = new AdditionalMethodsGenerator(typeUtils, staticImports, logger); + return additionalMethodsGenerator.generateAdditionalMethods(componentsToProcess, structure); + } + + private TypeMirror loadType(String className) { + var typeElement = elementUtils.getTypeElement(className); + if (isNull(typeElement)) { + return null; + } + + var type = typeElement.asType(); + return typeUtils.erasure(type); + } + + private TypeMirror loadType(ProcessedType className) { + return loadType(className.className()); + } +} diff --git a/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/Names.java b/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/Names.java new file mode 100644 index 0000000..89c5ec1 --- /dev/null +++ b/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/Names.java @@ -0,0 +1,44 @@ + +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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.Set; + +final class Names { + 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"; + + 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 Set PREDEFINED_IMMUTABLE_NAMES = Set.of( + GUAVA_PACKAGE + "." + IMMUTABLE_SET, + GUAVA_PACKAGE + "." + IMMUTABLE_SORTED_SET, + GUAVA_PACKAGE + "." + IMMUTABLE_LIST, + GUAVA_PACKAGE + "." + IMMUTABLE_MAP + ); + + private Names() {} +} diff --git a/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/ProcessedType.java b/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/ProcessedType.java new file mode 100644 index 0000000..bfda15e --- /dev/null +++ b/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/ProcessedType.java @@ -0,0 +1,125 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +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.compact.Names.GUAVA_IMMUTABLE_LIST_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.compact.Names.GUAVA_IMMUTABLE_MAP_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.compact.Names.GUAVA_IMMUTABLE_SET_CLASS_NAME; +import static pl.com.labaj.autorecord.extension.compact.Names.GUAVA_IMMUTABLE_SORTED_SET_CLASS_NAME; + +@SuppressWarnings("java:S1192") +enum ProcessedType { + MAP("java.util.Map", Set.of(), GUAVA_IMMUTABLE_MAP_CLASS_NAME, "copyOf"){ + @Override List genericNames() {return List.of("K", "V");} + }, + 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"), + OBJECT("java.lang.Object", Set.of(SET, LIST, MAP), null, "copyOfObject") { + @Override List genericNames() {return List.of();} + }; + + private static final Set ALL_TYPES = EnumSet.allOf(ProcessedType.class); + private static final Map NAME_TO_TYPES = ALL_TYPES.stream() + .collect(toMap(ProcessedType::className, identity())); + private final String className; + + private final Set directSubTypes; + private final ClassName factoryClassName; + private final String factoryMethodName; + private final String camelCaseName; + + ProcessedType(String className, + Set directSubTypes, + ClassName factoryClassName, + String factoryMethodName) { + this.className = className; + this.directSubTypes = directSubTypes; + this.factoryClassName = factoryClassName; + this.factoryMethodName = factoryMethodName; + + camelCaseName = toCamelCase(); + } + + static Set allProcessedTypes() { + return ALL_TYPES; + } + + static Optional pTypeOf(String className) { + return Optional.ofNullable(NAME_TO_TYPES.get(className)); + } + + static boolean sameFactory(ProcessedType pType, ProcessedType subPType) { + return subPType.factoryClassName().equals(pType.factoryClassName()) && subPType.factoryMethodName().equals(pType.factoryMethodName()); + } + + String className() { + return className; + } + + String argumentName() { + return camelCaseName; + } + + 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 toCamelCase() { + 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/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/ProcessedTypesStructure.java b/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/ProcessedTypesStructure.java new file mode 100644 index 0000000..538f859 --- /dev/null +++ b/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/ProcessedTypesStructure.java @@ -0,0 +1,154 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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 javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Types; +import java.util.Collection; +import java.util.EnumMap; +import java.util.Map; +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.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.compact.ImmutableCollectionsExtension.PROCESSED_TYPES; +import static pl.com.labaj.autorecord.extension.compact.ProcessedType.OBJECT; +import static pl.com.labaj.autorecord.extension.compact.ProcessedType.allProcessedTypes; + +final class ProcessedTypesStructure { + private final Map> immutableTypes; + private final Map additionalMethodNeeded; + + private ProcessedTypesStructure(EnumMap> immutableTypes, Map additionalMethodNeeded) { + this.immutableTypes = immutableTypes; + this.additionalMethodNeeded = additionalMethodNeeded; + } + + String debugInfo() { + return immutableTypes.entrySet().stream() + .map(item -> { + var types = item.getValue() + .stream() + .map(TypeMirror::toString) + .map(name -> substringAfterLast(name, ".")) + .collect(joining(", ", "(", ")")); + var className = substringAfterLast(item.getKey().className(), "."); + var needsAdditionalMethod = additionalMethodNeeded.get(item.getKey()); + + return className + " : " + needsAdditionalMethod + " -> " + types; + }) + .collect(joining("\n")); + } + + boolean needsAdditionalMethod(ProcessedType processedType) { + return additionalMethodNeeded.containsKey(processedType) && additionalMethodNeeded.get(processedType); + } + + Set getImmutableTypes(ProcessedType pType) { + return immutableTypes.get(pType); + } + + static class Builder { + private final Types typeUtils; + private final Set immutableTypes; + + Builder(Types typeUtils, Set immutableTypes) { + this.typeUtils = typeUtils; + this.immutableTypes = immutableTypes; + } + + ProcessedTypesStructure buildStructure() { + var structure = allProcessedTypes().stream() + .collect(toMap( + identity(), + pType -> immutableTypes + .stream() + .filter(immutableType -> typeUtils.isSubtype(immutableType, PROCESSED_TYPES.get(pType))) + .collect(toCollection(() -> new TreeSet<>(comparing(TypeMirror::toString)))), + (s1, s2) -> { + s1.addAll(s2); + return s1; + }, + () -> new EnumMap<>(ProcessedType.class) + )); + + optimizeStructure(OBJECT, structure); + + var additionalMethodNeeded = new EnumMap(ProcessedType.class); + collectNeedsInfo(null, OBJECT, structure, additionalMethodNeeded); + + return new ProcessedTypesStructure(structure, additionalMethodNeeded); + } + + @SuppressWarnings("SortedCollectionWithNonComparableKeys") + private Set optimizeStructure(ProcessedType pType, EnumMap> structure) { + var typeMirrors = structure.get(pType); + + if (pType.directSubTypes().isEmpty()) { + return typeMirrors; + } + + var toRemove = pType.directSubTypes().stream() + .map(subType -> optimizeStructure(subType, structure)) + .flatMap(Collection::stream) + .collect(toSet()); + + structure.get(pType).removeAll(toRemove); + + return Stream.concat(toRemove.stream(), typeMirrors.stream()).collect(toSet()); + } + + private boolean collectNeedsInfo(ProcessedType parent, ProcessedType pType, + EnumMap> structure, + EnumMap additionalMethodNeeded) { + + + if (pType.directSubTypes().isEmpty()) { + var result = !structure.get(pType).isEmpty(); + additionalMethodNeeded.put(pType, result); + return result; + } + + var needs = new AtomicBoolean(); + var subNeededInfo = pType.directSubTypes().stream() + .collect(toMap( + identity(), + subType -> collectNeedsInfo(pType, subType, structure, additionalMethodNeeded) + )); + + var someSubNeeds = subNeededInfo.values().stream() + .reduce(false, (b1, b2) -> b1 || b2); + needs.set(someSubNeeds); + + if (!needs.get()) { + needs.set(!nonNull(parent)); + } + + additionalMethodNeeded.put(pType, needs.get()); + return needs.get(); + } + } +} diff --git a/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/RecordComponent.java b/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/RecordComponent.java new file mode 100644 index 0000000..b2c2a05 --- /dev/null +++ b/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/RecordComponent.java @@ -0,0 +1,109 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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 javax.lang.model.util.Types; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static java.util.stream.Collectors.joining; +import static javax.lang.model.type.TypeKind.ERROR; +import static pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension.PROCESSED_TYPES; +import static pl.com.labaj.autorecord.extension.compact.ProcessedType.pTypeOf; + +record RecordComponent(String name, boolean isNullable, TypeMirror declaredType, ProcessedType 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 Types typeUtils; + private final Set immutableTypes; + private final Set collectionTypes; + private final Logger logger; + private final Map toBeProcessedCache = new HashMap<>(); + + Builder(Types typeUtils, Set immutableTypes, Logger logger) { + this.typeUtils = typeUtils; + this.immutableTypes = immutableTypes; + collectionTypes = Set.copyOf(PROCESSED_TYPES.values()); + 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 = typeUtils.erasure(componentType); + var shouldBeProcessed = toBeProcessedCache.computeIfAbsent(declaredType.toString(), name -> shouldBeProcessed(declaredType)); + + if (!shouldBeProcessed) { + return null; + } + + var pType = pTypeOf(declaredType.toString()) + .orElseGet(() -> { + logger.mandatoryWarning("Unrecognized type for processing " + declaredType); + return null; + }); + + 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 -> typeUtils.isSameType(componentType, immutableType)); + if (isImmutableType) { + logger.debug(componentType + " is immutable"); + return false; + } + + var hasImmutableSuperType = immutableTypes.stream() + .anyMatch(immutableType -> typeUtils.isSubtype(componentType, immutableType)); + if (hasImmutableSuperType) { + logger.debug(componentType + " has immutable super type"); + return false; + } + + var isCollection = collectionTypes.stream() + .anyMatch(collectionType -> typeUtils.isSameType(componentType, collectionType)); + if (isCollection) { + logger.debug(componentType + " is collection"); + } + + return isCollection; + } + } +} diff --git a/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/StatementGenerator.java b/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/StatementGenerator.java new file mode 100644 index 0000000..7ad9f5c --- /dev/null +++ b/extensions/immutable-collections/src/main/java/pl/com/labaj/autorecord/extension/compact/StatementGenerator.java @@ -0,0 +1,91 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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.Logger; +import pl.com.labaj.autorecord.context.StaticImports; + +import javax.annotation.Nullable; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Types; +import java.util.EnumMap; +import java.util.Objects; + +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; +import static pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension.PROCESSED_TYPES; +import static pl.com.labaj.autorecord.extension.compact.ProcessedType.allProcessedTypes; + +final class StatementGenerator { + + private static final EnumMap RECORD_STATEMENT_GENERATORS = allProcessedTypes().stream() + .collect(toMap( + identity(), + StatementGenerator::builderFor, + (b1, b2) -> b1, + () -> new EnumMap<>(ProcessedType.class) + )); + + private final Types typeUtils; + private final ProcessedTypesStructure structure; + private final StaticImports staticImports; + private final Logger logger; + + StatementGenerator(Types typeUtils, + ProcessedTypesStructure structure, + StaticImports staticImports, Logger logger) { + this.typeUtils = typeUtils; + this.structure = structure; + this.staticImports = staticImports; + this.logger = logger; + } + + CodeBlock generateStatement(RecordComponent recordComponent) { + var pType = recordComponent.pType(); + return RECORD_STATEMENT_GENERATORS.get(pType) + .generateStatement(typeUtils, PROCESSED_TYPES, recordComponent, staticImports, structure, logger); + } + + private static RecordStatementGenerator builderFor(ProcessedType pType) { + return (typeUtils, pTypes, component, staticImports, structure, logger) -> { + var nullable = component.isNullable(); + if (nullable) { + staticImports.add(Objects.class, "isNull"); + } + + if (structure.needsAdditionalMethod(pType)) { + var format = nullable ? "$1L = isNull($1L) ? null : _$2L($1L)" : "$1L = _$2L($1L)"; + return CodeBlock.of(format, component.name(), pType.factoryMethodName()); + } + + var format = nullable ? "$1L = isNull($1L) ? null : $2T.$3L($1L)" : "$1L = $2T.$3L($1L)"; + return CodeBlock.of(format, component.name(), pType.factoryClassName(), pType.factoryMethodName()); + }; + } + + @FunctionalInterface + private interface RecordStatementGenerator { + @Nullable + CodeBlock generateStatement(Types typeUtils, + EnumMap pTypes, + RecordComponent recordComponent, + StaticImports staticImports, + ProcessedTypesStructure structure, + Logger logger); + } +} diff --git a/extensions/immutable-collections/src/test/java/pl/com/labaj/autorecord/extension/compact/ImmutableCollectionsExtensionTest.java b/extensions/immutable-collections/src/test/java/pl/com/labaj/autorecord/extension/compact/ImmutableCollectionsExtensionTest.java new file mode 100644 index 0000000..c873b18 --- /dev/null +++ b/extensions/immutable-collections/src/test/java/pl/com/labaj/autorecord/extension/compact/ImmutableCollectionsExtensionTest.java @@ -0,0 +1,158 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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", + "ItemWithListsNoUserDefinedCollections", + "ItemWithLists", + "ItemWithSetsNoUserDefinedCollections", + "ItemWithSets", + }; + + static Stream names() { + return Arrays.stream(NAMES); + } + + private Compiler compiler; + + @BeforeEach + void setUp() { + compiler = javac().withClasspath(prepareClasspath()); + } + + @ParameterizedTest(name = "{0}.java") + @MethodSource("names") + void shouldGenerateSingleRecord(String name) { + //given + var generatedPath = "pl/com/labaj/autorecord/extension/compact/"; + + var inputs = List.of( + forResource("in/" + name + ".java"), + forResource("UserCollections.java") + ); + + var expectedOutput = forResource("out/" + name + "Record.java"); + + //when + var compilation = compiler + .withProcessors(new AutoRecordProcessor()) + .compile(inputs); + + //then + assertAll( + () -> assertThat(compilation).generatedSourceFile(generatedPath + name + "Record") + .hasSourceEquivalentTo(expectedOutput), + () -> assertThat(compilation).succeeded() + ); + } + + @Test + void shouldGenerateCorrectAllRecordsTogether() { + //given + var generatedPath = "pl/com/labaj/autorecord/extension/compact/"; + + var inputs = Arrays.stream(NAMES) + .map(name -> forResource("in/" + name + ".java")) + .collect(toCollection(ArrayList::new)); + + inputs.add(forResource("UserCollections.java")); + + var expectedOutputs = Arrays.stream(NAMES) + .collect(toMap( + identity(), + name -> forResource("out/" + name + "Record.java") + )); + + //when + var compilation = compiler + .withProcessors(new AutoRecordProcessor()) + .compile(inputs); + + //then + var assertions = Arrays.stream(NAMES) + .map(name -> (Executable) () -> assertThat(compilation).generatedSourceFile(generatedPath + name + "Record") + .hasSourceEquivalentTo(expectedOutputs.get(name))) + .collect(toCollection(ArrayList::new)); + + assertions.add(() -> assertThat(compilation).succeeded()); + + 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/extensions/immutable-collections/src/test/resources/UserCollections.java b/extensions/immutable-collections/src/test/resources/UserCollections.java new file mode 100644 index 0000000..62ecb75 --- /dev/null +++ b/extensions/immutable-collections/src/test/resources/UserCollections.java @@ -0,0 +1,69 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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.AbstractSet; +import java.util.Comparator; +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; + +class UserCollections { + + interface UserSet extends Set {} + + interface UserNavigableSet extends NavigableSet {} + + interface UserSortedSet extends SortedSet {} + + interface UserList extends List {} + + interface UserMap extends Map {} + + interface UserQueue extends Queue {} + + static class UserSetImpl extends AbstractSet implements UserSet { + @Override public Iterator iterator() {return null;} + @Override public int size() {return 0;} + } + + 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;} + } + + static class UserSortedSetImpl extends SortedSetImpl implements UserSortedSet {} + + static class UserNavigableSeImpl extends TreeSet implements UserNavigableSet {} + + static class UserListImpl extends AbstractList implements UserList{ + @Override public E get(int index) {return null;} + @Override public int size() {return 0;} + } +} diff --git a/extensions/immutable-collections/src/test/resources/in/ItemWithLists.java b/extensions/immutable-collections/src/test/resources/in/ItemWithLists.java new file mode 100644 index 0000000..67d727b --- /dev/null +++ b/extensions/immutable-collections/src/test/resources/in/ItemWithLists.java @@ -0,0 +1,46 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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 javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +@AutoRecord +@AutoRecord.Extension( + extensionClass = "pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension", + parameters = { + "pl.com.labaj.autorecord.extension.compact.UserCollections.UserList" + }) +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/extensions/immutable-collections/src/test/resources/in/ItemWithListsNoUserDefinedCollections.java b/extensions/immutable-collections/src/test/resources/in/ItemWithListsNoUserDefinedCollections.java new file mode 100644 index 0000000..5c01533 --- /dev/null +++ b/extensions/immutable-collections/src/test/resources/in/ItemWithListsNoUserDefinedCollections.java @@ -0,0 +1,42 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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 javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +@AutoRecord +@AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension") +interface ItemWithListsNoUserDefinedCollections { + 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/extensions/immutable-collections/src/test/resources/in/ItemWithObject.java b/extensions/immutable-collections/src/test/resources/in/ItemWithObject.java new file mode 100644 index 0000000..6852920 --- /dev/null +++ b/extensions/immutable-collections/src/test/resources/in/ItemWithObject.java @@ -0,0 +1,28 @@ + +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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.compact.ImmutableCollectionsExtension") +interface ItemWithObject { + Object object(); + @Nullable Object nullableObject(); +} \ No newline at end of file diff --git a/extensions/immutable-collections/src/test/resources/in/ItemWithSets.java b/extensions/immutable-collections/src/test/resources/in/ItemWithSets.java new file mode 100644 index 0000000..1674aef --- /dev/null +++ b/extensions/immutable-collections/src/test/resources/in/ItemWithSets.java @@ -0,0 +1,67 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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 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.compact.ImmutableCollectionsExtension", + parameters = { + "pl.com.labaj.autorecord.extension.compact.UserCollections.UserSet", + "pl.com.labaj.autorecord.extension.compact.UserCollections.UserSortedSet", + "pl.com.labaj.autorecord.extension.compact.UserCollections.UserNavigableSet" + }) +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.UserNavigableSeImpl 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.UserNavigableSeImpl nullableUserNavigableSetImpl(); + @Nullable ImmutableSet nullableImmutableSet(); +} diff --git a/extensions/immutable-collections/src/test/resources/in/ItemWithSetsNoUserDefinedCollections.java b/extensions/immutable-collections/src/test/resources/in/ItemWithSetsNoUserDefinedCollections.java new file mode 100644 index 0000000..3d50cb1 --- /dev/null +++ b/extensions/immutable-collections/src/test/resources/in/ItemWithSetsNoUserDefinedCollections.java @@ -0,0 +1,61 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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 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.compact.ImmutableCollectionsExtension") +interface ItemWithSetsNoUserDefinedCollections> { + 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.UserNavigableSeImpl 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.UserNavigableSeImpl nullableUserNavigableSetImpl(); + @Nullable ImmutableSet nullableImmutableSet(); +} diff --git a/extensions/immutable-collections/src/test/resources/out/ItemWithListsNoUserDefinedCollectionsRecord.java b/extensions/immutable-collections/src/test/resources/out/ItemWithListsNoUserDefinedCollectionsRecord.java new file mode 100644 index 0000000..7da2e97 --- /dev/null +++ b/extensions/immutable-collections/src/test/resources/out/ItemWithListsNoUserDefinedCollectionsRecord.java @@ -0,0 +1,65 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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; + +@Generated("pl.com.labaj.autorecord.AutoRecord") +@GeneratedWithAutoRecord +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.compact.ImmutableCollectionsExtension + list = _copyOf(list); + nullableList = isNull(nullableList) ? null : _copyOf(nullableList); + } + + @Generated("pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension") + private static List _copyOf(List list) { + if (list instanceof ImmutableList) { + return list; + } + return ImmutableList.copyOf(list); + } +} \ No newline at end of file diff --git a/extensions/immutable-collections/src/test/resources/out/ItemWithListsRecord.java b/extensions/immutable-collections/src/test/resources/out/ItemWithListsRecord.java new file mode 100644 index 0000000..2d489ac --- /dev/null +++ b/extensions/immutable-collections/src/test/resources/out/ItemWithListsRecord.java @@ -0,0 +1,66 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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; + +@Generated("pl.com.labaj.autorecord.AutoRecord") +@GeneratedWithAutoRecord +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.compact.ImmutableCollectionsExtension + list = _copyOf(list); + nullableList = isNull(nullableList) ? null : _copyOf(nullableList); + } + + @Generated("pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension") + private static List _copyOf(List list) { + if (list instanceof ImmutableList + || list instanceof UserCollections.UserList) { + return list; + } + return ImmutableList.copyOf(list); + } +} \ No newline at end of file diff --git a/extensions/immutable-collections/src/test/resources/out/ItemWithObjectRecord.java b/extensions/immutable-collections/src/test/resources/out/ItemWithObjectRecord.java new file mode 100644 index 0000000..73df502 --- /dev/null +++ b/extensions/immutable-collections/src/test/resources/out/ItemWithObjectRecord.java @@ -0,0 +1,105 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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 com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedSet; +import java.lang.Object; +import java.util.List; +import java.util.Map; +import java.util.NavigableSet; +import java.util.Set; +import java.util.SortedSet; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; +import pl.com.labaj.autorecord.GeneratedWithAutoRecord; + +@Generated("pl.com.labaj.autorecord.AutoRecord") +@GeneratedWithAutoRecord +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.compact.ImmutableCollectionsExtension + object = _copyOfObject(object); + nullableObject = isNull(nullableObject) ? null : _copyOfObject(nullableObject); + } + + @Generated("pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension") + private static Object _copyOfObject(Object object) { + if (object instanceof Set set) { + return _copyOf(set); + } + if (object instanceof List list) { + return _copyOf(list); + } + if (object instanceof Map map) { + return _copyOf(map); + } + return object; + } + + @Generated("pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension") + private static Set _copyOf(Set set) { + if (set instanceof ImmutableSet) { + return set; + } + if (set instanceof SortedSet sortedSet) { + return _copyOfSorted(sortedSet); + } + return ImmutableSet.copyOf(set); + } + + @Generated("pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension") + private static SortedSet _copyOfSorted(SortedSet sortedSet) { + if (sortedSet instanceof NavigableSet navigableSet) { + return _copyOfSorted(navigableSet); + } + return ImmutableSortedSet.copyOfSorted(sortedSet); + } + + @Generated("pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension") + private static NavigableSet _copyOfSorted(NavigableSet navigableSet) { + if (navigableSet instanceof ImmutableSortedSet) { + return navigableSet; + } + return ImmutableSortedSet.copyOfSorted(navigableSet); + } + + @Generated("pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension") + private static List _copyOf(List list) { + if (list instanceof ImmutableList) { + return list; + } + return ImmutableList.copyOf(list); + } + + @Generated("pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension") + private static Map _copyOf(Map map) { + if (map instanceof ImmutableMap) { + return map; + } + return ImmutableMap.copyOf(map); + } + +} \ No newline at end of file diff --git a/extensions/immutable-collections/src/test/resources/out/ItemWithSetsNoUserDefinedCollectionsRecord.java b/extensions/immutable-collections/src/test/resources/out/ItemWithSetsNoUserDefinedCollectionsRecord.java new file mode 100644 index 0000000..d993576 --- /dev/null +++ b/extensions/immutable-collections/src/test/resources/out/ItemWithSetsNoUserDefinedCollectionsRecord.java @@ -0,0 +1,117 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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 com.google.common.collect.ImmutableSortedSet; +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; + +@Generated("pl.com.labaj.autorecord.AutoRecord") +@GeneratedWithAutoRecord +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.UserNavigableSeImpl 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.UserNavigableSeImpl 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.compact.ImmutableCollectionsExtension + set = _copyOf(set); + sortedSet = _copyOfSorted(sortedSet); + navigableSet = _copyOfSorted(navigableSet); + nullableSet = isNull(nullableSet) ? null : _copyOf(nullableSet); + nullableSortedSet = isNull(nullableSortedSet) ? null : _copyOfSorted(nullableSortedSet); + nullableNavigableSet = isNull(nullableNavigableSet) ? null : _copyOfSorted(nullableNavigableSet); + } + + @Generated("pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension") + private static Set _copyOf(Set set) { + if (set instanceof ImmutableSet) { + return set; + } + if (set instanceof SortedSet sortedSet) { + return _copyOfSorted(sortedSet); + } + return ImmutableSet.copyOf(set); + } + + @Generated("pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension") + private static SortedSet _copyOfSorted(SortedSet sortedSet) { + if (sortedSet instanceof NavigableSet navigableSet) { + return _copyOfSorted(navigableSet); + } + return ImmutableSortedSet.copyOfSorted(sortedSet); + } + + @Generated("pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension") + private static NavigableSet _copyOfSorted(NavigableSet navigableSet) { + if (navigableSet instanceof ImmutableSortedSet) { + return navigableSet; + } + return ImmutableSortedSet.copyOfSorted(navigableSet); + } +} \ No newline at end of file diff --git a/extensions/immutable-collections/src/test/resources/out/ItemWithSetsRecord.java b/extensions/immutable-collections/src/test/resources/out/ItemWithSetsRecord.java new file mode 100644 index 0000000..e90a270 --- /dev/null +++ b/extensions/immutable-collections/src/test/resources/out/ItemWithSetsRecord.java @@ -0,0 +1,121 @@ +package pl.com.labaj.autorecord.extension.compact; + +/*- + * 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 com.google.common.collect.ImmutableSortedSet; +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; + +@Generated("pl.com.labaj.autorecord.AutoRecord") +@GeneratedWithAutoRecord +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.UserNavigableSeImpl 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.UserNavigableSeImpl 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.compact.ImmutableCollectionsExtension + set = _copyOf(set); + sortedSet = _copyOfSorted(sortedSet); + navigableSet = _copyOfSorted(navigableSet); + nullableSet = isNull(nullableSet) ? null : _copyOf(nullableSet); + nullableSortedSet = isNull(nullableSortedSet) ? null : _copyOfSorted(nullableSortedSet); + nullableNavigableSet = isNull(nullableNavigableSet) ? null : _copyOfSorted(nullableNavigableSet); + } + + @Generated("pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension") + private static Set _copyOf(Set set) { + if (set instanceof ImmutableSet + || set instanceof UserCollections.UserSet) { + return set; + } + if (set instanceof SortedSet sortedSet) { + return _copyOfSorted(sortedSet); + } + return ImmutableSet.copyOf(set); + } + + @Generated("pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension") + private static SortedSet _copyOfSorted(SortedSet sortedSet) { + if (sortedSet instanceof UserCollections.UserSortedSet) { + return sortedSet; + } + if (sortedSet instanceof NavigableSet navigableSet) { + return _copyOfSorted(navigableSet); + } + return ImmutableSortedSet.copyOfSorted(sortedSet); + } + + @Generated("pl.com.labaj.autorecord.extension.compact.ImmutableCollectionsExtension") + private static NavigableSet _copyOfSorted(NavigableSet navigableSet) { + if (navigableSet instanceof ImmutableSortedSet + || navigableSet instanceof UserCollections.UserNavigableSet) { + return navigableSet; + } + return ImmutableSortedSet.copyOfSorted(navigableSet); + } +} \ No newline at end of file diff --git a/modules/auto-record-tests/pom.xml b/modules/auto-record-tests/pom.xml index 945fc22..f0de4b3 100644 --- a/modules/auto-record-tests/pom.xml +++ b/modules/auto-record-tests/pom.xml @@ -6,7 +6,7 @@ pl.com.labaj auto-record-project - 2.0.1-SNAPSHOT + 2.1.1-SNAPSHOT ../../pom.xml 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/PersonWithExtensions.java b/modules/auto-record-tests/src/test/java/pl/com/labaj/autorecord/test/extension/PersonWithExtensions.java index e4169b6..21ae836 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/test/extension/PersonWithExtensions.java @@ -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/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/pom.xml b/modules/auto-record/pom.xml index 9bc79fa..812c1c3 100644 --- a/modules/auto-record/pom.xml +++ b/modules/auto-record/pom.xml @@ -6,7 +6,7 @@ pl.com.labaj auto-record-project - 2.0.1-SNAPSHOT + 2.1.1-SNAPSHOT ../../pom.xml diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/AutoRecord.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/AutoRecord.java index 48b8f1a..a81bbd4 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/AutoRecord.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/AutoRecord.java @@ -48,6 +48,8 @@ /** * 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 */ @@ -82,11 +84,13 @@ /** * Used to specify an extension for the {@link AutoRecordProcessor} 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 +98,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/context/Context.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/Context.java index 4c810f7..e5483d0 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/Context.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/Context.java @@ -27,6 +27,7 @@ * 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 */ diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/Logger.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/Logger.java index e105036..afe5cb3 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/Logger.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/context/Logger.java @@ -53,7 +53,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/extension/AutoRecordExtension.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/AutoRecordExtension.java index 7e553cd..3a697d3 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/AutoRecordExtension.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/AutoRecordExtension.java @@ -19,6 +19,8 @@ import pl.com.labaj.autorecord.AutoRecord; import pl.com.labaj.autorecord.processor.AutoRecordProcessor; +import javax.annotation.processing.ProcessingEnvironment; + /** * Represents an extension for the {@link AutoRecordProcessor} that can be used * to customize the record generation process. @@ -29,6 +31,8 @@ *

    *
  • {@link CompactConstructorExtension}
  • *
+ *

+ * Compatibility Note: Methods may be added to this interface in future releases of the library. * * @see Extensions Wiki * @since 2.1.0 @@ -38,9 +42,13 @@ 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 + * @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/CompactConstructorExtension.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/CompactConstructorExtension.java index fdfa12e..2b863ec 100644 --- 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 @@ -17,14 +17,18 @@ */ import com.squareup.javapoet.CodeBlock; +import com.squareup.javapoet.MethodSpec; import pl.com.labaj.autorecord.context.Context; import pl.com.labaj.autorecord.context.StaticImports; import pl.com.labaj.autorecord.processor.AutoRecordProcessor; +import java.util.List; + /** * 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. + *

Compatibility Note: Methods may be added to this interface in future releases of the library. * * @since 2.1.0 */ @@ -38,12 +42,14 @@ public interface CompactConstructorExtension extends AutoRecordExtension { * @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) { + 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 @@ -57,6 +63,8 @@ default CodeBlock prefixCompactConstructorContent(Context context, StaticImports /** * 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 @@ -67,4 +75,25 @@ default CodeBlock prefixCompactConstructorContent(Context context, StaticImports 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(); + } } diff --git a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/package-info.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/package-info.java index 1a4c9ba..6edebce 100644 --- a/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/package-info.java +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/extension/package-info.java @@ -24,7 +24,7 @@ * @since 2.1.0 */ -@API(status = STABLE, since = "2.1.0") +@API(status = STABLE, since = "3.0.0") package pl.com.labaj.autorecord.extension; import org.apiguardian.api.API; 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..4341c98 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 @@ -64,8 +64,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(); } @@ -154,7 +154,7 @@ private void processElement(TypeElement sourceInterface, private void logStartEnd(String prefix, TypeElement sourceInterface, MessagerLogger 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..9c01863 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/RecordFiles.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/RecordFiles.java new file mode 100644 index 0000000..e6e5e2a --- /dev/null +++ b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/RecordFiles.java @@ -0,0 +1,69 @@ +package pl.com.labaj.autorecord.processor; + +/*- + * 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.JavaFile; +import pl.com.labaj.autorecord.context.Logger; + +import javax.annotation.processing.Filer; +import javax.annotation.processing.FilerException; +import java.io.IOException; +import java.util.List; + +import static javax.tools.StandardLocation.SOURCE_OUTPUT; + +record RecordFiles(JavaFile recordFile, List additionalFiles) { + void writeFiles(Filer filer, Logger logger) { + write(filer, recordFile); + + additionalFiles.stream() + .filter(javaFile -> hasToBeWritten(javaFile, filer, logger)) + .forEach(javaFile -> write(filer, javaFile)); + } + + private void write(Filer filer, JavaFile javaFile) { + try { + javaFile.writeTo(filer); + } catch (IOException e) { + throw new AutoRecordProcessorException("Cannot write file", e); + } + } + + private boolean hasToBeWritten(JavaFile javaFile, Filer filer, Logger logger) { + try { + var packageName = javaFile.packageName; + var fileName = javaFile.typeSpec.name; + + boolean hasToBeWritten = true; + try { + var resource = filer.getResource(SOURCE_OUTPUT, packageName, fileName + ".java"); + hasToBeWritten = resource.getLastModified() == 0; + } catch (FilerException e) { + var filePath = packageName.replace('.', '/') + "/" + fileName + ".java"; + var message = e.getMessage(); + if (message.contains("Attempt to reopen a file for path") && message.contains(filePath)) { + hasToBeWritten = false; + } + } + + logger.debug("Additional file " + packageName + "." + fileName + " hasToBeWritten: " + hasToBeWritten); + return hasToBeWritten; + } catch (IOException e) { + throw new AutoRecordProcessorException("Cannot check if file exists", e); + } + } +} 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..26ba5da 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,9 +17,12 @@ */ 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; +import javax.lang.model.type.ErrorType; +import javax.lang.model.type.TypeKind; import java.util.List; import java.util.function.Predicate; @@ -27,6 +30,9 @@ 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 +45,16 @@ List getComponents(List allMethods, Predicat } private RecordComponent toRecordComponent(ExecutableElement method) { - var type = method.getReturnType(); + var returnType = method.getReturnType(); + + if (returnType.getKind() == TypeKind.ERROR) { + var returnTypeElement = ((ErrorType) returnType).asElement(); + if (ERROR_INDICATOR.equals(returnTypeElement.toString())) { + throw new AutoRecordProcessorException("Cannot infer type of " + method.getSimpleName() + "() method. Probably it is generic and not in classpath or sourcepath"); + } + } + + 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..84befa1 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; @@ -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/generator/CompactConstructorSubGenerator.java b/modules/auto-record/src/main/java/pl/com/labaj/autorecord/processor/generator/CompactConstructorSubGenerator.java index 7412612..6f2e18b 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; @@ -27,6 +28,7 @@ import pl.com.labaj.autorecord.processor.context.ProcessorContext; import javax.annotation.Nullable; +import javax.annotation.processing.Generated; import javax.lang.model.element.Modifier; import java.util.Deque; import java.util.LinkedList; @@ -34,6 +36,7 @@ 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 +66,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 +76,22 @@ 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); + } + + 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/doc-examples/pom.xml b/modules/doc-examples/pom.xml index 73d94b9..34b8067 100644 --- a/modules/doc-examples/pom.xml +++ b/modules/doc-examples/pom.xml @@ -6,7 +6,7 @@ pl.com.labaj auto-record-project - 2.0.1-SNAPSHOT + 2.1.1-SNAPSHOT ../../pom.xml 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/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..7836270 100644 --- a/modules/mvn-aggregation/pom.xml +++ b/modules/mvn-aggregation/pom.xml @@ -6,7 +6,7 @@ pl.com.labaj auto-record-project - 2.0.1-SNAPSHOT + 2.1.1-SNAPSHOT ../../pom.xml @@ -19,6 +19,8 @@ true true true + + 1.0.0-SNAPSHOT @@ -34,6 +36,12 @@ ${project.parent.version} test + + pl.com.labaj + auto-record-immutable-collections + ${auto-record-immutable-collections.version} + provided + diff --git a/pom.xml b/pom.xml index a1e5ad8..24cdafb 100644 --- a/pom.xml +++ b/pom.xml @@ -9,13 +9,14 @@ pl.com.labaj auto-record-project - 2.0.1-SNAPSHOT + 2.1.1-SNAPSHOT pom 2023 modules/auto-record modules/auto-record-tests + extensions/immutable-collections modules/doc-examples modules/mvn-aggregation @@ -24,7 +25,7 @@ 17 UTF-8 UTF-8 - 2023-07-03T11:23:03Z + 2023-07-21T11:15:27Z 4.2 3.3.1 @@ -56,6 +57,8 @@ --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED true + + ${maven.multiModuleProjectDirectory}/.build/lic-header.txt @@ -134,8 +137,7 @@ - -
${maven.multiModuleProjectDirectory}/.build/lic-header.txt
+
${license-maven-plugin.header}
**/*.java @@ -170,9 +172,6 @@ none - - -Xlint:unchecked -