Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package org.dddjava.jig.gradle;

import org.dddjava.jig.domain.model.sources.filesystem.SourceBasePath;
import org.dddjava.jig.domain.model.sources.filesystem.SourceBasePaths;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.DependencySet;
Expand All @@ -22,12 +20,13 @@ public class GradleProject {
final Project project;

public GradleProject(Project project) {
if (isNonJavaProject(project)) {
throw new IllegalStateException("Java プラグインが適用されていません。");
}
this.project = project;
}

public static boolean isJavaProject(Project project) {
return findJavaPluginExtension(project).isPresent();
}

public Set<Path> classPaths() {
return sourceSets()
.map(SourceSet::getOutput)
Expand Down Expand Up @@ -60,16 +59,24 @@ private Stream<SourceSet> sourceSets() {
.filter(sourceSet -> !sourceSet.getName().equals(SourceSet.TEST_SOURCE_SET_NAME)));
}

public SourceBasePaths rawSourceLocations() {
/**
* 依存プロジェクトを含むすべてのクラスパスを返す
*/
public Set<Path> allClassPaths() {
return allDependencyProjectsFrom(project)
.map(GradleProject::new)
.map(gradleProject ->
new SourceBasePaths(
new SourceBasePath(gradleProject.classPaths()),
new SourceBasePath(gradleProject.sourcePaths())
))
.reduce(SourceBasePaths::merge)
.orElseThrow(() -> new IllegalStateException("対象プロジェクトが見つかりません。"));
.flatMap(gp -> gp.classPaths().stream())
.collect(toSet());
}

/**
* 依存プロジェクトを含むすべてのソースパスを返す
*/
public Set<Path> allSourcePaths() {
return allDependencyProjectsFrom(project)
.map(GradleProject::new)
.flatMap(gp -> gp.sourcePaths().stream())
.collect(toSet());
}

private Stream<Project> allDependencyProjectsFrom(Project currentProject) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
package org.dddjava.jig.gradle;

import org.dddjava.jig.domain.model.documents.documentformat.JigDiagramFormat;
import org.dddjava.jig.domain.model.documents.documentformat.JigDocument;
import org.dddjava.jig.infrastructure.configuration.JigProperties;
import org.gradle.api.Project;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.Optional;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;

public class JigConfig {

Expand All @@ -22,66 +15,17 @@ public class JigConfig {

String outputDirectory = "";

String outputOmitPrefix = ".+\\.(service|domain\\.(model|type))\\.";

JigDiagramFormat diagramFormat = JigDiagramFormat.SVG;
String dotTimeout = "10s";

boolean diagramTransitiveReduction = true;

List<JigDocument> documentTypes() {
List<JigDocument> toExclude = documentTypesToExclude();
return documentTypesToInclude().stream()
.filter(each -> !toExclude.contains(each))
.toList();
}

List<JigDocument> documentTypesToExclude() {
if (documentTypesExclude.isEmpty()) return List.of();
return documentTypesExclude.stream()
.map(JigDocument::valueOf)
.toList();
}

List<JigDocument> documentTypesToInclude() {
if (documentTypes.isEmpty()) return JigDocument.canonical();
return documentTypes.stream()
.map(JigDocument::valueOf)
.toList();
public List<String> getDocumentTypesExclude() {
return documentTypesExclude;
}

public JigProperties toJigProperties(Project project) {
return new JigProperties(
documentTypes(),
Optional.ofNullable(modelPattern).filter(s -> !s.isEmpty()), resolveOutputDirectory(project),
diagramFormat,
diagramTransitiveReduction,
parseDotTimeout()
);
}

private Duration parseDotTimeout() {
if (dotTimeout.endsWith("ms")) {
return Duration.ofMillis(Long.parseLong(dotTimeout.substring(0, dotTimeout.length() - 2)));
}
if (dotTimeout.endsWith("s")) {
return Duration.ofSeconds(Long.parseLong(dotTimeout.substring(0, dotTimeout.length() - 1)));
}
throw new IllegalArgumentException("dotTimeout must be end with ms or s. " + dotTimeout + " is invalid.");
}

private Path resolveOutputDirectory(Project project) {
if (this.outputDirectory.isEmpty()) {
return defaultOutputDirectory(project);
}
return Paths.get(this.outputDirectory);
}

private Path defaultOutputDirectory(Project project) {
Path path = Paths.get(getOutputDirectory());
if (path.isAbsolute()) return path;
var buildDirectory = project.getLayout().getBuildDirectory();
return buildDirectory.getAsFile().get().toPath().resolve("jig");
public void setDocumentTypesExclude(List<String> documentTypesExclude) {
this.documentTypesExclude = documentTypesExclude;
}

public String getModelPattern() {
Expand Down Expand Up @@ -135,23 +79,4 @@ public void setDiagramTransitiveReduction(boolean diagramTransitiveReduction) {
this.diagramTransitiveReduction = diagramTransitiveReduction;
}

public String getOutputOmitPrefix() {
return outputOmitPrefix;
}

public void setOutputOmitPrefix(String outputOmitPrefix) {
this.outputOmitPrefix = outputOmitPrefix;
}

public String propertiesText() {
return new StringJoiner("\n\t", "jig {\n\t", "\n}")
.add("documentTypes = '" + documentTypes + '\'')
.add("modelPattern = '" + modelPattern + '\'')
.add("outputDirectory = '" + outputDirectory + '\'')
.add("diagramFormat= '" + diagramFormat + '\'')
.add("dotTimeout= '" + dotTimeout + '\'')
.add("diagramTransitiveReduction= '" + diagramTransitiveReduction + '\'')
.add("outputOmitPrefix = '" + outputOmitPrefix + '\'')
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,46 @@
import org.gradle.api.plugins.ExtensionContainer;
import org.gradle.api.tasks.TaskContainer;

import java.nio.file.Path;

public class JigGradlePlugin implements Plugin<Project> {

@Override
public void apply(Project project) {
ExtensionContainer extensions = project.getExtensions();
extensions.create("jig", JigConfig.class);
JigConfig config = extensions.create("jig", JigConfig.class);
TaskContainer tasks = project.getTasks();

tasks.register("jigReports", JigReportsTask.class).configure(task -> {
task.setGroup("JIG");
task.setDescription("Generates JIG documentation for the main source code.");

// JigConfig のプロパティをタスクプロパティにワイヤリング
task.getModelPattern().convention(project.provider(config::getModelPattern));
task.getDocumentTypes().convention(project.provider(config::getDocumentTypes));
task.getDocumentTypesExclude().convention(project.provider(config::getDocumentTypesExclude));
task.getDiagramFormat().convention(project.provider(() -> config.getDiagramFormat().name()));
task.getDiagramTransitiveReduction().convention(project.provider(config::isDiagramTransitiveReduction));
task.getDotTimeout().convention(project.provider(config::getDotTimeout));

// 出力ディレクトリ(project をキャプチャしないよう configuration phase で解決)
String outputDir = config.getOutputDirectory();
if (outputDir.isEmpty()) {
task.getOutputDirectory().convention(project.getLayout().getBuildDirectory().dir("jig"));
} else {
task.getOutputDirectory().set(project.getLayout().getProjectDirectory().dir(outputDir));
}

// Java プラグインの適用状態(configuration phase で解決)
task.getJavaPluginApplied().convention(GradleProject.isJavaProject(project));

// ソース/クラスパス(configuration phase で解決し直接設定。
// Provider でラップすると project をキャプチャしてしまい、Gradle 8 の configuration cache でシリアライズできないため。)
if (GradleProject.isJavaProject(project)) {
GradleProject gp = new GradleProject(project);
task.getClassFiles().from(gp.allClassPaths().stream().map(Path::toFile).toList());
task.getSourceFiles().from(gp.allSourcePaths().stream().map(Path::toFile).toList());
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,106 @@
import org.dddjava.jig.HandleResult;
import org.dddjava.jig.JigExecutor;
import org.dddjava.jig.JigResult;
import org.dddjava.jig.domain.model.documents.documentformat.JigDiagramFormat;
import org.dddjava.jig.domain.model.documents.documentformat.JigDocument;
import org.dddjava.jig.domain.model.sources.filesystem.SourceBasePath;
import org.dddjava.jig.domain.model.sources.filesystem.SourceBasePaths;
import org.dddjava.jig.infrastructure.configuration.Configuration;
import org.dddjava.jig.infrastructure.configuration.JigProperties;
import org.gradle.api.DefaultTask;
import org.gradle.api.Project;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.PathSensitive;
import org.gradle.api.tasks.PathSensitivity;
import org.gradle.api.tasks.TaskAction;
import org.gradle.work.DisableCachingByDefault;

import java.io.File;
import java.nio.file.Path;
import java.time.Duration;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toSet;

@DisableCachingByDefault(because = "JigReportsTask depends on JigExecutor and cannot define output")
public class JigReportsTask extends DefaultTask {
@DisableCachingByDefault(because = "JigReportsTask generates files via JigExecutor whose outputs are not fully declarable")
public abstract class JigReportsTask extends DefaultTask {

@Input
public abstract Property<String> getModelPattern();

@Input
public abstract ListProperty<String> getDocumentTypes();

@Input
public abstract ListProperty<String> getDocumentTypesExclude();

@Input
public abstract Property<String> getDiagramFormat();

@Input
public abstract Property<Boolean> getDiagramTransitiveReduction();

@Input
public abstract Property<String> getDotTimeout();

@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
public abstract ConfigurableFileCollection getClassFiles();

@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
public abstract ConfigurableFileCollection getSourceFiles();

@Internal
public abstract Property<Boolean> getJavaPluginApplied();

@OutputDirectory
public abstract DirectoryProperty getOutputDirectory();

@TaskAction
void outputReports() {
Project project = getProject();
JigConfig config = project.getExtensions().findByType(JigConfig.class);
if (config == null) {
getLogger().warn("jig-gradle-pluginの設定が取得できません。通常は起こらないはずで、疑われるのはプラグイン側の実装ミスです。続行できないため終了します。");
return;
if (!getJavaPluginApplied().getOrElse(false)) {
throw new IllegalStateException("Java プラグインが適用されていません。");
}

Configuration configuration = Configuration.from(config.toJigProperties(getProject()));
List<JigDocument> documentTypes = resolveDocumentTypes();
Path outputDirectory = getOutputDirectory().getAsFile().get().toPath();

getLogger().info("-- configuration -------------------------------------------\n{}\n------------------------------------------------------------", config.propertiesText());
JigProperties jigProperties = new JigProperties(
documentTypes,
Optional.ofNullable(getModelPattern().getOrNull()).filter(s -> !s.isEmpty()),
outputDirectory,
JigDiagramFormat.valueOf(getDiagramFormat().get()),
getDiagramTransitiveReduction().get(),
parseDotTimeout(getDotTimeout().get())
);

Configuration configuration = Configuration.from(jigProperties);

getLogger().info("-- configuration -------------------------------------------\n{}\n------------------------------------------------------------",
jigProperties);

long startTime = System.currentTimeMillis();
SourceBasePaths sourceBasePaths = new GradleProject(project).rawSourceLocations();

Set<Path> classPaths = getClassFiles().getFiles().stream()
.map(File::toPath)
.collect(toSet());
Set<Path> sourcePaths = getSourceFiles().getFiles().stream()
.map(File::toPath)
.collect(toSet());
SourceBasePaths sourceBasePaths = new SourceBasePaths(
new SourceBasePath(classPaths),
new SourceBasePath(sourcePaths)
);

JigResult jigResult = JigExecutor.standard(configuration, sourceBasePaths);
List<HandleResult> handleResultList = jigResult.listResult();
Expand All @@ -48,4 +119,28 @@ void outputReports() {
System.currentTimeMillis() - startTime, resultLog);
}

private List<JigDocument> resolveDocumentTypes() {
List<JigDocument> toExclude = getDocumentTypesExclude().get().stream()
.map(JigDocument::valueOf)
.toList();

List<String> includeTypes = getDocumentTypes().get();
List<JigDocument> toInclude = includeTypes.isEmpty()
? JigDocument.canonical()
: includeTypes.stream().map(JigDocument::valueOf).toList();

return toInclude.stream()
.filter(each -> !toExclude.contains(each))
.toList();
}

private Duration parseDotTimeout(String dotTimeout) {
if (dotTimeout.endsWith("ms")) {
return Duration.ofMillis(Long.parseLong(dotTimeout.substring(0, dotTimeout.length() - 2)));
}
if (dotTimeout.endsWith("s")) {
return Duration.ofSeconds(Long.parseLong(dotTimeout.substring(0, dotTimeout.length() - 1)));
}
throw new IllegalArgumentException("dotTimeout must be end with ms or s. " + dotTimeout + " is invalid.");
}
}
Loading