diff --git a/pom.xml b/pom.xml index 4e8418dc..8a0ccead 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ src/etc/header.txt - 1.12.1 + 1.0 5.5.2 3.24.2 7.2 @@ -123,9 +123,9 @@ - com.squareup - javapoet - ${javapoet-version} + io.soabase.java-composer + java-composer + ${java-composer-version} diff --git a/record-builder-processor/pom.xml b/record-builder-processor/pom.xml index 6465532e..4fab111f 100644 --- a/record-builder-processor/pom.xml +++ b/record-builder-processor/pom.xml @@ -32,8 +32,8 @@ - com.squareup - javapoet + io.soabase.java-composer + java-composer diff --git a/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/InternalRecordInterfaceProcessor.java b/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/InternalRecordInterfaceProcessor.java index b050c5c0..b56da57a 100644 --- a/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/InternalRecordInterfaceProcessor.java +++ b/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/InternalRecordInterfaceProcessor.java @@ -27,8 +27,6 @@ import javax.lang.model.type.TypeKind; import javax.tools.Diagnostic; import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; import static io.soabase.recordbuilder.processor.ElementUtils.getBuilderName; @@ -40,11 +38,7 @@ class InternalRecordInterfaceProcessor { private final String packageName; private final TypeSpec recordType; private final List recordComponents; - private final TypeElement iface; private final ClassType recordClassType; - private final List alternateMethods; - - private static final String FAKE_METHOD_NAME = "__FAKE__"; private static final Set javaBeanPrefixes = Set.of("get", "is"); @@ -56,7 +50,6 @@ private record Component(ExecutableElement element, Optional alternateNa this.processingEnv = processingEnv; packageName = packageNameOpt.orElseGet(() -> ElementUtils.getPackageName(iface)); recordComponents = getRecordComponents(iface); - this.iface = iface; ClassType ifaceClassType = ElementUtils.getClassType(iface, iface.getTypeParameters()); recordClassType = ElementUtils.getClassType(packageName, @@ -64,15 +57,23 @@ private record Component(ExecutableElement element, Optional alternateNa List typeVariables = iface.getTypeParameters().stream().map(TypeVariableName::get) .collect(Collectors.toList()); - MethodSpec methodSpec = generateArgumentList(); - - TypeSpec.Builder builder = TypeSpec.classBuilder(recordClassType.name()).addSuperinterface(iface.asType()) - .addMethod(methodSpec).addModifiers(Modifier.PUBLIC).addAnnotation(generatedRecordInterfaceAnnotation) + TypeSpec.Builder builder = TypeSpec.recordBuilder(recordClassType.name()).addSuperinterface(iface.asType()) .addTypeVariables(typeVariables); if (metaData.addClassRetainedGenerated()) { builder.addAnnotation(recordBuilderGeneratedAnnotation); } + var actualPackage = ElementUtils.getPackageName(iface); + addVisibility(builder, actualPackage.equals(packageName), iface.getModifiers()); + + recordComponents.forEach(component -> { + String name = component.alternateName.orElseGet(() -> component.element.getSimpleName().toString()); + FieldSpec parameterSpec = FieldSpec.builder(ClassName.get(component.element.getReturnType()), name).build(); + builder.addTypeVariables(component.element.getTypeParameters().stream().map(TypeVariableName::get) + .collect(Collectors.toList())); + builder.addField(parameterSpec); + }); + if (addRecordBuilder) { ClassType builderClassType = ElementUtils.getClassType(packageName, getBuilderName(iface, metaData, recordClassType, metaData.suffix()) + "." @@ -90,7 +91,7 @@ private record Component(ExecutableElement element, Optional alternateNa } } - alternateMethods = buildAlternateMethods(recordComponents); + addAlternateMethods(builder, recordComponents); recordType = builder.build(); } @@ -111,59 +112,27 @@ ClassType recordClassType() { return recordClassType; } - String toRecord(String classSource) { - // javapoet does yet support records - so a class was created and we can reshape it - // The class will look something like this: - /* - * // Auto generated by io.soabase.recordbuilder.core.RecordBuilder: https://github.com/Randgalt/record-builder - * package io.soabase.recordbuilder.test; - * - * import io.soabase.recordbuilder.core.RecordBuilder; import javax.annotation.processing.Generated; - * - * @Generated("io.soabase.recordbuilder.core.RecordInterface") - * - * @RecordBuilder public class MyRecord implements MyInterface { void __FAKE__(String name, int age) { } } - */ - Pattern pattern = Pattern.compile("(.*)(implements.*)(\\{)(.*" + FAKE_METHOD_NAME + ")(\\(.*\\))(.*)", - Pattern.MULTILINE | Pattern.DOTALL); - Matcher matcher = pattern.matcher(classSource); - if (!matcher.find() || matcher.groupCount() != 6) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, - "Internal error generating record. Group count: " + matcher.groupCount(), iface); + private void addVisibility(TypeSpec.Builder builder, boolean builderIsInRecordPackage, Set modifiers) { + if (builderIsInRecordPackage) { + if (modifiers.contains(Modifier.PUBLIC) || modifiers.contains(Modifier.PRIVATE) + || modifiers.contains(Modifier.PROTECTED)) { + builder.addModifiers(Modifier.PUBLIC); // builders are top level classes - can only be public or + // package-private + } + // is package-private + } else { + builder.addModifiers(Modifier.PUBLIC); } - - String declaration = matcher.group(1).trim().replace("class", "record"); - String implementsSection = matcher.group(2).trim(); - String argumentList = matcher.group(5).trim(); - - StringBuilder fixedRecord = new StringBuilder(declaration).append(argumentList).append(' ') - .append(implementsSection).append(" {"); - alternateMethods.forEach(method -> fixedRecord.append('\n').append(method)); - fixedRecord.append('}'); - return fixedRecord.toString(); - } - - private MethodSpec generateArgumentList() { - MethodSpec.Builder builder = MethodSpec.methodBuilder(FAKE_METHOD_NAME); - recordComponents.forEach(component -> { - String name = component.alternateName.orElseGet(() -> component.element.getSimpleName().toString()); - ParameterSpec parameterSpec = ParameterSpec.builder(ClassName.get(component.element.getReturnType()), name) - .build(); - builder.addTypeVariables(component.element.getTypeParameters().stream().map(TypeVariableName::get) - .collect(Collectors.toList())); - builder.addParameter(parameterSpec); - }); - return builder.build(); } - private List buildAlternateMethods(List recordComponents) { - return recordComponents.stream().filter(component -> component.alternateName.isPresent()).map(component -> { + private void addAlternateMethods(TypeSpec.Builder builder, List recordComponents) { + recordComponents.stream().filter(component -> component.alternateName.isPresent()).forEach(component -> { var method = MethodSpec.methodBuilder(component.element.getSimpleName().toString()) .addAnnotation(Override.class).addAnnotation(generatedRecordInterfaceAnnotation) .returns(ClassName.get(component.element.getReturnType())).addModifiers(Modifier.PUBLIC) .addCode("return $L();", component.alternateName.get()).build(); - return method.toString(); - }).collect(Collectors.toList()); + builder.addMethod(method); + }); } private List getRecordComponents(TypeElement iface) { diff --git a/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/RecordBuilderProcessor.java b/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/RecordBuilderProcessor.java index ac5b34f0..643d8e5c 100644 --- a/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/RecordBuilderProcessor.java +++ b/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/RecordBuilderProcessor.java @@ -36,7 +36,6 @@ import java.io.Writer; import java.util.Optional; import java.util.Set; -import java.util.function.Function; public class RecordBuilderProcessor extends AbstractProcessor { private static final String RECORD_BUILDER = RecordBuilder.class.getName(); @@ -173,7 +172,7 @@ private void processRecordInterface(TypeElement element, boolean addRecordBuilde return; } writeRecordInterfaceJavaFile(element, internalProcessor.packageName(), internalProcessor.recordClassType(), - internalProcessor.recordType(), metaData, internalProcessor::toRecord); + internalProcessor.recordType(), metaData); } private void processRecordBuilder(TypeElement record, RecordBuilder.Options metaData, @@ -229,12 +228,10 @@ private void writeRecordBuilderJavaFile(TypeElement record, String packageName, } private void writeRecordInterfaceJavaFile(TypeElement element, String packageName, ClassType classType, - TypeSpec type, RecordBuilder.Options metaData, Function toRecordProc) { + TypeSpec type, RecordBuilder.Options metaData) { JavaFile javaFile = javaFileBuilder(packageName, type, metaData); - String classSourceCode = javaFile.toString(); - int generatedIndex = classSourceCode.indexOf("@Generated"); - String recordSourceCode = toRecordProc.apply(classSourceCode); + String recordSourceCode = javaFile.toString(); Filer filer = processingEnv.getFiler(); try {