diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriter.java | 670 | ||||
| -rw-r--r-- | src/test/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriterTests.java | 444 |
2 files changed, 603 insertions, 511 deletions
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriter.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriter.java index 9eea1ca..38dde1f 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriter.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriter.java @@ -39,48 +39,7 @@ public class JavaWriter implements AutoCloseable { this.writer.close(); } - private void writeBinaryOperation(JavaBinaryOperation binaryOperation) throws IOException { - var precedence = precedence(binaryOperation); - var isLeftAssociative = isLeftAssociative(binaryOperation.operator()); - - this.writeSubExpression( - binaryOperation.left(), - precedence, - isLeftAssociative - ); - this.writer.write(" "); - this.writer.write(switch (binaryOperation.operator()) { - case ASSIGN -> "="; - case BITWISE_AND -> "&"; - case BITWISE_OR -> "|"; - case EQUAL_TO -> "=="; - case LEFT_SHIFT -> "<<"; - case NOT_EQUAL_TO -> "!="; - case UNSIGNED_RIGHT_SHIFT -> ">>>"; - }); - this.writer.write(" "); - this.writeSubExpression( - binaryOperation.right(), - precedence, - !isLeftAssociative - ); - } - - private boolean isLeftAssociative(JavaBinaryOperator operator) { - return switch (operator) { - case ASSIGN -> false; - case BITWISE_AND -> true; - case BITWISE_OR -> true; - case EQUAL_TO -> true; - case LEFT_SHIFT -> true; - case NOT_EQUAL_TO -> true; - case UNSIGNED_RIGHT_SHIFT -> true; - }; - } - - private void writeBooleanLiteral(JavaBooleanLiteral booleanLiteral) throws IOException { - this.writer.write(booleanLiteral.value() ? "true" : "false"); - } + // == Compilation units == public void writeCompilationUnit(JavaCompilationUnit compilationUnit) throws IOException { this.writer.writeHeader(); @@ -97,6 +56,8 @@ public class JavaWriter implements AutoCloseable { this.writer.newLine(); } + // == Type declarations == + void writeTypeDeclaration(JavaTypeDeclaration typeDeclaration) throws IOException { switch (typeDeclaration) { case JavaClassDeclaration classDeclaration -> { @@ -117,71 +78,148 @@ public class JavaWriter implements AutoCloseable { } } - void writeBlock(JavaBlock block) throws IOException { - writeWithSeparator( - block.statements(), - this::writeBlockStatement, - () -> this.writer.newLine() - ); - } + private void writeClassDeclaration(JavaClassDeclaration classDeclaration) throws IOException { + writeDocComment(classDeclaration.docComment()); - void writeBlockStatement(JavaBlockStatement statement) throws IOException { - switch (statement) { - case JavaExpressionStatement expressionStatement -> { - writeExpressionStatement(expressionStatement); - } + this.writer.write("public class "); + this.writeIdentifier(classDeclaration.name()); + this.writer.write(" "); - case JavaLocalVariableDeclaration localVariableDeclaration -> { - writeLocalVariableDeclaration(localVariableDeclaration); - } + this.writeImplements(classDeclaration.implementsTypes()); - case JavaReturn returnStatement -> { - writeReturnStatement(returnStatement); - } + this.writer.write("{"); + this.writer.indent(); + + for (var bodyDeclaration: classDeclaration.body()) { + writer.newLine(); + writeClassBodyDeclaration(bodyDeclaration); + writer.newLine(); } - } - private void writeCast(JavaCast cast) throws IOException { - this.writer.write("("); - this.writeTypeRef(cast.type()); - this.writer.write(") "); + this.writer.newLine(); + this.writeCustomArea(classDeclaration.customArea()); - this.writeSubExpression(cast.operand(), JavaPrecedence.PRIMARY, true); + this.writer.dedent(); + this.writer.write("}"); } - private void writeDocComment(DocComment docComment) throws IOException { - for (var line : docComment.lines()) { - this.writer.write("/// "); - this.writer.write(line); - this.writer.write("\n"); + private void writeEnumDeclaration(JavaEnumDeclaration enumDeclaration) throws IOException { + this.writeDocComment(enumDeclaration.docComment()); + this.writer.write("public enum "); + this.writeIdentifier(enumDeclaration.name()); + this.writer.write(" {"); + this.writer.indent(); + this.writer.newLine(); + + if (!enumDeclaration.constants().isEmpty()) { + writeWithSeparator( + enumDeclaration.constants(), + constant -> this.writeIdentifier(constant.name()), + () -> { + this.writer.write(","); + this.writer.newLine(); + } + ); + this.writer.newLine(); + this.writer.newLine(); } + + this.writeCustomArea(enumDeclaration.customArea()); + this.writer.dedent(); + this.writer.write("}"); } - private void writeClassDeclaration(JavaClassDeclaration classDeclaration) throws IOException { - writeDocComment(classDeclaration.docComment()); + private void writeInterfaceDeclaration(JavaInterfaceDeclaration interfaceDeclaration) throws IOException { + writeDocComment(interfaceDeclaration.docComment()); - this.writer.write("public class "); - this.writeIdentifier(classDeclaration.name()); + this.writer.write("public "); + switch (interfaceDeclaration.openness()) { + case OPEN -> { + } + case SEALED -> { + this.writer.write("sealed "); + } + } + this.writer.write("interface "); + this.writeIdentifier(interfaceDeclaration.name()); this.writer.write(" "); - this.writeImplements(classDeclaration.implementsTypes()); + if (!interfaceDeclaration.permitsTypes().isEmpty()) { + writer.write("permits "); + writeWithSeparator( + interfaceDeclaration.permitsTypes(), + this::writeTypeRef, + () -> writer.write(", ") + ); + writer.write(" "); + } this.writer.write("{"); this.writer.indent(); - for (var bodyDeclaration: classDeclaration.body()) { + for (var bodyDeclaration: interfaceDeclaration.body()) { writer.newLine(); writeClassBodyDeclaration(bodyDeclaration); writer.newLine(); } this.writer.newLine(); - this.writeCustomArea(classDeclaration.customArea()); + this.writeCustomArea(interfaceDeclaration.customArea()); + this.writer.dedent(); + this.writer.write("}"); + } + + private void writeRecordDeclaration(JavaRecordDeclaration recordDeclaration) throws IOException { + writeDocComment(recordDeclaration.docComment()); + + this.writer.write("public record "); + this.writeIdentifier(recordDeclaration.name()); + this.writer.write("("); + + writeWithSeparator( + recordDeclaration.components(), + component -> { + writeTypeRef(component.type()); + writer.write(" "); + this.writeIdentifier(component.name()); + }, + () -> writer.write(", ") + ); + + writer.write(") "); + + this.writeImplements(recordDeclaration.implementsTypes()); + + this.writer.write("{"); + this.writer.indent(); + + for (var bodyDeclaration : recordDeclaration.body()) { + writer.newLine(); + writeClassBodyDeclaration(bodyDeclaration); + writer.newLine(); + } + + this.writer.newLine(); + this.writeCustomArea(recordDeclaration.customArea()); this.writer.dedent(); this.writer.write("}"); } + private void writeImplements(List<JavaTypeRef> implementsTypes) throws IOException { + if (!implementsTypes.isEmpty()) { + this.writer.write("implements "); + writeWithSeparator( + implementsTypes, + this::writeTypeRef, + () -> writer.write(", ") + ); + writer.write(" "); + } + } + + // == Type declaration bodies == + void writeClassBodyDeclaration(JavaClassBodyDeclaration bodyDeclaration) throws IOException { switch (bodyDeclaration) { case JavaConstructorDeclaration constructorDeclaration -> { @@ -231,40 +269,173 @@ public class JavaWriter implements AutoCloseable { this.writer.write("}"); } - private void writeCustomArea(JavaCustomArea customArea) throws IOException { - var customAreaName = new StringWriter(); - writeTypeRef(customArea.type(), customAreaName::write); - customAreaName.write(" "); - customAreaName.write(customArea.name()); - this.writer.writeCustomArea(customAreaName.toString()); + private void writeFieldDeclaration(JavaFieldDeclaration fieldDeclaration) throws IOException { + this.writeVisibility(fieldDeclaration.visibility()); + this.writer.write(" final "); + this.writeTypeRef(fieldDeclaration.type()); + this.writer.write(" "); + this.writeIdentifier(fieldDeclaration.name()); + this.writer.write(";"); } - private void writeEnumDeclaration(JavaEnumDeclaration enumDeclaration) throws IOException { - this.writeDocComment(enumDeclaration.docComment()); - this.writer.write("public enum "); - this.writeIdentifier(enumDeclaration.name()); - this.writer.write(" {"); - this.writer.indent(); - this.writer.newLine(); + void writeMethodDeclaration(JavaMethodDeclaration method) throws IOException { + writeVisibility(method.visibility()); + this.writer.write(" "); - if (!enumDeclaration.constants().isEmpty()) { + switch (method.kind()) { + case STATIC -> { + this.writer.write("static "); + } + case INSTANCE -> { + } + } + + writeTypeRef(method.returnType()); + this.writer.write(" "); + this.writeIdentifier(method.name()); + this.writer.write("("); + + writeWithSeparator( + method.params(), + param -> { + writeTypeRef(param.type()); + writer.write(" "); + this.writeIdentifier(param.name()); + }, + () -> writer.write(", ") + ); + + this.writer.write(")"); + + if (!method.throws_().isEmpty()) { + this.writer.write(" throws "); writeWithSeparator( - enumDeclaration.constants(), - constant -> this.writeIdentifier(constant.name()), - () -> { - this.writer.write(","); - this.writer.newLine(); - } + method.throws_(), + exceptionType -> writeTypeRef(exceptionType), + () -> this.writer.write(", ") ); + } + + if (method.body().isEmpty()) { + this.writer.write(";"); + } else { + this.writer.write(" {"); + this.writer.indent(); + if (!method.body().get().isEmpty()) { + this.writer.newLine(); + writeBlock(method.body().get()); + } + this.writer.dedent(); this.writer.newLine(); - this.writer.newLine(); + this.writer.write("}"); } + } - this.writeCustomArea(enumDeclaration.customArea()); - this.writer.dedent(); - this.writer.write("}"); + // == Methods == + + // == Type references == + + void writeTypeRef(JavaTypeRef type) throws IOException { + writeTypeRef(type, writer::write); + } + + private static void writeTypeRef(JavaTypeRef type, WriteString writer) throws IOException { + for (var part : type.packageName().parts()) { + writeIdentifier(part, writer); + writer.write("."); + } + + writeWithSeparator( + type.typeNames(), + typeName -> writeIdentifier(typeName, writer), + () -> writer.write(".") + ); + + if (!type.args().isEmpty()) { + writer.write("<"); + writeWithSeparator( + type.args(), + arg -> writeTypeArg(arg, writer), + () -> writer.write(", ") + ); + writer.write(">"); + } } + private static void writeTypeArg(JavaTypeArg arg, WriteString writer) throws IOException { + switch (arg.variance()) { + case INVARIANT -> { + // Do nothing. + } + case SUPER -> { + writer.write("? super "); + } + } + writeTypeRef(arg.type(), writer); + } + + // == Visibility == + + private void writeVisibility(JavaVisibility visibility) throws IOException { + this.writer.write(switch (visibility) { + case PUBLIC -> "public"; + case PRIVATE -> "private"; + }); + } + + // == Statements == + + void writeBlock(JavaBlock block) throws IOException { + writeWithSeparator( + block.statements(), + this::writeBlockStatement, + () -> this.writer.newLine() + ); + } + + void writeBlockStatement(JavaBlockStatement statement) throws IOException { + switch (statement) { + case JavaExpressionStatement expressionStatement -> { + writeExpressionStatement(expressionStatement); + } + + case JavaLocalVariableDeclaration localVariableDeclaration -> { + writeLocalVariableDeclaration(localVariableDeclaration); + } + + case JavaReturn returnStatement -> { + writeReturnStatement(returnStatement); + } + } + } + + private void writeExpressionStatement(JavaExpressionStatement expressionStatement) throws IOException { + writeTopLevelExpression(expressionStatement.expression()); + this.writer.write(";"); + } + + private void writeLocalVariableDeclaration(JavaLocalVariableDeclaration localVariableDeclaration) throws IOException { + if (localVariableDeclaration.type().isPresent()) { + this.writeTypeRef(localVariableDeclaration.type().get()); + } else { + this.writer.write("var"); + } + + this.writer.write(" "); + this.writeIdentifier(localVariableDeclaration.name()); + this.writer.write(" = "); + this.writeTopLevelExpression(localVariableDeclaration.initializer()); + this.writer.write(";"); + } + + private void writeReturnStatement(JavaReturn returnStatement) throws IOException { + this.writer.write("return "); + writeTopLevelExpression(returnStatement.value()); + this.writer.write(";"); + } + + // == Expressions == + void writeTopLevelExpression(JavaExpression expression) throws IOException { switch (expression) { case JavaBinaryOperation binaryOperation -> { @@ -378,102 +549,65 @@ public class JavaWriter implements AutoCloseable { }; } - private void writeExpressionStatement(JavaExpressionStatement expressionStatement) throws IOException { - writeTopLevelExpression(expressionStatement.expression()); - this.writer.write(";"); - } - - private void writeFieldAccess(JavaFieldAccess fieldAccess) throws IOException { - this.writeSubExpression(fieldAccess.receiver(), JavaPrecedence.PRIMARY, true); - this.writer.write("."); - this.writeIdentifier(fieldAccess.fieldName()); - } + private void writeBinaryOperation(JavaBinaryOperation binaryOperation) throws IOException { + var precedence = precedence(binaryOperation); + var isLeftAssociative = isLeftAssociative(binaryOperation.operator()); - private void writeFieldDeclaration(JavaFieldDeclaration fieldDeclaration) throws IOException { - this.writeVisibility(fieldDeclaration.visibility()); - this.writer.write(" final "); - this.writeTypeRef(fieldDeclaration.type()); + this.writeSubExpression( + binaryOperation.left(), + precedence, + isLeftAssociative + ); this.writer.write(" "); - this.writeIdentifier(fieldDeclaration.name()); - this.writer.write(";"); + this.writer.write(switch (binaryOperation.operator()) { + case ASSIGN -> "="; + case BITWISE_AND -> "&"; + case BITWISE_OR -> "|"; + case EQUAL_TO -> "=="; + case LEFT_SHIFT -> "<<"; + case NOT_EQUAL_TO -> "!="; + case UNSIGNED_RIGHT_SHIFT -> ">>>"; + }); + this.writer.write(" "); + this.writeSubExpression( + binaryOperation.right(), + precedence, + !isLeftAssociative + ); } - private void writeIdentifier(JavaIdentifier identifier) throws IOException { - this.writer.write(identifier.value()); + private boolean isLeftAssociative(JavaBinaryOperator operator) { + return switch (operator) { + case ASSIGN -> false; + case BITWISE_AND -> true; + case BITWISE_OR -> true; + case EQUAL_TO -> true; + case LEFT_SHIFT -> true; + case NOT_EQUAL_TO -> true; + case UNSIGNED_RIGHT_SHIFT -> true; + }; } - private static void writeIdentifier(JavaIdentifier identifier, WriteString writer) throws IOException { - writer.write(identifier.value()); + private void writeBooleanLiteral(JavaBooleanLiteral booleanLiteral) throws IOException { + this.writer.write(booleanLiteral.value() ? "true" : "false"); } - private void writeImplements(List<JavaTypeRef> implementsTypes) throws IOException { - if (!implementsTypes.isEmpty()) { - this.writer.write("implements "); - writeWithSeparator( - implementsTypes, - this::writeTypeRef, - () -> writer.write(", ") - ); - writer.write(" "); - } - } + private void writeCast(JavaCast cast) throws IOException { + this.writer.write("("); + this.writeTypeRef(cast.type()); + this.writer.write(") "); - private void writeIntegerLiteral(JavaIntegerLiteral integerLiteral) throws IOException { - this.writer.write(Long.toString(integerLiteral.value())); + this.writeSubExpression(cast.operand(), JavaPrecedence.PRIMARY, true); } - private void writeInterfaceDeclaration(JavaInterfaceDeclaration interfaceDeclaration) throws IOException { - writeDocComment(interfaceDeclaration.docComment()); - - this.writer.write("public "); - switch (interfaceDeclaration.openness()) { - case OPEN -> { - } - case SEALED -> { - this.writer.write("sealed "); - } - } - this.writer.write("interface "); - this.writeIdentifier(interfaceDeclaration.name()); - this.writer.write(" "); - - if (!interfaceDeclaration.permitsTypes().isEmpty()) { - writer.write("permits "); - writeWithSeparator( - interfaceDeclaration.permitsTypes(), - this::writeTypeRef, - () -> writer.write(", ") - ); - writer.write(" "); - } - - this.writer.write("{"); - this.writer.indent(); - - for (var bodyDeclaration: interfaceDeclaration.body()) { - writer.newLine(); - writeClassBodyDeclaration(bodyDeclaration); - writer.newLine(); - } - - this.writer.newLine(); - this.writeCustomArea(interfaceDeclaration.customArea()); - this.writer.dedent(); - this.writer.write("}"); + private void writeFieldAccess(JavaFieldAccess fieldAccess) throws IOException { + this.writeSubExpression(fieldAccess.receiver(), JavaPrecedence.PRIMARY, true); + this.writer.write("."); + this.writeIdentifier(fieldAccess.fieldName()); } - private void writeLocalVariableDeclaration(JavaLocalVariableDeclaration localVariableDeclaration) throws IOException { - if (localVariableDeclaration.type().isPresent()) { - this.writeTypeRef(localVariableDeclaration.type().get()); - } else { - this.writer.write("var"); - } - - this.writer.write(" "); - this.writeIdentifier(localVariableDeclaration.name()); - this.writer.write(" = "); - this.writeTopLevelExpression(localVariableDeclaration.initializer()); - this.writer.write(";"); + private void writeIntegerLiteral(JavaIntegerLiteral integerLiteral) throws IOException { + this.writer.write(Long.toString(integerLiteral.value())); } private void writeMethodCall(JavaMethodCall call) throws IOException { @@ -489,59 +623,6 @@ public class JavaWriter implements AutoCloseable { this.writer.write(")"); } - void writeMethodDeclaration(JavaMethodDeclaration method) throws IOException { - writeVisibility(method.visibility()); - this.writer.write(" "); - - switch (method.kind()) { - case STATIC -> { - this.writer.write("static "); - } - case INSTANCE -> { - } - } - - writeTypeRef(method.returnType()); - this.writer.write(" "); - this.writeIdentifier(method.name()); - this.writer.write("("); - - writeWithSeparator( - method.params(), - param -> { - writeTypeRef(param.type()); - writer.write(" "); - this.writeIdentifier(param.name()); - }, - () -> writer.write(", ") - ); - - this.writer.write(")"); - - if (!method.throws_().isEmpty()) { - this.writer.write(" throws "); - writeWithSeparator( - method.throws_(), - exceptionType -> writeTypeRef(exceptionType), - () -> this.writer.write(", ") - ); - } - - if (method.body().isEmpty()) { - this.writer.write(";"); - } else { - this.writer.write(" {"); - this.writer.indent(); - if (!method.body().get().isEmpty()) { - this.writer.newLine(); - writeBlock(method.body().get()); - } - this.writer.dedent(); - this.writer.newLine(); - this.writer.write("}"); - } - } - private void writeMethodRef(JavaMethodRef methodRef) throws IOException { this.writeTypeRef(methodRef.type()); this.writer.write("::"); @@ -564,61 +645,10 @@ public class JavaWriter implements AutoCloseable { this.writer.write("null"); } - private void writePackageName(JavaPackageName packageName) throws IOException { - writeWithSeparator( - packageName.parts(), - this::writeIdentifier, - () -> this.writer.write(".") - ); - } - - private void writeRecordDeclaration(JavaRecordDeclaration recordDeclaration) throws IOException { - writeDocComment(recordDeclaration.docComment()); - - this.writer.write("public record "); - this.writeIdentifier(recordDeclaration.name()); - this.writer.write("("); - - writeWithSeparator( - recordDeclaration.components(), - component -> { - writeTypeRef(component.type()); - writer.write(" "); - this.writeIdentifier(component.name()); - }, - () -> writer.write(", ") - ); - - writer.write(") "); - - this.writeImplements(recordDeclaration.implementsTypes()); - - this.writer.write("{"); - this.writer.indent(); - - for (var bodyDeclaration : recordDeclaration.body()) { - writer.newLine(); - writeClassBodyDeclaration(bodyDeclaration); - writer.newLine(); - } - - this.writer.newLine(); - this.writeCustomArea(recordDeclaration.customArea()); - - this.writer.dedent(); - this.writer.write("}"); - } - private void writeRef(JavaRef ref) throws IOException { this.writeIdentifier(ref.name()); } - private void writeReturnStatement(JavaReturn returnStatement) throws IOException { - this.writer.write("return "); - writeTopLevelExpression(returnStatement.value()); - this.writer.write(";"); - } - private void writeStaticFieldAccess(JavaStaticFieldAccess staticFieldAccess) throws IOException { this.writeTypeRef(staticFieldAccess.type()); this.writer.write("."); @@ -653,50 +683,42 @@ public class JavaWriter implements AutoCloseable { this.writeSubExpression(ternaryConditional.ifFalse(), JavaPrecedence.TERNARY, true); } - private static void writeTypeArg(JavaTypeArg arg, WriteString writer) throws IOException { - switch (arg.variance()) { - case INVARIANT -> { - // Do nothing. - } - case SUPER -> { - writer.write("? super "); - } - } - writeTypeRef(arg.type(), writer); - } + // == Identifiers == - void writeTypeRef(JavaTypeRef type) throws IOException { - writeTypeRef(type, writer::write); + private void writeIdentifier(JavaIdentifier identifier) throws IOException { + this.writer.write(identifier.value()); } - private static void writeTypeRef(JavaTypeRef type, WriteString writer) throws IOException { - for (var part : type.packageName().parts()) { - writeIdentifier(part, writer); - writer.write("."); - } + private static void writeIdentifier(JavaIdentifier identifier, WriteString writer) throws IOException { + writer.write(identifier.value()); + } + private void writePackageName(JavaPackageName packageName) throws IOException { writeWithSeparator( - type.typeNames(), - typeName -> writeIdentifier(typeName, writer), - () -> writer.write(".") + packageName.parts(), + this::writeIdentifier, + () -> this.writer.write(".") ); + } - if (!type.args().isEmpty()) { - writer.write("<"); - writeWithSeparator( - type.args(), - arg -> writeTypeArg(arg, writer), - () -> writer.write(", ") - ); - writer.write(">"); - } + // == Custom areas == + + private void writeCustomArea(JavaCustomArea customArea) throws IOException { + var customAreaName = new StringWriter(); + writeTypeRef(customArea.type(), customAreaName::write); + customAreaName.write(" "); + customAreaName.write(customArea.name()); + this.writer.writeCustomArea(customAreaName.toString()); } - private void writeVisibility(JavaVisibility visibility) throws IOException { - this.writer.write(switch (visibility) { - case PUBLIC -> "public"; - case PRIVATE -> "private"; - }); + // == Doc comments == + + private void writeDocComment(DocComment docComment) throws IOException { + for (var line : docComment.lines()) { + this.writer.write("/// "); + this.writer.write(line); + this.writer.write("\n"); + } } interface WriteString { diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriterTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriterTests.java index ecdcaff..5464116 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriterTests.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriterTests.java @@ -14,6 +14,8 @@ import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.equalTo; public class JavaWriterTests { + // == Compilation units == + @Test public void compilationUnit() throws IOException { var java = new JavaCompilationUnit( @@ -43,6 +45,10 @@ public class JavaWriterTests { )); } + // == Type declarations == + + // === Class declarations === + @Test public void emptyClassDeclaration() throws IOException { var java = new JavaClassDeclaration( @@ -168,6 +174,8 @@ public class JavaWriterTests { )); } + // === Enum declarations === + @Test public void emptyEnumDeclaration() throws IOException { var java = JavaEnumDeclaration.arbitrary() @@ -227,6 +235,8 @@ public class JavaWriterTests { )); } + // === Interface declarations === + @Test public void openInterfaceDeclaration() throws IOException { var java = new JavaInterfaceDeclaration( @@ -352,6 +362,8 @@ public class JavaWriterTests { )); } + // === Record declarations === + @Test public void emptyRecordDeclaration() throws IOException { var java = new JavaRecordDeclaration( @@ -479,6 +491,118 @@ public class JavaWriterTests { )); } + // == Type declaration bodies == + + // === Constructor declarations === + + @Test + public void emptyConstructor() throws IOException { + var java = new JavaConstructorDeclaration( + JavaIdentifier.of("Rectangle"), + JavaVisibility.PUBLIC, + List.of(), + JavaBlock.EMPTY + ); + + var string = write(writer -> writer.writeClassBodyDeclaration(java)); + + assertThat(string, equalTo(""" + public Rectangle() { + }""" + )); + } + + @Test + public void publicConstructor() throws IOException { + var java = new JavaConstructorDeclaration( + JavaIdentifier.of("Rectangle"), + JavaVisibility.PUBLIC, + List.of(), + JavaBlock.EMPTY + ); + + var string = write(writer -> writer.writeClassBodyDeclaration(java)); + + assertThat(string, equalTo(""" + public Rectangle() { + }""" + )); + } + + @Test + public void privateConstructor() throws IOException { + var java = new JavaConstructorDeclaration( + JavaIdentifier.of("Rectangle"), + JavaVisibility.PRIVATE, + List.of(), + JavaBlock.EMPTY + ); + + var string = write(writer -> writer.writeClassBodyDeclaration(java)); + + assertThat(string, equalTo(""" + private Rectangle() { + }""" + )); + } + + @Test + public void constructorWithParams() throws IOException { + var java = new JavaConstructorDeclaration( + JavaIdentifier.of("Rectangle"), + JavaVisibility.PUBLIC, + List.of( + new JavaParam(JavaTypeRef.INT, JavaIdentifier.of("b")), + new JavaParam(JavaTypeRef.LONG, JavaIdentifier.of("c")) + ), + JavaBlock.EMPTY + ); + + var string = write(writer -> writer.writeClassBodyDeclaration(java)); + + assertThat(string, equalTo(""" + public Rectangle(int b, long c) { + }""" + )); + } + + @Test + public void constructorWithBody() throws IOException { + var java = new JavaConstructorDeclaration( + JavaIdentifier.of("Rectangle"), + JavaVisibility.PUBLIC, + List.of(), + new JavaBlock(List.of( + new JavaReturn(new JavaIntegerLiteral(42)) + )) + ); + + var string = write(writer -> writer.writeClassBodyDeclaration(java)); + + assertThat(string, equalTo(""" + public Rectangle() { + return 42; + }""" + )); + } + + // === Field declarations === + + @Test + public void fieldDeclaration() throws IOException { + var java = new JavaFieldDeclaration( + JavaVisibility.PRIVATE, + JavaTypeRef.INT, + JavaIdentifier.of("count") + ); + + var string = write(writer -> writer.writeClassBodyDeclaration(java)); + + assertThat(string, equalTo("private final int count;")); + } + + // === Method declarations === + @Test public void emptyMethodWithoutBody() throws IOException { var java = JavaMethodDeclaration.arbitrary() @@ -633,109 +757,72 @@ public class JavaWriterTests { )); } - @Test - public void emptyConstructor() throws IOException { - var java = new JavaConstructorDeclaration( - JavaIdentifier.of("Rectangle"), - JavaVisibility.PUBLIC, - List.of(), - JavaBlock.EMPTY - ); - - var string = write(writer -> writer.writeClassBodyDeclaration(java)); + // == Methods == - assertThat(string, equalTo(""" - public Rectangle() { - }""" - )); - } + // == Type references == @Test - public void publicConstructor() throws IOException { - var java = new JavaConstructorDeclaration( - JavaIdentifier.of("Rectangle"), - JavaVisibility.PUBLIC, - List.of(), - JavaBlock.EMPTY + public void typeRefWithoutArgsHasNoAngleBrackets() throws IOException { + var java = JavaTypeRef.topLevel( + JavaPackageName.of("abc", "def"), + JavaIdentifier.of("One") ); - var string = write(writer -> writer.writeClassBodyDeclaration(java)); + var string = write(writer -> writer.writeTypeRef(java)); - assertThat(string, equalTo(""" - public Rectangle() { - }""" - )); + assertThat(string, equalTo("abc.def.One")); } @Test - public void privateConstructor() throws IOException { - var java = new JavaConstructorDeclaration( - JavaIdentifier.of("Rectangle"), - JavaVisibility.PRIVATE, - List.of(), - JavaBlock.EMPTY + public void typeRefWithArgsHasAngleBrackets() throws IOException { + var java = JavaTypeRef.topLevelGeneric( + JavaPackageName.of("abc", "def"), + JavaIdentifier.of("One"), + List.of( + JavaTypeArg.invariant(JavaTypeRef.topLevel(JavaPackageName.EMPTY, JavaIdentifier.of("Two"))), + JavaTypeArg.invariant(JavaTypeRef.topLevel(JavaPackageName.EMPTY, JavaIdentifier.of("Three"))) + ) ); - var string = write(writer -> writer.writeClassBodyDeclaration(java)); + var string = write(writer -> writer.writeTypeRef(java)); - assertThat(string, equalTo(""" - private Rectangle() { - }""" - )); + assertThat(string, equalTo("abc.def.One<Two, Three>")); } @Test - public void constructorWithParams() throws IOException { - var java = new JavaConstructorDeclaration( - JavaIdentifier.of("Rectangle"), - JavaVisibility.PUBLIC, + public void typeRefArgCanBeSuper() throws IOException { + var java = JavaTypeRef.topLevelGeneric( + JavaPackageName.of("abc", "def"), + JavaIdentifier.of("One"), List.of( - new JavaParam(JavaTypeRef.INT, JavaIdentifier.of("b")), - new JavaParam(JavaTypeRef.LONG, JavaIdentifier.of("c")) - ), - JavaBlock.EMPTY + JavaTypeArg.superType(JavaTypeRef.topLevel(JavaPackageName.EMPTY, JavaIdentifier.of("Two"))), + JavaTypeArg.superType(JavaTypeRef.topLevel(JavaPackageName.EMPTY, JavaIdentifier.of("Three"))) + ) ); - var string = write(writer -> writer.writeClassBodyDeclaration(java)); + var string = write(writer -> writer.writeTypeRef(java)); - assertThat(string, equalTo(""" - public Rectangle(int b, long c) { - }""" - )); + assertThat(string, equalTo("abc.def.One<? super Two, ? super Three>")); } @Test - public void constructorWithBody() throws IOException { - var java = new JavaConstructorDeclaration( - JavaIdentifier.of("Rectangle"), - JavaVisibility.PUBLIC, - List.of(), - new JavaBlock(List.of( - new JavaReturn(new JavaIntegerLiteral(42)) - )) + public void typeRefInnerTypeNameIsSeparatedByDot() throws IOException { + var java = JavaTypeRef.inner( + JavaPackageName.of("abc", "def"), + JavaIdentifier.of("One"), + JavaIdentifier.of("Two") ); - var string = write(writer -> writer.writeClassBodyDeclaration(java)); + var string = write(writer -> writer.writeTypeRef(java)); - assertThat(string, equalTo(""" - public Rectangle() { - return 42; - }""" - )); + assertThat(string, equalTo("abc.def.One.Two")); } - @Test - public void fieldDeclaration() throws IOException { - var java = new JavaFieldDeclaration( - JavaVisibility.PRIVATE, - JavaTypeRef.INT, - JavaIdentifier.of("count") - ); + // == Visibility == - var string = write(writer -> writer.writeClassBodyDeclaration(java)); + // == Statements == - assertThat(string, equalTo("private final int count;")); - } + // === Blocks === @Test public void block() throws IOException { @@ -749,6 +836,8 @@ public class JavaWriterTests { assertThat(string, equalTo("return 42;\nreturn 47;")); } + // === Expression statements === + @Test public void expressionStatement() throws IOException { var java = new JavaExpressionStatement(new JavaIntegerLiteral(42)); @@ -758,6 +847,8 @@ public class JavaWriterTests { assertThat(string, equalTo("42;")); } + // === Local variable declarations === + @Test public void localVariableDeclarationUsingExplicitType() throws IOException { var java = JavaLocalVariableDeclaration.arbitrary() @@ -783,6 +874,8 @@ public class JavaWriterTests { assertThat(string, equalTo("var count = 42;")); } + // === Returns === + @Test public void returnStatement() throws IOException { var java = new JavaReturn(new JavaIntegerLiteral(42)); @@ -792,59 +885,9 @@ public class JavaWriterTests { assertThat(string, equalTo("return 42;")); } - @Test - public void falseLiteral() throws IOException { - var java = new JavaBooleanLiteral(false); - - var string = write(writer -> writer.writeTopLevelExpression(java)); - - assertThat(string, equalTo("false")); - } - - @Test - public void trueLiteral() throws IOException { - var java = new JavaBooleanLiteral(true); - - var string = write(writer -> writer.writeTopLevelExpression(java)); - - assertThat(string, equalTo("true")); - } - - @Test - public void cast() throws IOException { - var java = new JavaCast(JavaTypeRef.INT, new JavaIntegerLiteral(42)); - - var string = write(writer -> writer.writeTopLevelExpression(java)); - - assertThat(string, equalTo("(int) 42")); - } - - @Test - public void integerLiteral() throws IOException { - var java = new JavaIntegerLiteral(42); - - var string = write(writer -> writer.writeTopLevelExpression(java)); - - assertThat(string, equalTo("42")); - } - - @Test - public void nullLiteral() throws IOException { - var java = new JavaNullLiteral(); - - var string = write(writer -> writer.writeTopLevelExpression(java)); - - assertThat(string, equalTo("null")); - } - - @Test - public void stringLiteral() throws IOException { - var java = new JavaStringLiteral("hello"); - - var string = write(writer -> writer.writeTopLevelExpression(java)); + // == Expressions == - assertThat(string, equalTo("\"hello\"")); - } + // === Binary operations === @Test public void binaryOperationAssignment() throws IOException { @@ -937,6 +980,39 @@ public class JavaWriterTests { assertThat(string, equalTo("x | y")); } + // === Boolean literals === + + @Test + public void falseLiteral() throws IOException { + var java = new JavaBooleanLiteral(false); + + var string = write(writer -> writer.writeTopLevelExpression(java)); + + assertThat(string, equalTo("false")); + } + + @Test + public void trueLiteral() throws IOException { + var java = new JavaBooleanLiteral(true); + + var string = write(writer -> writer.writeTopLevelExpression(java)); + + assertThat(string, equalTo("true")); + } + + // === Casts === + + @Test + public void cast() throws IOException { + var java = new JavaCast(JavaTypeRef.INT, new JavaIntegerLiteral(42)); + + var string = write(writer -> writer.writeTopLevelExpression(java)); + + assertThat(string, equalTo("(int) 42")); + } + + // === Field accesses === + @Test public void fieldAccess() throws IOException { var java = new JavaFieldAccess( @@ -949,18 +1025,19 @@ public class JavaWriterTests { assertThat(string, equalTo("one.two")); } + // === Integer literals === + @Test - public void staticFieldAccess() throws IOException { - var java = new JavaStaticFieldAccess( - JavaTypeRef.topLevel(JavaPackageName.of("one"), JavaIdentifier.of("Two")), - JavaIdentifier.of("three") - ); + public void integerLiteral() throws IOException { + var java = new JavaIntegerLiteral(42); var string = write(writer -> writer.writeTopLevelExpression(java)); - assertThat(string, equalTo("one.Two.three")); + assertThat(string, equalTo("42")); } + // === Method refs === + @Test public void referenceToMethodOnType() throws IOException { var java = new JavaMethodRef( @@ -973,6 +1050,8 @@ public class JavaWriterTests { assertThat(string, equalTo("one.Two::three")); } + // === New expressions === + @Test public void newExpression() throws IOException { var java = new JavaNewExpression( @@ -985,6 +1064,19 @@ public class JavaWriterTests { assertThat(string, equalTo("new one.Two(1, 2)")); } + // === Null literals === + + @Test + public void nullLiteral() throws IOException { + var java = new JavaNullLiteral(); + + var string = write(writer -> writer.writeTopLevelExpression(java)); + + assertThat(string, equalTo("null")); + } + + // === Refs === + @Test public void ref() throws IOException { var java = new JavaRef(JavaIdentifier.of("x")); @@ -994,6 +1086,22 @@ public class JavaWriterTests { assertThat(string, equalTo("x")); } + // === Static field accesses === + + @Test + public void staticFieldAccess() throws IOException { + var java = new JavaStaticFieldAccess( + JavaTypeRef.topLevel(JavaPackageName.of("one"), JavaIdentifier.of("Two")), + JavaIdentifier.of("three") + ); + + var string = write(writer -> writer.writeTopLevelExpression(java)); + + assertThat(string, equalTo("one.Two.three")); + } + + // === Ternary conditionals === + @Test public void ternaryConditional() throws IOException { var java = new JavaTernaryConditional( @@ -1007,6 +1115,19 @@ public class JavaWriterTests { assertThat(string, equalTo("x ? y : z")); } + // === String literals === + + @Test + public void stringLiteral() throws IOException { + var java = new JavaStringLiteral("hello"); + + var string = write(writer -> writer.writeTopLevelExpression(java)); + + assertThat(string, equalTo("\"hello\"")); + } + + // === Precedence handling === + @Test public void whenSubExpressionHasLowerPrecedenceThenSubExpressionIsParenthesized() throws IOException { var java = JavaBinaryOperation.arbitrary() @@ -1071,62 +1192,9 @@ public class JavaWriterTests { assertThat(string, equalTo("(a = b) = c = d")); } - @Test - public void typeRefWithoutArgsHasNoAngleBrackets() throws IOException { - var java = JavaTypeRef.topLevel( - JavaPackageName.of("abc", "def"), - JavaIdentifier.of("One") - ); - - var string = write(writer -> writer.writeTypeRef(java)); - - assertThat(string, equalTo("abc.def.One")); - } - - @Test - public void typeRefWithArgsHasAngleBrackets() throws IOException { - var java = JavaTypeRef.topLevelGeneric( - JavaPackageName.of("abc", "def"), - JavaIdentifier.of("One"), - List.of( - JavaTypeArg.invariant(JavaTypeRef.topLevel(JavaPackageName.EMPTY, JavaIdentifier.of("Two"))), - JavaTypeArg.invariant(JavaTypeRef.topLevel(JavaPackageName.EMPTY, JavaIdentifier.of("Three"))) - ) - ); - - var string = write(writer -> writer.writeTypeRef(java)); + // == Identifiers == - assertThat(string, equalTo("abc.def.One<Two, Three>")); - } - - @Test - public void typeRefArgCanBeSuper() throws IOException { - var java = JavaTypeRef.topLevelGeneric( - JavaPackageName.of("abc", "def"), - JavaIdentifier.of("One"), - List.of( - JavaTypeArg.superType(JavaTypeRef.topLevel(JavaPackageName.EMPTY, JavaIdentifier.of("Two"))), - JavaTypeArg.superType(JavaTypeRef.topLevel(JavaPackageName.EMPTY, JavaIdentifier.of("Three"))) - ) - ); - - var string = write(writer -> writer.writeTypeRef(java)); - - assertThat(string, equalTo("abc.def.One<? super Two, ? super Three>")); - } - - @Test - public void typeRefInnerTypeNameIsSeparatedByDot() throws IOException { - var java = JavaTypeRef.inner( - JavaPackageName.of("abc", "def"), - JavaIdentifier.of("One"), - JavaIdentifier.of("Two") - ); - - var string = write(writer -> writer.writeTypeRef(java)); - - assertThat(string, equalTo("abc.def.One.Two")); - } + // == Custom areas == @Test public void customAreaContentsArePreserved() throws IOException { @@ -1158,6 +1226,8 @@ public class JavaWriterTests { )); } + // == Doc comments == + private String write(Write write) throws IOException { return write(write, Optional.empty()); } |
