Skip to content

Commit

Permalink
Use Java Composer instead of JavaPoet
Browse files Browse the repository at this point in the history
Get rid of hack to create a record now that Java Composer supports
records
  • Loading branch information
Randgalt committed Mar 27, 2024
1 parent c9811e3 commit 1570882
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 70 deletions.
8 changes: 4 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@

<license-file-path>src/etc/header.txt</license-file-path>

<javapoet-version>1.12.1</javapoet-version>
<java-composer-version>1.0</java-composer-version>
<junit-jupiter-version>5.5.2</junit-jupiter-version>
<assertj-core.version>3.24.2</assertj-core.version>
<asm-version>7.2</asm-version>
Expand Down Expand Up @@ -123,9 +123,9 @@
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<version>${javapoet-version}</version>
<groupId>io.soabase.java-composer</groupId>
<artifactId>java-composer</artifactId>
<version>${java-composer-version}</version>
</dependency>

<dependency>
Expand Down
4 changes: 2 additions & 2 deletions record-builder-processor/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@

<dependencies>
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<groupId>io.soabase.java-composer</groupId>
<artifactId>java-composer</artifactId>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -40,11 +38,7 @@ class InternalRecordInterfaceProcessor {
private final String packageName;
private final TypeSpec recordType;
private final List<Component> recordComponents;
private final TypeElement iface;
private final ClassType recordClassType;
private final List<String> alternateMethods;

private static final String FAKE_METHOD_NAME = "__FAKE__";

private static final Set<String> javaBeanPrefixes = Set.of("get", "is");

Expand All @@ -56,23 +50,30 @@ private record Component(ExecutableElement element, Optional<String> 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,
getBuilderName(iface, metaData, ifaceClassType, metaData.interfaceSuffix()), iface.getTypeParameters());
List<TypeVariableName> typeVariables = iface.getTypeParameters().stream().map(TypeVariableName::get)
.collect(Collectors.toList());

MethodSpec methodSpec = generateArgumentList();

TypeSpec.Builder builder = TypeSpec.classBuilder(recordClassType.name()).addSuperinterface(iface.asType())
.addMethod(methodSpec).addModifiers(Modifier.PUBLIC).addAnnotation(generatedRecordInterfaceAnnotation)
TypeSpec.Builder builder = TypeSpec.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()) + "."
Expand All @@ -90,7 +91,7 @@ private record Component(ExecutableElement element, Optional<String> alternateNa
}
}

alternateMethods = buildAlternateMethods(recordComponents);
addAlternateMethods(builder, recordComponents);

recordType = builder.build();
}
Expand All @@ -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<Modifier> 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<String> buildAlternateMethods(List<Component> recordComponents) {
return recordComponents.stream().filter(component -> component.alternateName.isPresent()).map(component -> {
private void addAlternateMethods(TypeSpec.Builder builder, List<Component> 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<Component> getRecordComponents(TypeElement iface) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<String, String> 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 {
Expand Down

0 comments on commit 1570882

Please sign in to comment.