Add SourceCodeStructure abstraction

Closes gh-987
This commit is contained in:
Stephane Nicoll
2019-08-19 19:09:26 +02:00
parent 3d11b9daff
commit 19ed484dc3
11 changed files with 179 additions and 64 deletions
@@ -20,6 +20,7 @@ import java.nio.file.Path;
import java.util.Objects;
import io.spring.initializr.generator.language.Language;
import io.spring.initializr.generator.language.SourceCodeStructure;
import org.springframework.core.io.support.SpringFactoriesLoader;
@@ -45,12 +46,12 @@ public interface BuildSystem {
return null;
}
default Path getMainDirectory(Path projectRoot, Language language) {
return projectRoot.resolve("src/main/" + language.id());
default SourceCodeStructure getMainDirectory(Path projectRoot, Language language) {
return new SourceCodeStructure(projectRoot.resolve("src/main/" + language.id()));
}
default Path getTestDirectory(Path projectRoot, Language language) {
return projectRoot.resolve("src/test/" + language.id());
default SourceCodeStructure getTestDirectory(Path projectRoot, Language language) {
return new SourceCodeStructure(projectRoot.resolve("src/test/" + language.id()));
}
static BuildSystem forId(String id) {
@@ -0,0 +1,69 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* 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
*
* https://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.
*/
package io.spring.initializr.generator.language;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* Provide dedicated method for directories that hold code.
*
* @author Stephane Nicoll
*/
public class SourceCodeStructure {
private final Path rootDirectory;
public SourceCodeStructure(Path rootDirectory) {
this.rootDirectory = rootDirectory;
}
public Path getRootDirectory() {
return this.rootDirectory;
}
/**
* Resource a source file, creating its package structure if necessary.
* @param packageName the name of the package
* @param file the name of the file (including its extension)
* @return the file to use to store a {@code CompilationUnit} with the specified
* package name name
* @throws IOException if an error occurred while trying to create the directory
* structure
*/
public Path resolveSourceFile(String packageName, String file) throws IOException {
return createPackage(packageName).resolve(file);
}
/**
* Create the specified package if necessary.
* @param packageName the name of the package to create
* @return the directory where source code to this package should reside
* @throws IOException if an error occurred while trying to create the directory
* structure
*/
public Path createPackage(String packageName) throws IOException {
if (!Files.exists(this.rootDirectory)) {
Files.createDirectories(this.rootDirectory);
}
Path directory = this.rootDirectory.resolve(packageName.replace('.', '/'));
Files.createDirectories(directory);
return directory;
}
}
@@ -17,7 +17,6 @@
package io.spring.initializr.generator.language;
import java.io.IOException;
import java.nio.file.Path;
/**
* A writer for some {@link SourceCode}.
@@ -28,11 +27,12 @@ import java.nio.file.Path;
public interface SourceCodeWriter<S extends SourceCode<?, ?>> {
/**
* Writes, to the given {@code directory}, the given {@code sourceCode}.
* @param directory the root directory beneath which the source code is written
* Write, to the given {@code structure}, the given {@code sourceCode}.
* @param structure the {@link SourceCodeStructure} beneath which the source code is
* written
* @param sourceCode the source code to write
* @throws IOException if writing fails
*/
void writeTo(Path directory, S sourceCode) throws IOException;
void writeTo(SourceCodeStructure structure, S sourceCode) throws IOException;
}
@@ -40,6 +40,7 @@ import io.spring.initializr.generator.language.Annotatable;
import io.spring.initializr.generator.language.Annotation;
import io.spring.initializr.generator.language.Parameter;
import io.spring.initializr.generator.language.SourceCode;
import io.spring.initializr.generator.language.SourceCodeStructure;
import io.spring.initializr.generator.language.SourceCodeWriter;
/**
@@ -87,18 +88,15 @@ public class GroovySourceCodeWriter implements SourceCodeWriter<GroovySourceCode
}
@Override
public void writeTo(Path directory, GroovySourceCode sourceCode) throws IOException {
if (!Files.exists(directory)) {
Files.createDirectories(directory);
}
public void writeTo(SourceCodeStructure structure, GroovySourceCode sourceCode) throws IOException {
for (GroovyCompilationUnit compilationUnit : sourceCode.getCompilationUnits()) {
writeTo(directory, compilationUnit);
writeTo(structure, compilationUnit);
}
}
private void writeTo(Path directory, GroovyCompilationUnit compilationUnit) throws IOException {
Path output = fileForCompilationUnit(directory, compilationUnit);
Files.createDirectories(output.getParent());
private void writeTo(SourceCodeStructure structure, GroovyCompilationUnit compilationUnit) throws IOException {
Path output = structure.resolveSourceFile(compilationUnit.getPackageName(),
compilationUnit.getName() + ".groovy");
try (IndentingWriter writer = this.indentingWriterFactory.createIndentingWriter("groovy",
Files.newBufferedWriter(output))) {
writer.println("package " + compilationUnit.getPackageName());
@@ -248,15 +246,6 @@ public class GroovySourceCodeWriter implements SourceCodeWriter<GroovySourceCode
+ String.join(", ", methodInvocation.getArguments()) + ")");
}
private Path fileForCompilationUnit(Path directory, GroovyCompilationUnit compilationUnit) {
return directoryForPackage(directory, compilationUnit.getPackageName())
.resolve(compilationUnit.getName() + ".groovy");
}
private Path directoryForPackage(Path directory, String packageName) {
return directory.resolve(packageName.replace('.', '/'));
}
private Set<String> determineImports(GroovyCompilationUnit compilationUnit) {
List<String> imports = new ArrayList<>();
for (GroovyTypeDeclaration typeDeclaration : compilationUnit.getTypeDeclarations()) {
@@ -40,6 +40,7 @@ import io.spring.initializr.generator.language.Annotatable;
import io.spring.initializr.generator.language.Annotation;
import io.spring.initializr.generator.language.Parameter;
import io.spring.initializr.generator.language.SourceCode;
import io.spring.initializr.generator.language.SourceCodeStructure;
import io.spring.initializr.generator.language.SourceCodeWriter;
/**
@@ -88,17 +89,15 @@ public class JavaSourceCodeWriter implements SourceCodeWriter<JavaSourceCode> {
}
@Override
public void writeTo(Path directory, JavaSourceCode sourceCode) throws IOException {
if (!Files.exists(directory)) {
Files.createDirectories(directory);
}
public void writeTo(SourceCodeStructure structure, JavaSourceCode sourceCode) throws IOException {
for (JavaCompilationUnit compilationUnit : sourceCode.getCompilationUnits()) {
writeTo(directory, compilationUnit);
writeTo(structure, compilationUnit);
}
}
private void writeTo(Path directory, JavaCompilationUnit compilationUnit) throws IOException {
Path output = fileForCompilationUnit(directory, compilationUnit);
private void writeTo(SourceCodeStructure structure, JavaCompilationUnit compilationUnit) throws IOException {
Path output = structure.resolveSourceFile(compilationUnit.getPackageName(),
compilationUnit.getName() + ".java");
Files.createDirectories(output.getParent());
try (IndentingWriter writer = this.indentingWriterFactory.createIndentingWriter("java",
Files.newBufferedWriter(output))) {
@@ -250,15 +249,6 @@ public class JavaSourceCodeWriter implements SourceCodeWriter<JavaSourceCode> {
+ String.join(", ", methodInvocation.getArguments()) + ")");
}
private Path fileForCompilationUnit(Path directory, JavaCompilationUnit compilationUnit) {
return directoryForPackage(directory, compilationUnit.getPackageName())
.resolve(compilationUnit.getName() + ".java");
}
private Path directoryForPackage(Path directory, String packageName) {
return directory.resolve(packageName.replace('.', '/'));
}
private Set<String> determineImports(JavaCompilationUnit compilationUnit) {
List<String> imports = new ArrayList<>();
for (JavaTypeDeclaration typeDeclaration : compilationUnit.getTypeDeclarations()) {
@@ -36,6 +36,7 @@ import io.spring.initializr.generator.language.Annotatable;
import io.spring.initializr.generator.language.Annotation;
import io.spring.initializr.generator.language.Parameter;
import io.spring.initializr.generator.language.SourceCode;
import io.spring.initializr.generator.language.SourceCodeStructure;
import io.spring.initializr.generator.language.SourceCodeWriter;
/**
@@ -53,17 +54,14 @@ public class KotlinSourceCodeWriter implements SourceCodeWriter<KotlinSourceCode
}
@Override
public void writeTo(Path directory, KotlinSourceCode sourceCode) throws IOException {
if (!Files.exists(directory)) {
Files.createDirectories(directory);
}
public void writeTo(SourceCodeStructure structure, KotlinSourceCode sourceCode) throws IOException {
for (KotlinCompilationUnit compilationUnit : sourceCode.getCompilationUnits()) {
writeTo(directory, compilationUnit);
writeTo(structure, compilationUnit);
}
}
private void writeTo(Path directory, KotlinCompilationUnit compilationUnit) throws IOException {
Path output = fileForCompilationUnit(directory, compilationUnit);
private void writeTo(SourceCodeStructure structure, KotlinCompilationUnit compilationUnit) throws IOException {
Path output = structure.resolveSourceFile(compilationUnit.getPackageName(), compilationUnit.getName() + ".kt");
Files.createDirectories(output.getParent());
try (IndentingWriter writer = this.indentingWriterFactory.createIndentingWriter("kotlin",
Files.newBufferedWriter(output))) {
@@ -281,15 +279,6 @@ public class KotlinSourceCodeWriter implements SourceCodeWriter<KotlinSourceCode
+ String.join(", ", functionInvocation.getArguments()) + ")");
}
private Path fileForCompilationUnit(Path directory, KotlinCompilationUnit compilationUnit) {
return directoryForPackage(directory, compilationUnit.getPackageName())
.resolve(compilationUnit.getName() + ".kt");
}
private Path directoryForPackage(Path directory, String packageName) {
return directory.resolve(packageName.replace('.', '/'));
}
private Set<String> determineImports(KotlinCompilationUnit compilationUnit) {
List<String> imports = new ArrayList<>();
for (KotlinTypeDeclaration typeDeclaration : compilationUnit.getTypeDeclarations()) {
@@ -20,6 +20,7 @@ import java.nio.file.Path;
import io.spring.initializr.generator.buildsystem.gradle.GradleBuildSystem;
import io.spring.initializr.generator.buildsystem.maven.MavenBuildSystem;
import io.spring.initializr.generator.language.SourceCodeStructure;
import io.spring.initializr.generator.language.java.JavaLanguage;
import io.spring.initializr.generator.language.kotlin.KotlinLanguage;
import org.junit.jupiter.api.Test;
@@ -53,14 +54,16 @@ class BuildSystemTests {
@Test
void defaultMainDirectory(@TempDir Path directory) {
Path mainDirectory = BuildSystem.forId("gradle").getMainDirectory(directory, new JavaLanguage());
assertThat(mainDirectory).isEqualTo(directory.resolve("src/main/java"));
SourceCodeStructure mainCodeStructure = BuildSystem.forId("gradle").getMainDirectory(directory,
new JavaLanguage());
assertThat(mainCodeStructure.getRootDirectory()).isEqualTo(directory.resolve("src/main/java"));
}
@Test
void defaultTestDirectory(@TempDir Path directory) {
Path mainDirectory = BuildSystem.forId("gradle").getTestDirectory(directory, new KotlinLanguage());
assertThat(mainDirectory).isEqualTo(directory.resolve("src/test/kotlin"));
SourceCodeStructure testCodeStructure = BuildSystem.forId("gradle").getTestDirectory(directory,
new KotlinLanguage());
assertThat(testCodeStructure.getRootDirectory()).isEqualTo(directory.resolve("src/test/kotlin"));
}
@Test
@@ -0,0 +1,71 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* 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
*
* https://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.
*/
package io.spring.initializr.generator.language;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SourceCodeStructure}.
*
* @author Stephane Nicoll
*/
class SourceCodeStructureTests {
@Test
void createPackage(@TempDir Path dir) throws IOException {
Path target = new SourceCodeStructure(dir).createPackage("com.example.test");
assertThat(target).exists().isDirectory().isEqualByComparingTo(dir.resolve("com/example/test"));
}
@Test
void createExistingPackageReturnsExistingDirectory(@TempDir Path dir) throws IOException {
Path target = dir.resolve("com/example");
Files.createDirectories(target);
assertThat(target).exists().isDirectory();
Path path = new SourceCodeStructure(dir).createPackage("com.example");
assertThat(path).isEqualByComparingTo(target);
}
@Test
void resolveSourceFile(@TempDir Path dir) throws IOException {
Path rootDir = dir.resolve("com/example");
assertThat(rootDir).doesNotExist();
Path target = rootDir.resolve("Test.java");
Path path = new SourceCodeStructure(dir).resolveSourceFile("com.example", "Test.java");
assertThat(path).doesNotExist().isEqualByComparingTo(target);
assertThat(rootDir).exists().isDirectory();
}
@Test
void resolveSourceFileWithExistingPackage(@TempDir Path dir) throws IOException {
Path rootDir = dir.resolve("com/example");
Files.createDirectories(rootDir);
assertThat(rootDir).exists().isDirectory();
Path target = rootDir.resolve("Test.java");
assertThat(target).doesNotExist();
Path path = new SourceCodeStructure(dir).resolveSourceFile("com.example", "Test.java");
assertThat(path).doesNotExist().isEqualByComparingTo(target);
}
}
@@ -26,6 +26,7 @@ import java.util.List;
import io.spring.initializr.generator.io.IndentingWriterFactory;
import io.spring.initializr.generator.language.Annotation;
import io.spring.initializr.generator.language.Parameter;
import io.spring.initializr.generator.language.SourceCodeStructure;
import io.spring.initializr.generator.test.io.TextTestUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@@ -277,7 +278,7 @@ class GroovySourceCodeWriterTests {
private Path writeSourceCode(GroovySourceCode sourceCode) throws IOException {
Path projectDirectory = Files.createTempDirectory(this.directory, "project-");
this.writer.writeTo(projectDirectory, sourceCode);
this.writer.writeTo(new SourceCodeStructure(projectDirectory), sourceCode);
return projectDirectory;
}
@@ -26,6 +26,7 @@ import java.util.List;
import io.spring.initializr.generator.io.IndentingWriterFactory;
import io.spring.initializr.generator.language.Annotation;
import io.spring.initializr.generator.language.Parameter;
import io.spring.initializr.generator.language.SourceCodeStructure;
import io.spring.initializr.generator.test.io.TextTestUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@@ -278,7 +279,7 @@ class JavaSourceCodeWriterTests {
private Path writeSourceCode(JavaSourceCode sourceCode) throws IOException {
Path projectDirectory = Files.createTempDirectory(this.directory, "project-");
this.writer.writeTo(projectDirectory, sourceCode);
this.writer.writeTo(new SourceCodeStructure(projectDirectory), sourceCode);
return projectDirectory;
}
@@ -25,6 +25,7 @@ import java.util.List;
import io.spring.initializr.generator.io.IndentingWriterFactory;
import io.spring.initializr.generator.language.Annotation;
import io.spring.initializr.generator.language.Parameter;
import io.spring.initializr.generator.language.SourceCodeStructure;
import io.spring.initializr.generator.test.io.TextTestUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@@ -335,7 +336,7 @@ class KotlinSourceCodeWriterTests {
private Path writeSourceCode(KotlinSourceCode sourceCode) throws IOException {
Path projectDirectory = Files.createTempDirectory(this.directory, "project-");
this.writer.writeTo(projectDirectory, sourceCode);
this.writer.writeTo(new SourceCodeStructure(projectDirectory), sourceCode);
return projectDirectory;
}