Skip to content

Commit

Permalink
Add systems for delegating, removing, and sanitizing existing javadocs
Browse files Browse the repository at this point in the history
  • Loading branch information
lukebemish committed Oct 11, 2023
1 parent f887d69 commit 64feafc
Show file tree
Hide file tree
Showing 9 changed files with 534 additions and 146 deletions.
5 changes: 3 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
}

group = 'dev.lukebemish.docpatcher'
version = '0.1.4'
version = '0.2.0'

java {
// I don't feel like dealing with old java...
Expand All @@ -22,9 +22,10 @@ repositories {

dependencies {
compileOnly 'org.jetbrains:annotations:24.0.1'
implementation 'net.neoforged.javadoctor:injector-spoon:2.0.3'
implementation 'net.neoforged.javadoctor:injector-spoon:2.0.4'
implementation 'fr.inria.gforge.spoon:spoon-core:10.4.1'
implementation 'com.google.guava:guava:32.1.2-jre'
implementation 'org.apache.commons:commons-text:1.10.0'
}

processResources {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package dev.lukebemish.docpatcher.plugin.api;

import dev.lukebemish.docpatcher.plugin.impl.JavadocStrippingVisitor;
import dev.lukebemish.docpatcher.plugin.impl.SpoonJavadocVisitor;
import dev.lukebemish.docpatcher.plugin.impl.Utils;
import net.neoforged.javadoctor.injector.CombiningJavadocProvider;
import net.neoforged.javadoctor.injector.JavadocInjector;
import net.neoforged.javadoctor.injector.JavadocProvider;
import net.neoforged.javadoctor.injector.ast.JClassParser;
Expand All @@ -11,13 +14,17 @@
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.RelativePath;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.*;
import org.jetbrains.annotations.NotNull;
import spoon.Launcher;
import spoon.support.compiler.FileSystemFile;
import spoon.support.compiler.VirtualFile;

import javax.inject.Inject;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;

public abstract class ApplyPatchesTask extends DefaultTask {
@InputDirectory
Expand All @@ -29,29 +36,27 @@ public abstract class ApplyPatchesTask extends DefaultTask {
@OutputDirectory
public abstract DirectoryProperty getOutputDirectory();
@Input
private int javaVersion;

public void setJavaVersion(int javaVersion) {
this.javaVersion = javaVersion;
}

public int getJavaVersion() {
if (javaVersion == 0) {
throw new RuntimeException("Java version not set");
}
return javaVersion;
}
public abstract Property<Integer> getJavaVersion();
@Input
@Optional
public abstract Property<String> getOriginalTag();
@Input
public abstract Property<Boolean> getKeepOriginal();
@Input
public abstract Property<Boolean> getSanitizeOriginal();

@Inject
public ApplyPatchesTask(Project project) {
var version = project.getExtensions().getByType(JavaPluginExtension.class).getToolchain().getLanguageVersion();
if (version.isPresent()) {
this.javaVersion = version.get().asInt();
this.getJavaVersion().convention(version.get().asInt());
}
getKeepOriginal().convention(true);
getSanitizeOriginal().convention(false);
}

private Launcher makeLauncher() {
return Utils.makeLauncher(getJavaVersion());
return Utils.makeLauncher(getJavaVersion().get());
}

@TaskAction
Expand All @@ -68,6 +73,13 @@ public void applyPatches() {
String className = fileName.substring(0, fileName.length() - 5);
try {
String contents = Files.readString(fileVisitDetails.getFile().toPath());
var launcher = makeLauncher();
launcher.addInputResource(new VirtualFile(contents));
var visitor = new JavadocStrippingVisitor(contents);
for (var type : launcher.buildModel().getAllTypes()) {
visitor.visit(type);
}
contents = visitor.build();
var result = injector.injectDocs(className, contents, null);
result.getResult().ifPresentOrElse(injectionResult -> {
try {
Expand Down Expand Up @@ -96,12 +108,11 @@ public void applyPatches() {
}

@NotNull
private JavadocInjector createInjector() {
JClassParser parser = new SpoonClassParser(this::makeLauncher);
private JavadocProvider createPatchInjector() {
if (getPatches().getOrNull() == null) {
return new JavadocInjector(parser, className -> null);
return className -> null;
}
JavadocProvider provider = className -> {
return className -> {
className = className.replace('.', '/');
var path = getPatches().get().getAsFile().toPath().resolve(className + ".docpatcher.json");
if (Files.exists(path)) {
Expand All @@ -114,6 +125,41 @@ private JavadocInjector createInjector() {
}
return null;
};
return new JavadocInjector(parser, provider);
}

@NotNull
private JavadocProvider createOriginalInjector() {
if (!getKeepOriginal().get()) {
return className -> null;
}
return className -> {
className = className.replace('.', '/');
var path = getSource().get().getAsFile().toPath().resolve(className + ".java");
if (Files.exists(path)) {
String tag = getOriginalTag().getOrNull();
Launcher launcher = makeLauncher();
launcher.addInputResource(new FileSystemFile(path.toFile()));
var mTypes = launcher.buildModel().getAllTypes().stream().toList();
if (mTypes.size() != 1) {
throw new RuntimeException("Expected 1 type, found " + mTypes.size());
}
var type = mTypes.get(0);
if (tag != null) {
SpoonJavadocVisitor.TagWrapper visitor = new SpoonJavadocVisitor.TagWrapper(tag, getSanitizeOriginal().get());
return visitor.visit(type);
}
var visitor = new SpoonJavadocVisitor.Simple(getSanitizeOriginal().get());
return visitor.visit(type);
}
return null;
};
}

@NotNull
private JavadocInjector createInjector() {
JClassParser parser = new SpoonClassParser(this::makeLauncher);
var patches = createPatchInjector();
var original = createOriginalInjector();
return new JavadocInjector(parser, new CombiningJavadocProvider(List.of(patches, original)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public class DiffSettings {
private final Property<SourceSet> patchesSourceSetProperty;
private final Property<SourceSet> outputSourceSetProperty;
private final Project project;
private String originalTag;
private boolean sanitizeOriginal;

public DiffSettings(ObjectFactory objectFactory, Project project) {
this.cleanProperty = objectFactory.directoryProperty();
Expand Down Expand Up @@ -209,12 +211,17 @@ void makeTasks(Project project) {
task.getPatches().set(getPatchesDirectory());
task.getSource().set(getCleanDirectory());
task.getOutputDirectory().set(getOutputDirectory());
if (getOriginalTag() != null) {
task.getOriginalTag().set(getOriginalTag());
}
task.getSanitizeOriginal().set(getSanitizeOriginal());
task.dependsOn(cleanTask);
});
var uncheckedApplyTask = project.getTasks().register(PREFIX_SETUP+StringUtils.capitalize(getModified())+"ApplyPatchesUnchecked", ApplyPatchesTask.class, task -> {
task.getPatches().set(getPatchesDirectory());
task.getSource().set(getCleanDirectory());
task.getOutputDirectory().set(getModifiedDirectory());
task.getKeepOriginal().set(false);
task.dependsOn(cleanTask);
});
project.getTasks().register(PREFIX_SETUP+StringUtils.capitalize(getModified())+"ApplyPatches", MissedPatchesTask.class, task -> {
Expand All @@ -233,4 +240,20 @@ void makeTasks(Project project) {
outputSourceSet.java(src ->
src.srcDir(outputProperty));
}

public String getOriginalTag() {
return originalTag;
}

public void setOriginalTag(String originalTag) {
this.originalTag = originalTag;
}

public boolean getSanitizeOriginal() {
return sanitizeOriginal;
}

public void setSanitizeOriginal(boolean sanitizeOriginal) {
this.sanitizeOriginal = sanitizeOriginal;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public void generatePatches() {

getProject().delete(getOutputDirectory());

var visitor = new SpoonJavadocVisitor();
var visitor = new SpoonJavadocVisitor.Comparing(false);

getModified().getAsFileTree().visit(fileVisitDetails -> {
if (!fileVisitDetails.isDirectory() && fileVisitDetails.getFile().getName().endsWith(".java")) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package dev.lukebemish.docpatcher.plugin.api;

import dev.lukebemish.docpatcher.plugin.impl.SpoonPatchVisitor;
import dev.lukebemish.docpatcher.plugin.impl.SpoonRemainingVisitor;
import dev.lukebemish.docpatcher.plugin.impl.Utils;
import net.neoforged.javadoctor.injector.JavadocProvider;
import net.neoforged.javadoctor.spec.ClassJavadoc;
Expand Down Expand Up @@ -59,7 +59,7 @@ public void missedPatches() {
return;
}
getProject().delete(getOutputDirectory());
SpoonPatchVisitor visitor = new SpoonPatchVisitor();
SpoonRemainingVisitor visitor = new SpoonRemainingVisitor();
JavadocProvider provider = makeProvider();
getSource().getAsFileTree().visit(fileVisitDetails -> {
RelativePath relativePath = fileVisitDetails.getRelativePath();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package dev.lukebemish.docpatcher.plugin.impl;

import spoon.reflect.code.CtJavaDoc;
import spoon.reflect.cu.SourcePosition;
import spoon.reflect.declaration.CtElement;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class JavadocStrippingVisitor {
public void visit(SourcePosition pos) {
if (pos.isValidPosition()) {
var begin = pos.getSourceStart();
var end = pos.getSourceEnd();
var endLast = end;
while (end < value.length()) {
if (!Character.isWhitespace(value.charAt(end))) {
break;
}
endLast = end;
end++;
}
breakStarts.add(begin);
breakEnds.add(endLast);
}
}

public void visit(CtElement element) {
if (element instanceof CtJavaDoc) {
visit(element.getPosition());
}
for (var child : element.getDirectChildren()) {
visit(child);
}
}

final List<Integer> breakStarts = new ArrayList<>();
final List<Integer> breakEnds = new ArrayList<>();
final String value;

public JavadocStrippingVisitor(String value) {
this.value = value;
}

public String build() {
List<Integer> indices = indices();

String out = value;
for (int i = indices.size() - 1; i >= 0; i--) {
int start = breakStarts.get(indices.get(i));
int end = breakEnds.get(indices.get(i));
if (end == out.length() - 1) {
out = out.substring(0, start);
} else {
out = out.substring(0, start) + out.substring(end+1);
}
}
return out;
}

private List<Integer> indices() {
if (breakStarts.isEmpty()) {
return Collections.emptyList();
}
var indices = new ArrayList<Integer>(breakStarts.size());
for (int i = 0; i < breakStarts.size(); i++) {
indices.add(i);
}
indices.sort((i1, i2) -> breakStarts.get(i1).compareTo(breakStarts.get(i2)));
return indices;
}
}
Loading

0 comments on commit 64feafc

Please sign in to comment.