add j2html plugin
This commit is contained in:
parent
324d8498ec
commit
9784dc72d7
45 changed files with 132590 additions and 19 deletions
34
gradle-plugin-j2html/build.gradle
Executable file
34
gradle-plugin-j2html/build.gradle
Executable file
|
@ -0,0 +1,34 @@
|
||||||
|
plugins {
|
||||||
|
id 'java-gradle-plugin'
|
||||||
|
alias(libs.plugins.publish)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'java-gradle-plugin'
|
||||||
|
apply plugin: 'com.gradle.plugin-publish'
|
||||||
|
|
||||||
|
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
||||||
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api gradleApi()
|
||||||
|
implementation libs.jsoup
|
||||||
|
implementation libs.javapoet
|
||||||
|
testImplementation gradleTestKit()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.hasProperty('gradle.publish.key')) {
|
||||||
|
gradlePlugin {
|
||||||
|
website = 'https://xbib.org/joerg/gradle-plugins/src/branch/main/gradle-plugin-j2html'
|
||||||
|
vcsUrl = 'https://xbib.org/joerg/gradle-plugins'
|
||||||
|
plugins {
|
||||||
|
j2htmlPlugin {
|
||||||
|
id = 'org.xbib.gradle.plugin.j2html'
|
||||||
|
implementationClass = 'org.xbib.gradle.plugin.j2html.J2HtmlPlugin'
|
||||||
|
version = project.version
|
||||||
|
description = 'Gradle J2HTML plugin'
|
||||||
|
displayName = 'Gradle J2HTML plugin'
|
||||||
|
tags.set(['j2html'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import com.j2html.codegen.generators.SpecializedTagClassCodeGenerator;
|
||||||
|
import com.j2html.codegen.generators.AttributeInterfaceCodeGenerator;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public final class App
|
||||||
|
{
|
||||||
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
final Path relPath = Paths.get("../library/src/main/java/j2html/");
|
||||||
|
final Path absPath = relPath.toAbsolutePath();
|
||||||
|
|
||||||
|
System.out.println("writing in "+absPath);
|
||||||
|
|
||||||
|
//decide if the files should be
|
||||||
|
//deleted or generated
|
||||||
|
final boolean delete = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
AttributeInterfaceCodeGenerator.generate(absPath, delete);
|
||||||
|
SpecializedTagClassCodeGenerator.generate(absPath, delete);
|
||||||
|
//TagCreatorCodeGenerator.print();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
//don't forget to auto-reformat the generated code.
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import com.j2html.codegen.model.AttrD;
|
||||||
|
|
||||||
|
import static com.j2html.codegen.generators.TagCreatorCodeGenerator.containerTags;
|
||||||
|
import static com.j2html.codegen.generators.TagCreatorCodeGenerator.emptyTags;
|
||||||
|
import static com.j2html.codegen.model.AttributesList.attributesDescriptive;
|
||||||
|
import static com.j2html.codegen.model.AttributesList.getCustomAttributesForHtmlTag;
|
||||||
|
import static java.lang.System.*;
|
||||||
|
|
||||||
|
public class Export {
|
||||||
|
|
||||||
|
public static void main(String[] args){
|
||||||
|
for (final String tag : emptyTags()) {
|
||||||
|
out.print("EMPTY-ELEMENT[");
|
||||||
|
out.print(tag);
|
||||||
|
out.print("]");
|
||||||
|
out.println();
|
||||||
|
}
|
||||||
|
for (final String tag : containerTags()) {
|
||||||
|
out.print("ELEMENT[");
|
||||||
|
out.print(tag);
|
||||||
|
out.print("]");
|
||||||
|
out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
out.println();
|
||||||
|
|
||||||
|
for(AttrD attr : attributesDescriptive()){
|
||||||
|
if(attr.hasArgument){
|
||||||
|
out.print("STRING");
|
||||||
|
}else{
|
||||||
|
out.print("BOOLEAN");
|
||||||
|
}
|
||||||
|
out.print("[");
|
||||||
|
out.print(attr.attr);
|
||||||
|
out.print("]");
|
||||||
|
out.println();
|
||||||
|
for(String tag : attr.tags){
|
||||||
|
out.print("ATTRIBUTE[");
|
||||||
|
out.print(tag);
|
||||||
|
out.print(":");
|
||||||
|
out.print(attr.attr);
|
||||||
|
out.print("]");
|
||||||
|
out.println();
|
||||||
|
}
|
||||||
|
out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import com.j2html.codegen.Model.Node;
|
||||||
|
import com.squareup.javapoet.*;
|
||||||
|
|
||||||
|
import javax.lang.model.element.Modifier;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.j2html.codegen.Model.Metadata.ON_OFF;
|
||||||
|
import static com.j2html.codegen.Model.Metadata.SELF_CLOSING;
|
||||||
|
|
||||||
|
public class Generator {
|
||||||
|
|
||||||
|
public static final ClassName INSTANCE = ClassName.get("j2html.tags", "IInstance");
|
||||||
|
public static final ClassName TAG = ClassName.get("j2html.tags", "Tag");
|
||||||
|
public static final ClassName EMPTY_TAG = ClassName.get("j2html.tags", "EmptyTag");
|
||||||
|
public static final ClassName CONTAINER_TAG = ClassName.get("j2html.tags", "ContainerTag");
|
||||||
|
|
||||||
|
public static void main(String... args) throws IOException {
|
||||||
|
Path path = Paths.get("j2html-codegen", "src", "test", "resources", "html.model");
|
||||||
|
String definitions = new String(Files.readAllBytes(path));
|
||||||
|
Model model = new Model();
|
||||||
|
Parser.parse(definitions, model);
|
||||||
|
|
||||||
|
Path dir = Paths.get("/j2html/generated-source");
|
||||||
|
Files.createDirectories(dir);
|
||||||
|
generate(dir, "j2html.tags.attributes", "j2html.tags.specialized", model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void generate(Path root, String attributePkg, String elementPkg, Model model) throws IOException {
|
||||||
|
Map<String, JavaFile> attributes = generateAttributePackage(attributePkg, model);
|
||||||
|
for (JavaFile file : attributes.values()) {
|
||||||
|
file.writeTo(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, JavaFile> elements = generateElementPackage(elementPkg, model, attributes);
|
||||||
|
for (JavaFile file : elements.values()) {
|
||||||
|
file.writeTo(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, JavaFile> generateElementPackage(String pkg, Model model, Map<String, JavaFile> attributes) {
|
||||||
|
Map<String, JavaFile> files = new HashMap<>();
|
||||||
|
|
||||||
|
// Convert all elements into classes.
|
||||||
|
for (Node element : model.elements()) {
|
||||||
|
ClassName className = ClassName.get(pkg, capitalize(element.name) + "Tag");
|
||||||
|
|
||||||
|
TypeSpec.Builder type = defineElementClass(element, className);
|
||||||
|
|
||||||
|
// Assign attributes to this element.
|
||||||
|
for (Node attribute : element.children) {
|
||||||
|
JavaFile file = attributes.get(attribute.name);
|
||||||
|
type.addSuperinterface(
|
||||||
|
ParameterizedTypeName.get(
|
||||||
|
ClassName.get(file.packageName, file.typeSpec.name),
|
||||||
|
className
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
files.put(
|
||||||
|
element.name,
|
||||||
|
JavaFile.builder(pkg, type.build())
|
||||||
|
.skipJavaLangImports(true)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, JavaFile> generateAttributePackage(String pkg, Model model) {
|
||||||
|
Map<String, JavaFile> files = new HashMap<>();
|
||||||
|
|
||||||
|
// Convert all attributes into classes.
|
||||||
|
for (Node attribute : model.attributes()) {
|
||||||
|
TypeSpec.Builder type = defineAttributeClass(pkg, attribute);
|
||||||
|
|
||||||
|
if (attribute.type.equals(Node.Type.STRING)) {
|
||||||
|
defineStringAttributeMethods(attribute, type);
|
||||||
|
} else if (attribute.type.equals(Node.Type.BOOLEAN) && !attribute.is(ON_OFF)) {
|
||||||
|
defineBooleanAttributeMethods(attribute, type);
|
||||||
|
} else if (attribute.type.equals(Node.Type.BOOLEAN) && attribute.is(ON_OFF)) {
|
||||||
|
defineOnOffAttributeMethods(attribute, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
files.put(
|
||||||
|
attribute.name,
|
||||||
|
JavaFile.builder(pkg, type.build())
|
||||||
|
.skipJavaLangImports(true)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TypeSpec.Builder defineElementClass(Node element, ClassName className) {
|
||||||
|
MethodSpec constructor = MethodSpec.constructorBuilder()
|
||||||
|
.addModifiers(Modifier.PUBLIC)
|
||||||
|
.addStatement("super(\"" + element.name + "\")")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TypeSpec.Builder type = TypeSpec.classBuilder(className)
|
||||||
|
.addModifiers(Modifier.PUBLIC)
|
||||||
|
.superclass(
|
||||||
|
ParameterizedTypeName.get(element.is(SELF_CLOSING) ? EMPTY_TAG : CONTAINER_TAG, className)
|
||||||
|
)
|
||||||
|
.addMethod(constructor);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TypeSpec.Builder defineAttributeClass(String pkg, Node attribute) {
|
||||||
|
ClassName name = ClassName.get(pkg, "I" + capitalize(attribute.name));
|
||||||
|
return TypeSpec.interfaceBuilder(name)
|
||||||
|
.addSuperinterface(ParameterizedTypeName.get(INSTANCE, TypeVariableName.get("T")))
|
||||||
|
.addTypeVariable(TypeVariableName.get("T", ParameterizedTypeName.get(TAG, TypeVariableName.get("T"))))
|
||||||
|
.addModifiers(Modifier.PUBLIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void defineBooleanAttributeMethods(Node attribute, TypeSpec.Builder type) {
|
||||||
|
MethodSpec with = MethodSpec.methodBuilder(methodName("is", attribute.name))
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
|
||||||
|
.addStatement("return self().attr(\"" + attribute.name + "\")")
|
||||||
|
.returns(TypeVariableName.get("T"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
MethodSpec withCond = MethodSpec.methodBuilder(methodName("withCond", attribute.name))
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
|
||||||
|
.addParameter(TypeName.BOOLEAN, "enable", Modifier.FINAL)
|
||||||
|
.addStatement("return enable ? self().attr(\"" + attribute.name + "\") : self()")
|
||||||
|
.returns(TypeVariableName.get("T"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
type.addMethod(with);
|
||||||
|
type.addMethod(withCond);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void defineOnOffAttributeMethods(Node attribute, TypeSpec.Builder type) {
|
||||||
|
MethodSpec with = MethodSpec.methodBuilder(methodName("is", attribute.name))
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
|
||||||
|
.addStatement("return self().attr(\"" + attribute.name + "\", \"on\")")
|
||||||
|
.returns(TypeVariableName.get("T"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
MethodSpec withCond = MethodSpec.methodBuilder(methodName("withCond", attribute.name))
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
|
||||||
|
.addParameter(TypeName.BOOLEAN, "enable", Modifier.FINAL)
|
||||||
|
.addStatement("return enable ? self().attr(\"" + attribute.name + "\", \"on\") : self()")
|
||||||
|
.returns(TypeVariableName.get("T"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
type.addMethod(with);
|
||||||
|
type.addMethod(withCond);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void defineStringAttributeMethods(Node attribute, TypeSpec.Builder type) {
|
||||||
|
MethodSpec with = MethodSpec.methodBuilder(methodName("with", attribute.name))
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
|
||||||
|
.addParameter(String.class, parameter(attribute), Modifier.FINAL)
|
||||||
|
.addStatement("return self().attr(\"" + attribute.name + "\", " + parameter(attribute) + ")")
|
||||||
|
.returns(TypeVariableName.get("T"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
MethodSpec withCond = MethodSpec.methodBuilder(methodName("withCond", attribute.name))
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
|
||||||
|
.addParameter(TypeName.BOOLEAN, "enable", Modifier.FINAL)
|
||||||
|
.addParameter(String.class, parameter(attribute), Modifier.FINAL)
|
||||||
|
.addStatement("return enable ? self().attr(\"" + attribute.name + "\", " + parameter(attribute) + ") : self()")
|
||||||
|
.returns(TypeVariableName.get("T"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
type.addMethod(with);
|
||||||
|
type.addMethod(withCond);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String parameter(Node attribute) {
|
||||||
|
return attribute.name + "_";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String methodName(String... words) {
|
||||||
|
String[] camelCase = new String[words.length];
|
||||||
|
camelCase[0] = words[0];
|
||||||
|
for (int i = 1; i < words.length; i++) {
|
||||||
|
camelCase[i] = capitalize(words[i]);
|
||||||
|
}
|
||||||
|
return String.join("", camelCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String capitalize(String word) {
|
||||||
|
return word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public final class GeneratorUtil {
|
||||||
|
|
||||||
|
public static final void deleteAllFilesInDir(final Path dir) throws IOException {
|
||||||
|
for(final File file : dir.toFile().listFiles()){
|
||||||
|
System.out.println("deleting " + file.toPath());
|
||||||
|
Files.delete(file.toPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
171
gradle-plugin-j2html/src/main/java/com/j2html/codegen/Model.java
Normal file
171
gradle-plugin-j2html/src/main/java/com/j2html/codegen/Model.java
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static com.j2html.codegen.Model.Metadata.ON_OFF;
|
||||||
|
import static com.j2html.codegen.Model.Metadata.SELF_CLOSING;
|
||||||
|
import static com.j2html.codegen.Model.Node.Type.*;
|
||||||
|
|
||||||
|
public class Model implements Parser.Listener {
|
||||||
|
|
||||||
|
private Map<String, Node> elements;
|
||||||
|
private Map<String, Node> attributes;
|
||||||
|
|
||||||
|
public Model() {
|
||||||
|
elements = new LinkedHashMap<>();
|
||||||
|
attributes = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Node> elements(){
|
||||||
|
return elements.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Node> attributes(){
|
||||||
|
return attributes.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node addElement(String name) {
|
||||||
|
return add(ELEMENT, name, elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node addBooleanAttribute(String name) {
|
||||||
|
return add(BOOLEAN, name, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node addStringAttribute(String name) {
|
||||||
|
return add(STRING, name, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node element(String name) {
|
||||||
|
if (!elements.containsKey(name)) {
|
||||||
|
throw new NodeDoesNotExist(name);
|
||||||
|
}
|
||||||
|
return elements.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node attribute(String name) {
|
||||||
|
if (!attributes.containsKey(name)) {
|
||||||
|
throw new NodeDoesNotExist(name);
|
||||||
|
}
|
||||||
|
return attributes.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node add(Node.Type type, String name, Map<String, Node> nodes) {
|
||||||
|
if (nodes.containsKey(name)) {
|
||||||
|
throw new NodeAlreadyExists(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node node = new Node(type, name);
|
||||||
|
nodes.put(name, node);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lineCommented(int line, String txt) {
|
||||||
|
// Ignore.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void elementDefined(int line, String name) {
|
||||||
|
attempt(() -> addElement(name), line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void emptyElementDefined(int line, String name) {
|
||||||
|
attempt(() -> addElement(name).annotate(SELF_CLOSING), line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void booleanDefined(int line, String name) {
|
||||||
|
attempt(() -> addBooleanAttribute(name), line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOffDefined(int line, String name) {
|
||||||
|
attempt(() -> addBooleanAttribute(name).annotate(ON_OFF), line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stringDefined(int line, String name) {
|
||||||
|
attempt(() -> addStringAttribute(name), line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attributeDefined(int line, String element, String name) {
|
||||||
|
attempt(() -> element(element).addChild(attribute(name)), line);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidLine(int line, String txt) {
|
||||||
|
throw new RuntimeException("Invalid line [" + line + "]: " + txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface Unsafe {
|
||||||
|
void call() throws RuntimeException;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void attempt(Unsafe operation, int line) {
|
||||||
|
try {
|
||||||
|
operation.call();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw new InvalidModel(e, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Node {
|
||||||
|
enum Type {
|
||||||
|
ELEMENT,
|
||||||
|
BOOLEAN,
|
||||||
|
STRING
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Type type;
|
||||||
|
public final String name;
|
||||||
|
public final List<Metadata> metadata;
|
||||||
|
public final List<Node> children;
|
||||||
|
|
||||||
|
private Node(Type type, String name) {
|
||||||
|
this.type = type;
|
||||||
|
this.name = name;
|
||||||
|
this.metadata = new ArrayList<>();
|
||||||
|
this.children = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void annotate(Metadata meta) {
|
||||||
|
metadata.add(meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChild(Node node) {
|
||||||
|
children.add(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean is(Metadata annotation){
|
||||||
|
return metadata.contains(annotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Metadata {
|
||||||
|
SELF_CLOSING,
|
||||||
|
ON_OFF,
|
||||||
|
OBSOLETE
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class InvalidModel extends RuntimeException {
|
||||||
|
public InvalidModel(Exception cause, int line) {
|
||||||
|
super(cause.getMessage() + ". At line " + line, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NodeAlreadyExists extends RuntimeException {
|
||||||
|
public NodeAlreadyExists(String name) {
|
||||||
|
super("Node already exists: " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NodeDoesNotExist extends RuntimeException {
|
||||||
|
public NodeDoesNotExist(String name) {
|
||||||
|
super("Node does not exist: " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class Parser {
|
||||||
|
|
||||||
|
private static final Pattern EMPTY_LINE_PATTERN = Pattern.compile("\\s*");
|
||||||
|
private static final Pattern COMMENT_PATTERN = Pattern.compile("#.*");
|
||||||
|
private static final Pattern NODE_PATTERN = Pattern.compile("(?<type>ELEMENT|EMPTY-ELEMENT|BOOLEAN|ONOFF|STRING)\\[(?<name>\\S+)\\]");
|
||||||
|
private static final Pattern ATTRIBUTE_PATTERN = Pattern.compile("ATTRIBUTE\\[(?<element>\\S+):(?<name>\\S+)\\]");
|
||||||
|
|
||||||
|
public interface Listener {
|
||||||
|
void lineCommented(int line, String txt);
|
||||||
|
|
||||||
|
void elementDefined(int line, String name);
|
||||||
|
|
||||||
|
void emptyElementDefined(int line, String name);
|
||||||
|
|
||||||
|
void booleanDefined(int line, String name);
|
||||||
|
|
||||||
|
void onOffDefined(int line, String name);
|
||||||
|
|
||||||
|
void stringDefined(int line, String name);
|
||||||
|
|
||||||
|
void attributeDefined(int line, String element, String name);
|
||||||
|
|
||||||
|
void invalidLine(int line, String txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void parse(String txt, Listener listener) {
|
||||||
|
String[] lines = txt.split("[\r\n]+");
|
||||||
|
|
||||||
|
for (int i = 0; i < lines.length; i++) {
|
||||||
|
int number = i + 1;
|
||||||
|
String line = lines[i];
|
||||||
|
|
||||||
|
if (match(EMPTY_LINE_PATTERN, line)) continue;
|
||||||
|
|
||||||
|
if (match(COMMENT_PATTERN, line, matcher -> {
|
||||||
|
listener.lineCommented(number, line);
|
||||||
|
})) continue;
|
||||||
|
|
||||||
|
if (match(NODE_PATTERN, line, matcher -> {
|
||||||
|
String type = matcher.group("type");
|
||||||
|
String name = matcher.group("name");
|
||||||
|
switch (type) {
|
||||||
|
case "ELEMENT":
|
||||||
|
listener.elementDefined(number, name);
|
||||||
|
break;
|
||||||
|
case "EMPTY-ELEMENT":
|
||||||
|
listener.emptyElementDefined(number, name);
|
||||||
|
break;
|
||||||
|
case "BOOLEAN":
|
||||||
|
listener.booleanDefined(number, name);
|
||||||
|
break;
|
||||||
|
case "ONOFF":
|
||||||
|
listener.onOffDefined(number, name);
|
||||||
|
break;
|
||||||
|
case "STRING":
|
||||||
|
listener.stringDefined(number, name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})) continue;
|
||||||
|
|
||||||
|
if (match(ATTRIBUTE_PATTERN, line, matcher -> {
|
||||||
|
listener.attributeDefined(
|
||||||
|
number,
|
||||||
|
matcher.group("element"),
|
||||||
|
matcher.group("name")
|
||||||
|
);
|
||||||
|
})) continue;
|
||||||
|
|
||||||
|
listener.invalidLine(number, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean match(Pattern pattern, String txt) {
|
||||||
|
return pattern.matcher(txt).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean match(Pattern pattern, String txt, Consumer<Matcher> onMatch) {
|
||||||
|
Matcher matcher = pattern.matcher(txt);
|
||||||
|
if (matcher.matches()) {
|
||||||
|
onMatch.accept(matcher);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
package com.j2html.codegen.generators;
|
||||||
|
|
||||||
|
import com.j2html.codegen.GeneratorUtil;
|
||||||
|
import com.j2html.codegen.model.AttrD;
|
||||||
|
import com.j2html.codegen.model.AttributesList;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public final class AttributeInterfaceCodeGenerator {
|
||||||
|
|
||||||
|
private static final String relPath = "tags/attributes/";
|
||||||
|
|
||||||
|
public static void generate(final Path absPath, final boolean delete) throws IOException {
|
||||||
|
|
||||||
|
//delete all files in the directory for fresh generation
|
||||||
|
final Path dir = Paths.get(absPath.toString(),relPath);
|
||||||
|
GeneratorUtil.deleteAllFilesInDir(dir);
|
||||||
|
|
||||||
|
for (final AttrD attr : AttributesList.attributesDescriptive()) {
|
||||||
|
final Path path = makePath(attr.attr, absPath);
|
||||||
|
final String interfaceName = interfaceNameFromAttribute(attr.attr)+"<T extends Tag<T>>";
|
||||||
|
/*
|
||||||
|
IFormAction<T extends Tag<T>> extends IInstance<T>
|
||||||
|
|
||||||
|
default T withFormAction(String formAction){
|
||||||
|
return self().attr("formaction", formAction);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
final String interfaceStr = getInterfaceTemplate(
|
||||||
|
interfaceName,
|
||||||
|
Optional.of("IInstance<T>"),
|
||||||
|
Arrays.asList("j2html.tags.Tag","j2html.tags.IInstance"),
|
||||||
|
interfaceNameFromAttribute(attr.attr).substring(1),
|
||||||
|
attr
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!delete) {
|
||||||
|
System.out.println("writing to "+path);
|
||||||
|
Files.write(path, interfaceStr.getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getPackage(){
|
||||||
|
return "package j2html.tags.attributes;\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String makeReturnTypeAndMethodName(final String name){
|
||||||
|
return "default "+ "T "+name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getInterfaceTemplate(
|
||||||
|
final String interfaceName,
|
||||||
|
final Optional<String> optExtends,
|
||||||
|
final List<String> imports,
|
||||||
|
final String interfaceNameSimple,
|
||||||
|
final AttrD attrD
|
||||||
|
){
|
||||||
|
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.append(getPackage());
|
||||||
|
sb.append("\n");
|
||||||
|
|
||||||
|
for(String importName : imports){
|
||||||
|
sb.append("import ").append(importName).append(";\n");
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
sb.append("public interface ")
|
||||||
|
.append(interfaceName);
|
||||||
|
|
||||||
|
optExtends.ifPresent(ext -> sb.append(" extends ").append(ext).append(" "));
|
||||||
|
|
||||||
|
sb.append(" {\n");
|
||||||
|
|
||||||
|
//interface contents
|
||||||
|
/*
|
||||||
|
IFormAction<T extends Tag> extends IInstance<T>
|
||||||
|
|
||||||
|
default T withFormAction(String formAction){
|
||||||
|
return self().attr("formaction", formAction);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
//IMPORTANT: '_' added as suffix to mitigate problems
|
||||||
|
//where attributes are java keywords. Just to make it consistent and avoid special cases.
|
||||||
|
final String attrName = interfaceNameSimple.toLowerCase();
|
||||||
|
final String paramName = attrName+"_";
|
||||||
|
|
||||||
|
//depending on if the attribute has an argument or not,
|
||||||
|
//generate methods according to the convention in Tag.java
|
||||||
|
// arg -> with$ATTR(arg), withCond$ATTR(condition, arg)
|
||||||
|
// no arg -> is$ATTR(), withCond$ATTR(condition)
|
||||||
|
|
||||||
|
//append the 'with$ATTR' method
|
||||||
|
writeAttributeMethod(interfaceNameSimple, attrD, sb, attrName, paramName);
|
||||||
|
writeAttributeMethodCond(interfaceNameSimple, attrD, sb, attrName, paramName);
|
||||||
|
|
||||||
|
sb.append("}\n");
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addAttributeNoArg(final StringBuilder sb, final String attrName){
|
||||||
|
//generate the code to add an attribute without an argument
|
||||||
|
|
||||||
|
//there are some special attributes
|
||||||
|
//which do take an argument, but where the argument
|
||||||
|
//is boolean (meaning on/off, yes/no and the like)
|
||||||
|
sb.append("self().attr(\"");
|
||||||
|
if (attrName.equals("autocomplete")){
|
||||||
|
sb.append(attrName).append("\",\"on\"");
|
||||||
|
} else {
|
||||||
|
sb.append(attrName).append("\"");
|
||||||
|
}
|
||||||
|
sb.append(");\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeAttributeMethodCond(String interfaceNameSimple, AttrD attrD, StringBuilder sb, String attrName, String paramName) {
|
||||||
|
|
||||||
|
sb.append(makeReturnTypeAndMethodName("withCond"+interfaceNameSimple));
|
||||||
|
|
||||||
|
if(attrD.hasArgument){
|
||||||
|
//add a variant where you can specify the argument
|
||||||
|
|
||||||
|
sb.append("(final boolean enable, final String ").append(paramName).append(") {");
|
||||||
|
|
||||||
|
sb.append("if (enable){\n");
|
||||||
|
sb.append("self().attr(\"").append(attrName).append("\", ").append(paramName).append(");\n");
|
||||||
|
sb.append("}\n");
|
||||||
|
|
||||||
|
sb.append("return self();\n");
|
||||||
|
}else{
|
||||||
|
//add a variant where you can toggle the attribute
|
||||||
|
|
||||||
|
sb.append("(final boolean enable) {");
|
||||||
|
sb.append("if (enable){\n");
|
||||||
|
addAttributeNoArg(sb, attrName);
|
||||||
|
sb.append("}\n");
|
||||||
|
sb.append("return self();\n");
|
||||||
|
}
|
||||||
|
sb.append("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeAttributeMethod(String interfaceNameSimple, AttrD attrD, StringBuilder sb, String attrName, String paramName) {
|
||||||
|
|
||||||
|
sb.append(makeReturnTypeAndMethodName(
|
||||||
|
((attrD.hasArgument)?"with":"is")+interfaceNameSimple)
|
||||||
|
);
|
||||||
|
|
||||||
|
if(attrD.hasArgument){
|
||||||
|
//add a variant where you can specify the argument
|
||||||
|
|
||||||
|
sb.append("(final String ").append(paramName).append(") {")
|
||||||
|
|
||||||
|
.append("return self().attr(\"").append(attrName).append("\", ").append(paramName).append(");\n");
|
||||||
|
}else{
|
||||||
|
//add a variant where you can toggle the attribute
|
||||||
|
|
||||||
|
sb.append("() {");
|
||||||
|
|
||||||
|
addAttributeNoArg(sb, attrName);
|
||||||
|
|
||||||
|
sb.append("return self();\n");
|
||||||
|
}
|
||||||
|
sb.append("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String interfaceNameFromAttribute(String attribute){
|
||||||
|
String res = attribute.substring(0,1).toUpperCase()+attribute.substring(1);
|
||||||
|
return "I" + res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path makePath(String tagLowerCase, final Path absPath){
|
||||||
|
final String filename = interfaceNameFromAttribute(tagLowerCase)+".java";
|
||||||
|
return Paths.get(absPath.toString(),relPath,filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
package com.j2html.codegen.generators;
|
||||||
|
|
||||||
|
|
||||||
|
import com.j2html.codegen.GeneratorUtil;
|
||||||
|
import com.j2html.codegen.model.AttributesList;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.j2html.codegen.generators.TagCreatorCodeGenerator.containerTags;
|
||||||
|
import static com.j2html.codegen.generators.TagCreatorCodeGenerator.emptyTags;
|
||||||
|
|
||||||
|
public final class SpecializedTagClassCodeGenerator {
|
||||||
|
|
||||||
|
private static final String relPath = "tags/specialized";
|
||||||
|
|
||||||
|
public static void generate(final Path absPath, final boolean delete) throws IOException {
|
||||||
|
|
||||||
|
//delete all files in the directory for fresh generation
|
||||||
|
final Path dir = Paths.get(absPath.toString(),relPath);
|
||||||
|
GeneratorUtil.deleteAllFilesInDir(dir);
|
||||||
|
|
||||||
|
//the delete argument serves to give the possibility
|
||||||
|
//to delete the classes that were written before
|
||||||
|
System.out.println("// EmptyTags, generated in " + SpecializedTagClassCodeGenerator.class);
|
||||||
|
|
||||||
|
for (final String tag : emptyTags()) {
|
||||||
|
final String className = classNameFromTag(tag);
|
||||||
|
final Path path = makePath(absPath,tag);
|
||||||
|
|
||||||
|
final List<String> interfaceNames = getInterfaceNamesForTag(tag);
|
||||||
|
|
||||||
|
final String classString =
|
||||||
|
getClassTemplate(
|
||||||
|
className,
|
||||||
|
Optional.of("EmptyTag<"+className+">"),
|
||||||
|
Arrays.asList(
|
||||||
|
"j2html.tags.EmptyTag",
|
||||||
|
"j2html.tags.attributes.*"
|
||||||
|
),
|
||||||
|
tag,
|
||||||
|
interfaceNames
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
public InputTag() {
|
||||||
|
super("input");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(!delete){
|
||||||
|
System.out.println("writing to "+path);
|
||||||
|
Files.write(path, classString.getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("// ContainerTags, generated in " + SpecializedTagClassCodeGenerator.class);
|
||||||
|
|
||||||
|
for (final String tag : containerTags()) {
|
||||||
|
final Path path = makePath(absPath, tag);
|
||||||
|
final String className = classNameFromTag(tag);
|
||||||
|
|
||||||
|
final List<String> interfaceNames = getInterfaceNamesForTag(tag);
|
||||||
|
|
||||||
|
final String classString =
|
||||||
|
getClassTemplate(
|
||||||
|
className,
|
||||||
|
Optional.of("ContainerTag<"+className+">"),
|
||||||
|
Arrays.asList(
|
||||||
|
"j2html.tags.ContainerTag",
|
||||||
|
"j2html.tags.attributes.*"
|
||||||
|
),
|
||||||
|
tag,
|
||||||
|
interfaceNames
|
||||||
|
);
|
||||||
|
|
||||||
|
if(delete){
|
||||||
|
if(Files.exists(path)) {
|
||||||
|
System.out.println("deleting " + path);
|
||||||
|
Files.delete(path);
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
System.out.println("writing to "+path);
|
||||||
|
Files.write(path, classString.getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static String classNameFromTag(String tageNameLowerCase){
|
||||||
|
String res = tageNameLowerCase.substring(0,1).toUpperCase()+tageNameLowerCase.substring(1);
|
||||||
|
return res + "Tag";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path makePath(final Path absPath, String tagLowerCase){
|
||||||
|
final String filename = classNameFromTag(tagLowerCase)+".java";
|
||||||
|
return Paths.get(absPath.toString(),relPath,filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getPackage(){
|
||||||
|
return "package j2html.tags.specialized;\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getClassTemplate(
|
||||||
|
final String className,
|
||||||
|
final Optional<String> optExtends,
|
||||||
|
final List<String> imports,
|
||||||
|
final String tag,
|
||||||
|
final List<String> interfaces
|
||||||
|
){
|
||||||
|
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.append(getPackage());
|
||||||
|
sb.append("\n");
|
||||||
|
|
||||||
|
for(String importName : imports){
|
||||||
|
sb.append("import ").append(importName).append(";\n");
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
sb.append("public final class ")
|
||||||
|
.append(className)
|
||||||
|
.append(" ");
|
||||||
|
|
||||||
|
optExtends.ifPresent(ext -> sb.append("extends ").append(ext).append(" "));
|
||||||
|
|
||||||
|
//add the 'implements' clause
|
||||||
|
if(!interfaces.isEmpty()) {
|
||||||
|
sb.append("\n");
|
||||||
|
sb.append("implements ");
|
||||||
|
|
||||||
|
final List<String> genericInterfaceNames
|
||||||
|
= interfaces.stream().map(iName -> iName+"<"+className+">")
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
sb.append(
|
||||||
|
String.join(",", genericInterfaceNames)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(" {\n");
|
||||||
|
|
||||||
|
//class contents
|
||||||
|
sb.append("public ")
|
||||||
|
.append(className)
|
||||||
|
.append("() {")
|
||||||
|
.append("super(\"").append(tag).append("\");")
|
||||||
|
.append("}\n");
|
||||||
|
|
||||||
|
sb.append("}\n");
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> getInterfaceNamesForTag(final String tagNameLowercase){
|
||||||
|
return AttributesList.getCustomAttributesForHtmlTag(tagNameLowercase)
|
||||||
|
.stream()
|
||||||
|
.map(
|
||||||
|
AttributeInterfaceCodeGenerator::interfaceNameFromAttribute
|
||||||
|
).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
package com.j2html.codegen.generators;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class TagCreatorCodeGenerator {
|
||||||
|
|
||||||
|
public static void print() {
|
||||||
|
|
||||||
|
System.out.println("// EmptyTags, generated in " + TagCreatorCodeGenerator.class);
|
||||||
|
|
||||||
|
for (String tag : emptyTags()) {
|
||||||
|
final String className = SpecializedTagClassCodeGenerator.classNameFromTag(tag);
|
||||||
|
final String publicstaticTypeMethod = "public static "+className+" "+tag+" ";
|
||||||
|
final String castReturn = " return ("+className+") ";
|
||||||
|
final String construct = " new "+className+"()";
|
||||||
|
|
||||||
|
String emptyA1 = publicstaticTypeMethod + "()";
|
||||||
|
String emptyA2 = "{ return "+construct+"; }";
|
||||||
|
// Attr shorthands
|
||||||
|
String emptyB1 = publicstaticTypeMethod + "(Attr.ShortForm shortAttr)";
|
||||||
|
String emptyB2 = "{ "+castReturn+" Attr.addTo("+construct+", shortAttr); }";
|
||||||
|
// Print
|
||||||
|
System.out.println(String.format("%-80s%1s", emptyA1, emptyA2));
|
||||||
|
System.out.println(String.format("%-80s%1s", emptyB1, emptyB2));
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("// ContainerTags, generated in " + TagCreatorCodeGenerator.class);
|
||||||
|
|
||||||
|
for (String tag : containerTags()) {
|
||||||
|
final String className = SpecializedTagClassCodeGenerator.classNameFromTag(tag);
|
||||||
|
final String publicstaticTypeMethod = "public static "+className+" "+tag+" ";
|
||||||
|
final String castReturn = " return ("+className+") ";
|
||||||
|
final String construct = " new "+className+"()";
|
||||||
|
|
||||||
|
String containerA1 = publicstaticTypeMethod+ "()";
|
||||||
|
String containerA2 = "{ "+castReturn + construct + "; }";
|
||||||
|
|
||||||
|
String containerB1 = publicstaticTypeMethod + "(String text)";
|
||||||
|
String containerB2 = "{ "+castReturn + construct + ".withText(text); }";
|
||||||
|
|
||||||
|
String containerC1 = publicstaticTypeMethod + "(DomContent... dc)";
|
||||||
|
String containerC2 = "{ "+castReturn + construct+".with(dc); }";
|
||||||
|
// Attr shorthands
|
||||||
|
String containerD1 = publicstaticTypeMethod + "(Attr.ShortForm shortAttr)";
|
||||||
|
String containerD2 = "{ "+castReturn+" Attr.addTo("+construct+", shortAttr); }";
|
||||||
|
|
||||||
|
String containerE1 = publicstaticTypeMethod + "(Attr.ShortForm shortAttr, String text)";
|
||||||
|
String containerE2 = "{ "+castReturn+" Attr.addTo("+construct+".withText(text), shortAttr); }";
|
||||||
|
|
||||||
|
String containerF1 = publicstaticTypeMethod + "(Attr.ShortForm shortAttr, DomContent... dc)";
|
||||||
|
String containerF2 = "{ "+castReturn+" Attr.addTo("+construct+".with(dc), shortAttr); }";
|
||||||
|
// Print
|
||||||
|
System.out.println(String.format("%-80s%1s", containerA1, containerA2));
|
||||||
|
System.out.println(String.format("%-80s%1s", containerB1, containerB2));
|
||||||
|
System.out.println(String.format("%-80s%1s", containerC1, containerC2));
|
||||||
|
System.out.println(String.format("%-80s%1s", containerD1, containerD2));
|
||||||
|
System.out.println(String.format("%-80s%1s", containerE1, containerE2));
|
||||||
|
System.out.println(String.format("%-80s%1s", containerF1, containerF2));
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a method that contains all ContainerTags, there is nothing below it
|
||||||
|
public static List<String> emptyTags() {
|
||||||
|
return Arrays.asList(
|
||||||
|
"area",
|
||||||
|
"base",
|
||||||
|
"br",
|
||||||
|
"col",
|
||||||
|
//"!DOCTYPE html",
|
||||||
|
"embed",
|
||||||
|
"hr",
|
||||||
|
"img",
|
||||||
|
"input",
|
||||||
|
"keygen",
|
||||||
|
"link",
|
||||||
|
"meta",
|
||||||
|
"param",
|
||||||
|
"source",
|
||||||
|
"track",
|
||||||
|
"wbr"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> containerTags() {
|
||||||
|
return Arrays.asList(
|
||||||
|
"a",
|
||||||
|
"abbr",
|
||||||
|
"address",
|
||||||
|
"article",
|
||||||
|
"aside",
|
||||||
|
"audio",
|
||||||
|
"b",
|
||||||
|
"bdi",
|
||||||
|
"bdo",
|
||||||
|
"blockquote",
|
||||||
|
"body",
|
||||||
|
"button",
|
||||||
|
"canvas",
|
||||||
|
"caption",
|
||||||
|
"cite",
|
||||||
|
"code",
|
||||||
|
"colgroup",
|
||||||
|
"data",
|
||||||
|
"datalist",
|
||||||
|
"dd",
|
||||||
|
"del",
|
||||||
|
"details",
|
||||||
|
"dfn",
|
||||||
|
"dialog",
|
||||||
|
"div",
|
||||||
|
"dl",
|
||||||
|
"dt",
|
||||||
|
"em",
|
||||||
|
"fieldset",
|
||||||
|
"figcaption",
|
||||||
|
"figure",
|
||||||
|
"footer",
|
||||||
|
"form",
|
||||||
|
"h1",
|
||||||
|
"h2",
|
||||||
|
"h3",
|
||||||
|
"h4",
|
||||||
|
"h5",
|
||||||
|
"h6",
|
||||||
|
"head",
|
||||||
|
"header",
|
||||||
|
"html",
|
||||||
|
"i",
|
||||||
|
"iframe",
|
||||||
|
"ins",
|
||||||
|
"kbd",
|
||||||
|
"label",
|
||||||
|
"legend",
|
||||||
|
"li",
|
||||||
|
"main",
|
||||||
|
"map",
|
||||||
|
"mark",
|
||||||
|
"menu",
|
||||||
|
"menuitem",
|
||||||
|
"meter",
|
||||||
|
"nav",
|
||||||
|
"noscript",
|
||||||
|
"object",
|
||||||
|
"ol",
|
||||||
|
"optgroup",
|
||||||
|
"option",
|
||||||
|
"output",
|
||||||
|
"p",
|
||||||
|
"picture",
|
||||||
|
"pre",
|
||||||
|
"progress",
|
||||||
|
"q",
|
||||||
|
"rp",
|
||||||
|
"rt",
|
||||||
|
"ruby",
|
||||||
|
"s",
|
||||||
|
"samp",
|
||||||
|
"script",
|
||||||
|
"section",
|
||||||
|
"select",
|
||||||
|
"slot",
|
||||||
|
"small",
|
||||||
|
"span",
|
||||||
|
"strong",
|
||||||
|
"style",
|
||||||
|
"sub",
|
||||||
|
"summary",
|
||||||
|
"sup",
|
||||||
|
"table",
|
||||||
|
"tbody",
|
||||||
|
"td",
|
||||||
|
"template",
|
||||||
|
"textarea",
|
||||||
|
"tfoot",
|
||||||
|
"th",
|
||||||
|
"thead",
|
||||||
|
"time",
|
||||||
|
"title",
|
||||||
|
"tr",
|
||||||
|
"u",
|
||||||
|
"ul",
|
||||||
|
"var",
|
||||||
|
"video"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
package com.j2html.codegen.generators;
|
||||||
|
|
||||||
|
import com.j2html.codegen.wattsi.AttributeDefinition;
|
||||||
|
import com.j2html.codegen.wattsi.ElementDefinition;
|
||||||
|
import com.j2html.codegen.wattsi.WattsiSource;
|
||||||
|
import com.squareup.javapoet.ClassName;
|
||||||
|
import com.squareup.javapoet.MethodSpec;
|
||||||
|
import com.squareup.javapoet.TypeSpec;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
|
||||||
|
import javax.lang.model.element.Modifier;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class WattsiGenerator {
|
||||||
|
|
||||||
|
public static void main(String... args) throws IOException {
|
||||||
|
Path source = Paths.get(args[0]);
|
||||||
|
Document doc = Jsoup.parse(source.toFile(), "UTF-8", "https://html.spec.whatwg.org/");
|
||||||
|
WattsiSource wattsi = new WattsiSource(doc);
|
||||||
|
|
||||||
|
List<ElementDefinition> elements = wattsi.elementDefinitions();
|
||||||
|
List<AttributeDefinition> attributes = wattsi.attributeDefinitions();
|
||||||
|
|
||||||
|
// for (ElementDefinition element : elements) {
|
||||||
|
// System.out.println((element.isObsolete() ? "!" : "") + element.name());
|
||||||
|
// for (AttributeDefinition attribute : attributes) {
|
||||||
|
// if (attribute.appliesTo(element)) {
|
||||||
|
// System.out.println(" " + (attribute.isObsolete() ? "!" : "") + attribute.name());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// System.out.println();
|
||||||
|
// }
|
||||||
|
|
||||||
|
for (ElementDefinition element : elements) {
|
||||||
|
ClassName className = ClassName.get(
|
||||||
|
"com.j2html",
|
||||||
|
capitalize(element.name()) + "Tag"
|
||||||
|
);
|
||||||
|
|
||||||
|
TypeSpec.Builder type = TypeSpec.classBuilder(className)
|
||||||
|
.addModifiers(Modifier.PUBLIC);
|
||||||
|
|
||||||
|
if (element.isObsolete()) {
|
||||||
|
type.addAnnotation(Deprecated.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (AttributeDefinition attribute : attributes) {
|
||||||
|
if (attribute.appliesTo(element)) {
|
||||||
|
String name = methodName("with", attribute.name().split("-"));
|
||||||
|
MethodSpec.Builder setter = MethodSpec.methodBuilder(name)
|
||||||
|
.addModifiers(Modifier.PUBLIC)
|
||||||
|
.returns(className)
|
||||||
|
.addStatement("return this");
|
||||||
|
|
||||||
|
if(attribute.isObsolete()){
|
||||||
|
setter.addAnnotation(Deprecated.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
type.addMethod(setter.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(type.build().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// System.out.println(doc.select("dfn"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String methodName(String prefix, String... words){
|
||||||
|
String[] tmp = new String[words.length + 1];
|
||||||
|
tmp[0] = prefix;
|
||||||
|
for(int i = 0; i < words.length; i++){
|
||||||
|
tmp[i+1] = words[i];
|
||||||
|
}
|
||||||
|
return methodName(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String methodName(String... words){
|
||||||
|
String[] camelCase = new String[words.length];
|
||||||
|
camelCase[0] = words[0];
|
||||||
|
for(int i = 1; i < words.length; i++){
|
||||||
|
camelCase[i] = capitalize(words[i]);
|
||||||
|
}
|
||||||
|
return String.join("", camelCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String capitalize(String word){
|
||||||
|
return word.substring(0,1).toUpperCase() + word.substring(1).toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.j2html.codegen.model;
|
||||||
|
|
||||||
|
public final class AttrD {
|
||||||
|
//attribute descriptor
|
||||||
|
|
||||||
|
public final String attr;
|
||||||
|
|
||||||
|
public final boolean hasArgument;
|
||||||
|
|
||||||
|
//the html tags that this attribute can be used on
|
||||||
|
public final String[] tags;
|
||||||
|
|
||||||
|
public AttrD(final String attr, boolean hasArgument){
|
||||||
|
this.attr = attr;
|
||||||
|
this.hasArgument = hasArgument;
|
||||||
|
this.tags = new String[]{};
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttrD(final String attr, boolean hasArgument, final String... tags) {
|
||||||
|
this.attr = attr;
|
||||||
|
this.hasArgument = hasArgument;
|
||||||
|
this.tags = tags;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
package com.j2html.codegen.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class AttributesList {
|
||||||
|
|
||||||
|
//https://www.w3schools.com/tags/ref_attributes.asp
|
||||||
|
|
||||||
|
public static List<String> getCustomAttributesForHtmlTag(final String tagLowercase){
|
||||||
|
|
||||||
|
final List<String> attrs = new ArrayList<>();
|
||||||
|
for(AttrD attrD : attributesDescriptive()){
|
||||||
|
if(
|
||||||
|
Arrays.asList(attrD.tags).contains(tagLowercase)
|
||||||
|
){
|
||||||
|
attrs.add(attrD.attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return attrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<AttrD> attributesDescriptive() {
|
||||||
|
return Arrays.asList(
|
||||||
|
new AttrD("accept", true, "input"),
|
||||||
|
//new AttrD("accept-charset","form"), //contains dashes, TODO
|
||||||
|
//new AttrD("accesskey"), //global attribute
|
||||||
|
new AttrD("action", true, "form"),
|
||||||
|
//"align", not supported in HTML5
|
||||||
|
new AttrD("alt", true, "area","img","input"),
|
||||||
|
new AttrD("async", false, "script"),
|
||||||
|
new AttrD("autocomplete", false, "form","input"),
|
||||||
|
new AttrD("autofocus", false, "button","input","select","textarea"),
|
||||||
|
new AttrD("autoplay", false, "audio","video"),
|
||||||
|
//"bgcolor", not supported in HTMTL5
|
||||||
|
//"border", not supported in HTML5
|
||||||
|
new AttrD("charset", true, "meta","script"),
|
||||||
|
new AttrD("checked", false, "input"),
|
||||||
|
new AttrD("cite", true, "blockquote","del","ins","q"),
|
||||||
|
//"class" already implemented in Tag.java // global attribute
|
||||||
|
new AttrD("cols", true, "textarea"),
|
||||||
|
new AttrD("colspan", true, "td","th"),
|
||||||
|
new AttrD("content", true, "meta"),
|
||||||
|
//"contenteditable" global attribute, should be in Tag.java
|
||||||
|
new AttrD("controls", false, "audio","video"),
|
||||||
|
new AttrD("coords", true, "area"),
|
||||||
|
new AttrD("data", true, "object"),
|
||||||
|
new AttrD("datetime", true, "del","ins","time"),
|
||||||
|
new AttrD("default", false, "track"),
|
||||||
|
new AttrD("defer", false, "script"),
|
||||||
|
//new AttrD("dir"), //global attribute
|
||||||
|
new AttrD("dirname", true, "input","textarea"),
|
||||||
|
new AttrD("disabled",false, "button","fieldset","input","optgroup","option","select","textarea"),
|
||||||
|
new AttrD("download",false, "a","area"),
|
||||||
|
//new AttrD("draggable") global attribute, should be in Tag.java
|
||||||
|
new AttrD("enctype", true, "form"),
|
||||||
|
new AttrD("for", true, "label","output"),
|
||||||
|
new AttrD("form", true, "button","fieldset","input","label","meter","object","output","select","textarea"),
|
||||||
|
new AttrD("formaction", true, "button","input"),
|
||||||
|
new AttrD("headers", true, "td","th"),
|
||||||
|
new AttrD("height", true, "canvas","embed","iframe","img","input","object","video"),
|
||||||
|
//new AttrD("hidden"), global attribute
|
||||||
|
new AttrD("high", true, "meter"),
|
||||||
|
new AttrD("href", true, "a","area","base","link"),
|
||||||
|
new AttrD("hreflang", true, "a","area","link"),
|
||||||
|
//"http-equiv", //TODO: '-' is problematic in code generation
|
||||||
|
//"id" global attribute, should be in Tag.java
|
||||||
|
new AttrD("ismap", false, "img"),
|
||||||
|
new AttrD("kind", true, "track"),
|
||||||
|
new AttrD("label", true, "track","option","optgroup"),
|
||||||
|
//"lang" global attribute, should be in Tag.java
|
||||||
|
new AttrD("list", true, "input"),
|
||||||
|
new AttrD("loop", false, "audio","video"),
|
||||||
|
new AttrD("low", true, "meter"),
|
||||||
|
new AttrD("max", true, "input","meter","progress"),
|
||||||
|
new AttrD("maxlength", true, "input","textarea"),
|
||||||
|
new AttrD("media", true, "a","area","link","source","style"),
|
||||||
|
new AttrD("method", true, "form"),
|
||||||
|
new AttrD("min", true, "input","meter"),
|
||||||
|
new AttrD("multiple", false, "input","select"),
|
||||||
|
new AttrD("muted", false, "video","audio"),
|
||||||
|
new AttrD("name", true, "button","fieldset","form","iframe","input","map","meta","object","output","param","select","slot","textarea"),
|
||||||
|
new AttrD("novalidate", false, "form"),
|
||||||
|
new AttrD("onabort", true, "audio","embed","img","object","video"),
|
||||||
|
new AttrD("onafterprint", true, "body"),
|
||||||
|
new AttrD("onbeforeprint", true, "body"),
|
||||||
|
new AttrD("onbeforeunload", true, "body"),
|
||||||
|
//new AttrD("onblur"), global attribute
|
||||||
|
new AttrD("oncanplay", true, "audio","embed","object","video"),
|
||||||
|
new AttrD("oncanplaythrough", true, "audio","video"),
|
||||||
|
/* a bunch of event attributes that are on all visible elements (so should be in Tag.java)
|
||||||
|
"onchange",
|
||||||
|
"onclick",
|
||||||
|
"oncontextmenu",
|
||||||
|
"oncopy",
|
||||||
|
*/
|
||||||
|
new AttrD("oncuechange", true, "track"),
|
||||||
|
/*
|
||||||
|
"oncut",
|
||||||
|
...
|
||||||
|
"ondrop",
|
||||||
|
*/
|
||||||
|
new AttrD("ondurationchange", true, "audio","video"),
|
||||||
|
new AttrD("onemptied", true, "audio","video"),
|
||||||
|
new AttrD("onended", true, "audio","video"),
|
||||||
|
new AttrD("onerror", true, "audio","body","embed","img","object","script","style","video"),
|
||||||
|
//new AttrD("onfocus"),// global attribute
|
||||||
|
new AttrD("onhashchange", true, "body"),
|
||||||
|
// ... a bunch of event attributes visible on all elements
|
||||||
|
new AttrD("onload", true, "body","iframe","img","input","link","script","style"),
|
||||||
|
new AttrD("onloadeddata", true, "audio","video"),
|
||||||
|
new AttrD("onloadedmetadata", true, "audio","video"),
|
||||||
|
new AttrD("onloadstart", true, "audio","video"),
|
||||||
|
// ... a bunch of event attributes visible on all elements
|
||||||
|
new AttrD("onoffline", true, "body"),
|
||||||
|
new AttrD("ononline", true, "body"),
|
||||||
|
new AttrD("onpagehide", true, "body"),
|
||||||
|
new AttrD("onpageshow", true, "body"),
|
||||||
|
//new AttrD("onpaste"),// global attribute
|
||||||
|
new AttrD("onpause", true, "audio","video"),
|
||||||
|
new AttrD("onplay", true, "audio","video"),
|
||||||
|
new AttrD("onplaying", true, "audio","video"),
|
||||||
|
new AttrD("onpopstate", true, "body"),
|
||||||
|
new AttrD("onprogress", true, "audio","video"),
|
||||||
|
new AttrD("onratechange", true, "audio","video"),
|
||||||
|
new AttrD("onreset", true, "form"),
|
||||||
|
new AttrD("onresize", true, "body"),
|
||||||
|
//new AttrD("onscroll"), //global attribute
|
||||||
|
new AttrD("onsearch", true, "input"),
|
||||||
|
new AttrD("onseeked", true, "audio","video"),
|
||||||
|
new AttrD("onseeking", true, "audio","video"),
|
||||||
|
//new AttrD("onselect"), //global attribute
|
||||||
|
new AttrD("onstalled", true, "audio","video"),
|
||||||
|
new AttrD("onstorage", true, "body"),
|
||||||
|
new AttrD("onsubmit", true, "form"),
|
||||||
|
new AttrD("onsuspend", true, "audio","video"),
|
||||||
|
new AttrD("ontimeupdate", true, "audio","video"),
|
||||||
|
new AttrD("ontoggle", true, "details"),
|
||||||
|
new AttrD("onunload", true, "body"),
|
||||||
|
new AttrD("onvolumechanged", true, "audio","video"),
|
||||||
|
new AttrD("onwaiting", true, "audio","video"),
|
||||||
|
//new AttrD("onwheel"), //global attribute
|
||||||
|
new AttrD("open", false, "details"),
|
||||||
|
new AttrD("optimum", true, "meter"),
|
||||||
|
new AttrD("pattern", true, "input"),
|
||||||
|
new AttrD("placeholder", true, "input","textarea"),
|
||||||
|
new AttrD("poster", true, "video"),
|
||||||
|
new AttrD("preload", true, "audio","video"),
|
||||||
|
new AttrD("readonly", false, "input","textarea"),
|
||||||
|
new AttrD("rel", true, "a","area","form","link"),
|
||||||
|
new AttrD("required", false, "input","select","textarea"),
|
||||||
|
new AttrD("reversed", false, "ol"),
|
||||||
|
new AttrD("rows", true, "textarea"),
|
||||||
|
new AttrD("rowspan", true, "td","th"),
|
||||||
|
new AttrD("sandbox", false, "iframe"),
|
||||||
|
new AttrD("scope", true, "th"),
|
||||||
|
new AttrD("selected", false, "option"),
|
||||||
|
new AttrD("shape", true, "area"),
|
||||||
|
new AttrD("size", true, "input","select"),
|
||||||
|
new AttrD("sizes", true, "img","link","source"),
|
||||||
|
new AttrD("span", true, "col","colgroup"),
|
||||||
|
//new AttrD("spellcheck"), //global attribute
|
||||||
|
new AttrD("src", true, "audio","embed","iframe","img","input","script","source","track","video"),
|
||||||
|
new AttrD("srcdoc", true, "iframe"),
|
||||||
|
new AttrD("srclang", true, "track"),
|
||||||
|
new AttrD("srcset", true, "img","source"),
|
||||||
|
new AttrD("start", true, "ol"),
|
||||||
|
new AttrD("step", true, "input"),
|
||||||
|
//new AttrD("style"), //global attribute
|
||||||
|
//new AttrD("tabindex"), //global attribute
|
||||||
|
new AttrD("target", true, "a","area","base","form"),
|
||||||
|
//new AttrD("title"), //global attribute
|
||||||
|
//new AttrD("translate"),// global attribute
|
||||||
|
new AttrD("type", true, "a","button","embed","input","link","menu","object","script","source","style"),
|
||||||
|
new AttrD("usemap", true, "img","object"),
|
||||||
|
new AttrD("value", true, "button","data","input","li","option","meter","progress","param"),
|
||||||
|
new AttrD("width", true, "canvas","embed","iframe","img","input","object","video"),
|
||||||
|
new AttrD("wrap", true, "textarea")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.j2html.codegen.wattsi;
|
||||||
|
|
||||||
|
public interface AttributeDefinition {
|
||||||
|
|
||||||
|
String name();
|
||||||
|
|
||||||
|
boolean appliesTo(ElementDefinition element);
|
||||||
|
|
||||||
|
boolean isObsolete();
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.j2html.codegen.wattsi;
|
||||||
|
|
||||||
|
public interface ElementDefinition {
|
||||||
|
|
||||||
|
String name();
|
||||||
|
|
||||||
|
boolean isSelfClosing();
|
||||||
|
|
||||||
|
boolean isObsolete();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
package com.j2html.codegen.wattsi;
|
||||||
|
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.nodes.Node;
|
||||||
|
import org.jsoup.nodes.TextNode;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
|
public class WattsiSource {
|
||||||
|
|
||||||
|
private final Document doc;
|
||||||
|
|
||||||
|
private final Set<Reference> obsolete = new HashSet<>();
|
||||||
|
|
||||||
|
public WattsiSource(Document doc) {
|
||||||
|
this.doc = doc;
|
||||||
|
|
||||||
|
// Find where obsolete elements are defined or referenced.
|
||||||
|
Elements obsoleteElements = doc.select("p:contains(Elements in the following list are entirely obsolete) + dl");
|
||||||
|
|
||||||
|
// Convert definitions into references to record obsolete elements.
|
||||||
|
obsoleteElements.select("dt > dfn[element]")
|
||||||
|
.stream()
|
||||||
|
.map(WattsiElement::new)
|
||||||
|
.map(WattsiElement::reference)
|
||||||
|
.forEach(obsolete::add);
|
||||||
|
|
||||||
|
// Extract references to record obsolete elements.
|
||||||
|
obsoleteElements.select("dt > code")
|
||||||
|
.stream()
|
||||||
|
.map(Element::childNodes)
|
||||||
|
.map(Reference::from)
|
||||||
|
.forEach(obsolete::add);
|
||||||
|
|
||||||
|
// Find where obsolete attributes are defined or referenced.
|
||||||
|
Elements obsoleteAttributes = doc.select("p:contains(The following attributes are obsolete) + dl");
|
||||||
|
|
||||||
|
// Convert definitions into references to record obsolete attributes.
|
||||||
|
obsoleteAttributes.select("dt > dfn[element-attr]").stream()
|
||||||
|
.map(WattsiAttribute::new)
|
||||||
|
.map(WattsiAttribute::reference)
|
||||||
|
.forEach(obsolete::add);
|
||||||
|
|
||||||
|
// System.out.println(obsoleteAttributes.select("dt"));
|
||||||
|
|
||||||
|
// obsoleteAttributes.select("dt > code").stream()
|
||||||
|
// .map(Element::childNodes)
|
||||||
|
// .map(Reference::from)
|
||||||
|
// .forEach(System.err::println);
|
||||||
|
|
||||||
|
// System.out.println(
|
||||||
|
// doc.select("dfn[obsolete]")
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ElementDefinition> elementDefinitions() {
|
||||||
|
return doc.select("dfn[element]").stream()
|
||||||
|
.map(WattsiElement::new)
|
||||||
|
.collect(toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AttributeDefinition> attributeDefinitions() {
|
||||||
|
return doc.select("dfn[element-attr]").stream()
|
||||||
|
.map(WattsiAttribute::new)
|
||||||
|
.collect(toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WattsiElement implements ElementDefinition {
|
||||||
|
private final Element dfn;
|
||||||
|
|
||||||
|
WattsiElement(Element dfn) {
|
||||||
|
if (!"dfn".equals(dfn.tagName())) {
|
||||||
|
throw new IllegalArgumentException("Element cannot be defined from: " + dfn);
|
||||||
|
}
|
||||||
|
if (!dfn.hasAttr("element")) {
|
||||||
|
throw new IllegalArgumentException("Does not define an element: " + dfn);
|
||||||
|
}
|
||||||
|
if (dfn.childrenSize() != 1) {
|
||||||
|
throw new IllegalArgumentException("Element cannot have multiple definitions: " + dfn);
|
||||||
|
}
|
||||||
|
this.dfn = dfn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Reference reference() {
|
||||||
|
return Reference.from(dfn.childNodes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
if (dfn.hasAttr("data-x")) {
|
||||||
|
return dfn.attr("data-x");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Reference.from(dfn.childNodes()).key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSelfClosing() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isObsolete() {
|
||||||
|
return obsolete.contains(reference());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WattsiAttribute implements AttributeDefinition {
|
||||||
|
private final Element dfn;
|
||||||
|
|
||||||
|
WattsiAttribute(Element dfn) {
|
||||||
|
if (!"dfn".equals(dfn.tagName())) {
|
||||||
|
throw new IllegalArgumentException("Attribute cannot be defined from: " + dfn);
|
||||||
|
}
|
||||||
|
if (!dfn.hasAttr("element-attr")) {
|
||||||
|
throw new IllegalArgumentException("Does not define an attribute: " + dfn);
|
||||||
|
}
|
||||||
|
if (dfn.childrenSize() != 1) {
|
||||||
|
throw new IllegalArgumentException("Attribute cannot have multiple definitions: " + dfn);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dfn = dfn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Reference reference() {
|
||||||
|
return Reference.from(dfn.childNodes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return reference().text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> targets() {
|
||||||
|
if (dfn.hasAttr("for")) {
|
||||||
|
return Arrays.asList(dfn.attr("for").trim().split(","));
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean appliesTo(ElementDefinition element) {
|
||||||
|
return targets().contains(element.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isObsolete() {
|
||||||
|
return obsolete.contains(reference());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Reference {
|
||||||
|
private final String key;
|
||||||
|
private final String text;
|
||||||
|
|
||||||
|
Reference(String key, String text) {
|
||||||
|
this.key = key;
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return key + "[" + text + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Reference reference = (Reference) o;
|
||||||
|
return key.equals(reference.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Reference from(List<Node> nodes) {
|
||||||
|
if (nodes.stream().allMatch(n -> n instanceof TextNode)) {
|
||||||
|
String txt = nodes.stream()
|
||||||
|
.map(n -> (TextNode) n)
|
||||||
|
.map(TextNode::text)
|
||||||
|
.collect(Collectors.joining(" "));
|
||||||
|
return new Reference(txt, txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Node node : nodes) {
|
||||||
|
if (node instanceof Element) {
|
||||||
|
Element element = (Element) node;
|
||||||
|
if (element.is("code") || element.is("span")) {
|
||||||
|
if (element.hasAttr("data-x")) {
|
||||||
|
return new Reference(element.attr("data-x").toLowerCase(), element.text());
|
||||||
|
} else {
|
||||||
|
return new Reference(element.text().toLowerCase(), element.text());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class AppTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
public void shouldAnswerWithTrue()
|
||||||
|
{
|
||||||
|
//dummy, just to conform to the default mvn
|
||||||
|
//directory layout
|
||||||
|
assertTrue( true );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import com.j2html.codegen.generators.TagCreatorCodeGenerator;
|
||||||
|
import com.j2html.codegen.wattsi.ElementDefinition;
|
||||||
|
import com.j2html.codegen.wattsi.WattsiSource;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class CodeGeneratorComplianceTests {
|
||||||
|
|
||||||
|
private WattsiSource specification;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws IOException {
|
||||||
|
Path source = Paths.get("src","test","resources","2022-01.wattsi");
|
||||||
|
Document doc = Jsoup.parse(source.toFile(), "UTF-8", "https://html.spec.whatwg.org/");
|
||||||
|
specification = new WattsiSource(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> generatedElements(){
|
||||||
|
Set<String> elements = new HashSet<>();
|
||||||
|
elements.addAll(TagCreatorCodeGenerator.emptyTags());
|
||||||
|
elements.addAll(TagCreatorCodeGenerator.containerTags());
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> specifiedElements(WattsiSource source){
|
||||||
|
Set<String> elements = new HashSet<>();
|
||||||
|
for(ElementDefinition element : source.elementDefinitions()){
|
||||||
|
elements.add(element.name());
|
||||||
|
}
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
// TODO restore this test once a policy has been determined for obsolete elements.
|
||||||
|
public void all_wattsi_elements_are_defined_in_the_code_generator() {
|
||||||
|
Set<String> generated = generatedElements();
|
||||||
|
|
||||||
|
List<String> undefined = specification.elementDefinitions().stream()
|
||||||
|
.filter(element -> !element.isObsolete())
|
||||||
|
.filter(element -> !generated.contains(element.name()))
|
||||||
|
.map(ElementDefinition::name)
|
||||||
|
.collect(toList());
|
||||||
|
|
||||||
|
assertEquals("HTML elements are missing", emptyList(), undefined);
|
||||||
|
// Currently missing (and mostly deprecated):
|
||||||
|
// hgroup
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void only_wattsi_elements_are_defined_in_the_code_generator(){
|
||||||
|
Set<String> specified = specifiedElements(specification);
|
||||||
|
|
||||||
|
List<String> invalid = generatedElements().stream()
|
||||||
|
.filter(element -> !specified.contains(element))
|
||||||
|
.collect(toList());
|
||||||
|
|
||||||
|
assertEquals("HTML elements are invalid", emptyList(), invalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package com.j2html.codegen;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
public class ParserTest {
|
||||||
|
|
||||||
|
private void verifyParsing(String txt, Consumer<Parser.Listener> checks) {
|
||||||
|
Parser.Listener listener = mock(Parser.Listener.class);
|
||||||
|
Parser.parse(txt, listener);
|
||||||
|
checks.accept(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void an_empty_input_has_no_events() {
|
||||||
|
verifyParsing("", listener -> {
|
||||||
|
verifyNoInteractions(listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whitespace_has_no_events() {
|
||||||
|
verifyParsing(" \t\t\t\t", listener -> {
|
||||||
|
verifyNoInteractions(listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void commented_lines_are_signaled() {
|
||||||
|
verifyParsing("#Comment 1.\n# Comment B?", listener -> {
|
||||||
|
InOrder order = inOrder(listener);
|
||||||
|
order.verify(listener).lineCommented(1, "#Comment 1.");
|
||||||
|
order.verify(listener).lineCommented(2, "# Comment B?");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void node_definitions_are_signaled() {
|
||||||
|
verifyParsing("ELEMENT[a]\nEMPTY-ELEMENT[b]\nBOOLEAN[c]\nONOFF[d]\nSTRING[e]", listener -> {
|
||||||
|
InOrder order = inOrder(listener);
|
||||||
|
order.verify(listener).elementDefined(1, "a");
|
||||||
|
order.verify(listener).emptyElementDefined(2, "b");
|
||||||
|
order.verify(listener).booleanDefined(3, "c");
|
||||||
|
order.verify(listener).onOffDefined(4, "d");
|
||||||
|
order.verify(listener).stringDefined(5, "e");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void attribute_definitions_are_signaled() {
|
||||||
|
verifyParsing("ATTRIBUTE[a:b]", listener -> {
|
||||||
|
InOrder order = inOrder(listener);
|
||||||
|
order.verify(listener).attributeDefined(1, "a", "b");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void invalid_lines_are_signaled() {
|
||||||
|
verifyParsing("lol, I dunno!\nIt Broke...", listener -> {
|
||||||
|
InOrder order = inOrder(listener);
|
||||||
|
order.verify(listener).invalidLine(1, "lol, I dunno!");
|
||||||
|
order.verify(listener).invalidLine(2, "It Broke...");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
128553
gradle-plugin-j2html/src/test/resources/2022-01.wattsi
Normal file
128553
gradle-plugin-j2html/src/test/resources/2022-01.wattsi
Normal file
File diff suppressed because it is too large
Load diff
622
gradle-plugin-j2html/src/test/resources/html.model
Normal file
622
gradle-plugin-j2html/src/test/resources/html.model
Normal file
|
@ -0,0 +1,622 @@
|
||||||
|
EMPTY-ELEMENT[area]
|
||||||
|
EMPTY-ELEMENT[base]
|
||||||
|
EMPTY-ELEMENT[br]
|
||||||
|
EMPTY-ELEMENT[col]
|
||||||
|
EMPTY-ELEMENT[embed]
|
||||||
|
EMPTY-ELEMENT[hr]
|
||||||
|
EMPTY-ELEMENT[img]
|
||||||
|
EMPTY-ELEMENT[input]
|
||||||
|
EMPTY-ELEMENT[keygen]
|
||||||
|
EMPTY-ELEMENT[link]
|
||||||
|
EMPTY-ELEMENT[meta]
|
||||||
|
EMPTY-ELEMENT[param]
|
||||||
|
EMPTY-ELEMENT[source]
|
||||||
|
EMPTY-ELEMENT[track]
|
||||||
|
EMPTY-ELEMENT[wbr]
|
||||||
|
|
||||||
|
ELEMENT[a]
|
||||||
|
ELEMENT[abbr]
|
||||||
|
ELEMENT[address]
|
||||||
|
ELEMENT[article]
|
||||||
|
ELEMENT[aside]
|
||||||
|
ELEMENT[audio]
|
||||||
|
ELEMENT[b]
|
||||||
|
ELEMENT[bdi]
|
||||||
|
ELEMENT[bdo]
|
||||||
|
ELEMENT[blockquote]
|
||||||
|
ELEMENT[body]
|
||||||
|
ELEMENT[button]
|
||||||
|
ELEMENT[canvas]
|
||||||
|
ELEMENT[caption]
|
||||||
|
ELEMENT[cite]
|
||||||
|
ELEMENT[code]
|
||||||
|
ELEMENT[colgroup]
|
||||||
|
ELEMENT[data]
|
||||||
|
ELEMENT[datalist]
|
||||||
|
ELEMENT[dd]
|
||||||
|
ELEMENT[del]
|
||||||
|
ELEMENT[details]
|
||||||
|
ELEMENT[dfn]
|
||||||
|
ELEMENT[dialog]
|
||||||
|
ELEMENT[div]
|
||||||
|
ELEMENT[dl]
|
||||||
|
ELEMENT[dt]
|
||||||
|
ELEMENT[em]
|
||||||
|
ELEMENT[fieldset]
|
||||||
|
ELEMENT[figcaption]
|
||||||
|
ELEMENT[figure]
|
||||||
|
ELEMENT[footer]
|
||||||
|
ELEMENT[form]
|
||||||
|
ELEMENT[h1]
|
||||||
|
ELEMENT[h2]
|
||||||
|
ELEMENT[h3]
|
||||||
|
ELEMENT[h4]
|
||||||
|
ELEMENT[h5]
|
||||||
|
ELEMENT[h6]
|
||||||
|
ELEMENT[head]
|
||||||
|
ELEMENT[header]
|
||||||
|
ELEMENT[html]
|
||||||
|
ELEMENT[i]
|
||||||
|
ELEMENT[iframe]
|
||||||
|
ELEMENT[ins]
|
||||||
|
ELEMENT[kbd]
|
||||||
|
ELEMENT[label]
|
||||||
|
ELEMENT[legend]
|
||||||
|
ELEMENT[li]
|
||||||
|
ELEMENT[main]
|
||||||
|
ELEMENT[map]
|
||||||
|
ELEMENT[mark]
|
||||||
|
ELEMENT[menu]
|
||||||
|
ELEMENT[menuitem]
|
||||||
|
ELEMENT[meter]
|
||||||
|
ELEMENT[nav]
|
||||||
|
ELEMENT[noscript]
|
||||||
|
ELEMENT[object]
|
||||||
|
ELEMENT[ol]
|
||||||
|
ELEMENT[optgroup]
|
||||||
|
ELEMENT[option]
|
||||||
|
ELEMENT[output]
|
||||||
|
ELEMENT[p]
|
||||||
|
ELEMENT[picture]
|
||||||
|
ELEMENT[pre]
|
||||||
|
ELEMENT[progress]
|
||||||
|
ELEMENT[q]
|
||||||
|
ELEMENT[rp]
|
||||||
|
ELEMENT[rt]
|
||||||
|
ELEMENT[ruby]
|
||||||
|
ELEMENT[s]
|
||||||
|
ELEMENT[samp]
|
||||||
|
ELEMENT[script]
|
||||||
|
ELEMENT[section]
|
||||||
|
ELEMENT[select]
|
||||||
|
ELEMENT[slot]
|
||||||
|
ELEMENT[small]
|
||||||
|
ELEMENT[span]
|
||||||
|
ELEMENT[strong]
|
||||||
|
ELEMENT[style]
|
||||||
|
ELEMENT[sub]
|
||||||
|
ELEMENT[summary]
|
||||||
|
ELEMENT[sup]
|
||||||
|
ELEMENT[table]
|
||||||
|
ELEMENT[tbody]
|
||||||
|
ELEMENT[td]
|
||||||
|
ELEMENT[template]
|
||||||
|
ELEMENT[textarea]
|
||||||
|
ELEMENT[tfoot]
|
||||||
|
ELEMENT[th]
|
||||||
|
ELEMENT[thead]
|
||||||
|
ELEMENT[time]
|
||||||
|
ELEMENT[title]
|
||||||
|
ELEMENT[tr]
|
||||||
|
ELEMENT[u]
|
||||||
|
ELEMENT[ul]
|
||||||
|
ELEMENT[var]
|
||||||
|
ELEMENT[video]
|
||||||
|
|
||||||
|
STRING[accept]
|
||||||
|
ATTRIBUTE[input:accept]
|
||||||
|
|
||||||
|
STRING[action]
|
||||||
|
ATTRIBUTE[form:action]
|
||||||
|
|
||||||
|
STRING[alt]
|
||||||
|
ATTRIBUTE[area:alt]
|
||||||
|
ATTRIBUTE[img:alt]
|
||||||
|
ATTRIBUTE[input:alt]
|
||||||
|
|
||||||
|
BOOLEAN[async]
|
||||||
|
ATTRIBUTE[script:async]
|
||||||
|
|
||||||
|
ONOFF[autocomplete]
|
||||||
|
ATTRIBUTE[form:autocomplete]
|
||||||
|
ATTRIBUTE[input:autocomplete]
|
||||||
|
|
||||||
|
BOOLEAN[autofocus]
|
||||||
|
ATTRIBUTE[button:autofocus]
|
||||||
|
ATTRIBUTE[input:autofocus]
|
||||||
|
ATTRIBUTE[select:autofocus]
|
||||||
|
ATTRIBUTE[textarea:autofocus]
|
||||||
|
|
||||||
|
BOOLEAN[autoplay]
|
||||||
|
ATTRIBUTE[audio:autoplay]
|
||||||
|
ATTRIBUTE[video:autoplay]
|
||||||
|
|
||||||
|
STRING[charset]
|
||||||
|
ATTRIBUTE[meta:charset]
|
||||||
|
ATTRIBUTE[script:charset]
|
||||||
|
|
||||||
|
BOOLEAN[checked]
|
||||||
|
ATTRIBUTE[input:checked]
|
||||||
|
|
||||||
|
STRING[cite]
|
||||||
|
ATTRIBUTE[blockquote:cite]
|
||||||
|
ATTRIBUTE[del:cite]
|
||||||
|
ATTRIBUTE[ins:cite]
|
||||||
|
ATTRIBUTE[q:cite]
|
||||||
|
|
||||||
|
STRING[cols]
|
||||||
|
ATTRIBUTE[textarea:cols]
|
||||||
|
|
||||||
|
STRING[colspan]
|
||||||
|
ATTRIBUTE[td:colspan]
|
||||||
|
ATTRIBUTE[th:colspan]
|
||||||
|
|
||||||
|
STRING[content]
|
||||||
|
ATTRIBUTE[meta:content]
|
||||||
|
|
||||||
|
BOOLEAN[controls]
|
||||||
|
ATTRIBUTE[audio:controls]
|
||||||
|
ATTRIBUTE[video:controls]
|
||||||
|
|
||||||
|
STRING[coords]
|
||||||
|
ATTRIBUTE[area:coords]
|
||||||
|
|
||||||
|
STRING[data]
|
||||||
|
ATTRIBUTE[object:data]
|
||||||
|
|
||||||
|
STRING[datetime]
|
||||||
|
ATTRIBUTE[del:datetime]
|
||||||
|
ATTRIBUTE[ins:datetime]
|
||||||
|
ATTRIBUTE[time:datetime]
|
||||||
|
|
||||||
|
BOOLEAN[default]
|
||||||
|
ATTRIBUTE[track:default]
|
||||||
|
|
||||||
|
BOOLEAN[defer]
|
||||||
|
ATTRIBUTE[script:defer]
|
||||||
|
|
||||||
|
STRING[dirname]
|
||||||
|
ATTRIBUTE[input:dirname]
|
||||||
|
ATTRIBUTE[textarea:dirname]
|
||||||
|
|
||||||
|
BOOLEAN[disabled]
|
||||||
|
ATTRIBUTE[button:disabled]
|
||||||
|
ATTRIBUTE[fieldset:disabled]
|
||||||
|
ATTRIBUTE[input:disabled]
|
||||||
|
ATTRIBUTE[optgroup:disabled]
|
||||||
|
ATTRIBUTE[option:disabled]
|
||||||
|
ATTRIBUTE[select:disabled]
|
||||||
|
ATTRIBUTE[textarea:disabled]
|
||||||
|
|
||||||
|
BOOLEAN[download]
|
||||||
|
ATTRIBUTE[a:download]
|
||||||
|
ATTRIBUTE[area:download]
|
||||||
|
|
||||||
|
STRING[enctype]
|
||||||
|
ATTRIBUTE[form:enctype]
|
||||||
|
|
||||||
|
STRING[for]
|
||||||
|
ATTRIBUTE[label:for]
|
||||||
|
ATTRIBUTE[output:for]
|
||||||
|
|
||||||
|
STRING[form]
|
||||||
|
ATTRIBUTE[button:form]
|
||||||
|
ATTRIBUTE[fieldset:form]
|
||||||
|
ATTRIBUTE[input:form]
|
||||||
|
ATTRIBUTE[label:form]
|
||||||
|
ATTRIBUTE[meter:form]
|
||||||
|
ATTRIBUTE[object:form]
|
||||||
|
ATTRIBUTE[output:form]
|
||||||
|
ATTRIBUTE[select:form]
|
||||||
|
ATTRIBUTE[textarea:form]
|
||||||
|
|
||||||
|
STRING[formaction]
|
||||||
|
ATTRIBUTE[button:formaction]
|
||||||
|
ATTRIBUTE[input:formaction]
|
||||||
|
|
||||||
|
STRING[headers]
|
||||||
|
ATTRIBUTE[td:headers]
|
||||||
|
ATTRIBUTE[th:headers]
|
||||||
|
|
||||||
|
STRING[height]
|
||||||
|
ATTRIBUTE[canvas:height]
|
||||||
|
ATTRIBUTE[embed:height]
|
||||||
|
ATTRIBUTE[iframe:height]
|
||||||
|
ATTRIBUTE[img:height]
|
||||||
|
ATTRIBUTE[input:height]
|
||||||
|
ATTRIBUTE[object:height]
|
||||||
|
ATTRIBUTE[video:height]
|
||||||
|
|
||||||
|
STRING[high]
|
||||||
|
ATTRIBUTE[meter:high]
|
||||||
|
|
||||||
|
STRING[href]
|
||||||
|
ATTRIBUTE[a:href]
|
||||||
|
ATTRIBUTE[area:href]
|
||||||
|
ATTRIBUTE[base:href]
|
||||||
|
ATTRIBUTE[link:href]
|
||||||
|
|
||||||
|
STRING[hreflang]
|
||||||
|
ATTRIBUTE[a:hreflang]
|
||||||
|
ATTRIBUTE[area:hreflang]
|
||||||
|
ATTRIBUTE[link:hreflang]
|
||||||
|
|
||||||
|
BOOLEAN[ismap]
|
||||||
|
ATTRIBUTE[img:ismap]
|
||||||
|
|
||||||
|
STRING[kind]
|
||||||
|
ATTRIBUTE[track:kind]
|
||||||
|
|
||||||
|
STRING[label]
|
||||||
|
ATTRIBUTE[track:label]
|
||||||
|
ATTRIBUTE[option:label]
|
||||||
|
ATTRIBUTE[optgroup:label]
|
||||||
|
|
||||||
|
STRING[list]
|
||||||
|
ATTRIBUTE[input:list]
|
||||||
|
|
||||||
|
BOOLEAN[loop]
|
||||||
|
ATTRIBUTE[audio:loop]
|
||||||
|
ATTRIBUTE[video:loop]
|
||||||
|
|
||||||
|
STRING[low]
|
||||||
|
ATTRIBUTE[meter:low]
|
||||||
|
|
||||||
|
STRING[max]
|
||||||
|
ATTRIBUTE[input:max]
|
||||||
|
ATTRIBUTE[meter:max]
|
||||||
|
ATTRIBUTE[progress:max]
|
||||||
|
|
||||||
|
STRING[maxlength]
|
||||||
|
ATTRIBUTE[input:maxlength]
|
||||||
|
ATTRIBUTE[textarea:maxlength]
|
||||||
|
|
||||||
|
STRING[media]
|
||||||
|
ATTRIBUTE[a:media]
|
||||||
|
ATTRIBUTE[area:media]
|
||||||
|
ATTRIBUTE[link:media]
|
||||||
|
ATTRIBUTE[source:media]
|
||||||
|
ATTRIBUTE[style:media]
|
||||||
|
|
||||||
|
STRING[method]
|
||||||
|
ATTRIBUTE[form:method]
|
||||||
|
|
||||||
|
STRING[min]
|
||||||
|
ATTRIBUTE[input:min]
|
||||||
|
ATTRIBUTE[meter:min]
|
||||||
|
|
||||||
|
BOOLEAN[multiple]
|
||||||
|
ATTRIBUTE[input:multiple]
|
||||||
|
ATTRIBUTE[select:multiple]
|
||||||
|
|
||||||
|
BOOLEAN[muted]
|
||||||
|
ATTRIBUTE[video:muted]
|
||||||
|
ATTRIBUTE[audio:muted]
|
||||||
|
|
||||||
|
STRING[name]
|
||||||
|
ATTRIBUTE[button:name]
|
||||||
|
ATTRIBUTE[fieldset:name]
|
||||||
|
ATTRIBUTE[form:name]
|
||||||
|
ATTRIBUTE[iframe:name]
|
||||||
|
ATTRIBUTE[input:name]
|
||||||
|
ATTRIBUTE[map:name]
|
||||||
|
ATTRIBUTE[meta:name]
|
||||||
|
ATTRIBUTE[object:name]
|
||||||
|
ATTRIBUTE[output:name]
|
||||||
|
ATTRIBUTE[param:name]
|
||||||
|
ATTRIBUTE[select:name]
|
||||||
|
ATTRIBUTE[slot:name]
|
||||||
|
ATTRIBUTE[textarea:name]
|
||||||
|
|
||||||
|
BOOLEAN[novalidate]
|
||||||
|
ATTRIBUTE[form:novalidate]
|
||||||
|
|
||||||
|
STRING[onabort]
|
||||||
|
ATTRIBUTE[audio:onabort]
|
||||||
|
ATTRIBUTE[embed:onabort]
|
||||||
|
ATTRIBUTE[img:onabort]
|
||||||
|
ATTRIBUTE[object:onabort]
|
||||||
|
ATTRIBUTE[video:onabort]
|
||||||
|
|
||||||
|
STRING[onafterprint]
|
||||||
|
ATTRIBUTE[body:onafterprint]
|
||||||
|
|
||||||
|
STRING[onbeforeprint]
|
||||||
|
ATTRIBUTE[body:onbeforeprint]
|
||||||
|
|
||||||
|
STRING[onbeforeunload]
|
||||||
|
ATTRIBUTE[body:onbeforeunload]
|
||||||
|
|
||||||
|
STRING[oncanplay]
|
||||||
|
ATTRIBUTE[audio:oncanplay]
|
||||||
|
ATTRIBUTE[embed:oncanplay]
|
||||||
|
ATTRIBUTE[object:oncanplay]
|
||||||
|
ATTRIBUTE[video:oncanplay]
|
||||||
|
|
||||||
|
STRING[oncanplaythrough]
|
||||||
|
ATTRIBUTE[audio:oncanplaythrough]
|
||||||
|
ATTRIBUTE[video:oncanplaythrough]
|
||||||
|
|
||||||
|
STRING[oncuechange]
|
||||||
|
ATTRIBUTE[track:oncuechange]
|
||||||
|
|
||||||
|
STRING[ondurationchange]
|
||||||
|
ATTRIBUTE[audio:ondurationchange]
|
||||||
|
ATTRIBUTE[video:ondurationchange]
|
||||||
|
|
||||||
|
STRING[onemptied]
|
||||||
|
ATTRIBUTE[audio:onemptied]
|
||||||
|
ATTRIBUTE[video:onemptied]
|
||||||
|
|
||||||
|
STRING[onended]
|
||||||
|
ATTRIBUTE[audio:onended]
|
||||||
|
ATTRIBUTE[video:onended]
|
||||||
|
|
||||||
|
STRING[onerror]
|
||||||
|
ATTRIBUTE[audio:onerror]
|
||||||
|
ATTRIBUTE[body:onerror]
|
||||||
|
ATTRIBUTE[embed:onerror]
|
||||||
|
ATTRIBUTE[img:onerror]
|
||||||
|
ATTRIBUTE[object:onerror]
|
||||||
|
ATTRIBUTE[script:onerror]
|
||||||
|
ATTRIBUTE[style:onerror]
|
||||||
|
ATTRIBUTE[video:onerror]
|
||||||
|
|
||||||
|
STRING[onhashchange]
|
||||||
|
ATTRIBUTE[body:onhashchange]
|
||||||
|
|
||||||
|
STRING[onload]
|
||||||
|
ATTRIBUTE[body:onload]
|
||||||
|
ATTRIBUTE[iframe:onload]
|
||||||
|
ATTRIBUTE[img:onload]
|
||||||
|
ATTRIBUTE[input:onload]
|
||||||
|
ATTRIBUTE[link:onload]
|
||||||
|
ATTRIBUTE[script:onload]
|
||||||
|
ATTRIBUTE[style:onload]
|
||||||
|
|
||||||
|
STRING[onloadeddata]
|
||||||
|
ATTRIBUTE[audio:onloadeddata]
|
||||||
|
ATTRIBUTE[video:onloadeddata]
|
||||||
|
|
||||||
|
STRING[onloadedmetadata]
|
||||||
|
ATTRIBUTE[audio:onloadedmetadata]
|
||||||
|
ATTRIBUTE[video:onloadedmetadata]
|
||||||
|
|
||||||
|
STRING[onloadstart]
|
||||||
|
ATTRIBUTE[audio:onloadstart]
|
||||||
|
ATTRIBUTE[video:onloadstart]
|
||||||
|
|
||||||
|
STRING[onoffline]
|
||||||
|
ATTRIBUTE[body:onoffline]
|
||||||
|
|
||||||
|
STRING[ononline]
|
||||||
|
ATTRIBUTE[body:ononline]
|
||||||
|
|
||||||
|
STRING[onpagehide]
|
||||||
|
ATTRIBUTE[body:onpagehide]
|
||||||
|
|
||||||
|
STRING[onpageshow]
|
||||||
|
ATTRIBUTE[body:onpageshow]
|
||||||
|
|
||||||
|
STRING[onpause]
|
||||||
|
ATTRIBUTE[audio:onpause]
|
||||||
|
ATTRIBUTE[video:onpause]
|
||||||
|
|
||||||
|
STRING[onplay]
|
||||||
|
ATTRIBUTE[audio:onplay]
|
||||||
|
ATTRIBUTE[video:onplay]
|
||||||
|
|
||||||
|
STRING[onplaying]
|
||||||
|
ATTRIBUTE[audio:onplaying]
|
||||||
|
ATTRIBUTE[video:onplaying]
|
||||||
|
|
||||||
|
STRING[onpopstate]
|
||||||
|
ATTRIBUTE[body:onpopstate]
|
||||||
|
|
||||||
|
STRING[onprogress]
|
||||||
|
ATTRIBUTE[audio:onprogress]
|
||||||
|
ATTRIBUTE[video:onprogress]
|
||||||
|
|
||||||
|
STRING[onratechange]
|
||||||
|
ATTRIBUTE[audio:onratechange]
|
||||||
|
ATTRIBUTE[video:onratechange]
|
||||||
|
|
||||||
|
STRING[onreset]
|
||||||
|
ATTRIBUTE[form:onreset]
|
||||||
|
|
||||||
|
STRING[onresize]
|
||||||
|
ATTRIBUTE[body:onresize]
|
||||||
|
|
||||||
|
STRING[onsearch]
|
||||||
|
ATTRIBUTE[input:onsearch]
|
||||||
|
|
||||||
|
STRING[onseeked]
|
||||||
|
ATTRIBUTE[audio:onseeked]
|
||||||
|
ATTRIBUTE[video:onseeked]
|
||||||
|
|
||||||
|
STRING[onseeking]
|
||||||
|
ATTRIBUTE[audio:onseeking]
|
||||||
|
ATTRIBUTE[video:onseeking]
|
||||||
|
|
||||||
|
STRING[onstalled]
|
||||||
|
ATTRIBUTE[audio:onstalled]
|
||||||
|
ATTRIBUTE[video:onstalled]
|
||||||
|
|
||||||
|
STRING[onstorage]
|
||||||
|
ATTRIBUTE[body:onstorage]
|
||||||
|
|
||||||
|
STRING[onsubmit]
|
||||||
|
ATTRIBUTE[form:onsubmit]
|
||||||
|
|
||||||
|
STRING[onsuspend]
|
||||||
|
ATTRIBUTE[audio:onsuspend]
|
||||||
|
ATTRIBUTE[video:onsuspend]
|
||||||
|
|
||||||
|
STRING[ontimeupdate]
|
||||||
|
ATTRIBUTE[audio:ontimeupdate]
|
||||||
|
ATTRIBUTE[video:ontimeupdate]
|
||||||
|
|
||||||
|
STRING[ontoggle]
|
||||||
|
ATTRIBUTE[details:ontoggle]
|
||||||
|
|
||||||
|
STRING[onunload]
|
||||||
|
ATTRIBUTE[body:onunload]
|
||||||
|
|
||||||
|
STRING[onvolumechanged]
|
||||||
|
ATTRIBUTE[audio:onvolumechanged]
|
||||||
|
ATTRIBUTE[video:onvolumechanged]
|
||||||
|
|
||||||
|
STRING[onwaiting]
|
||||||
|
ATTRIBUTE[audio:onwaiting]
|
||||||
|
ATTRIBUTE[video:onwaiting]
|
||||||
|
|
||||||
|
BOOLEAN[open]
|
||||||
|
ATTRIBUTE[details:open]
|
||||||
|
|
||||||
|
STRING[optimum]
|
||||||
|
ATTRIBUTE[meter:optimum]
|
||||||
|
|
||||||
|
STRING[pattern]
|
||||||
|
ATTRIBUTE[input:pattern]
|
||||||
|
|
||||||
|
STRING[placeholder]
|
||||||
|
ATTRIBUTE[input:placeholder]
|
||||||
|
ATTRIBUTE[textarea:placeholder]
|
||||||
|
|
||||||
|
STRING[poster]
|
||||||
|
ATTRIBUTE[video:poster]
|
||||||
|
|
||||||
|
STRING[preload]
|
||||||
|
ATTRIBUTE[audio:preload]
|
||||||
|
ATTRIBUTE[video:preload]
|
||||||
|
|
||||||
|
BOOLEAN[readonly]
|
||||||
|
ATTRIBUTE[input:readonly]
|
||||||
|
ATTRIBUTE[textarea:readonly]
|
||||||
|
|
||||||
|
STRING[rel]
|
||||||
|
ATTRIBUTE[a:rel]
|
||||||
|
ATTRIBUTE[area:rel]
|
||||||
|
ATTRIBUTE[form:rel]
|
||||||
|
ATTRIBUTE[link:rel]
|
||||||
|
|
||||||
|
BOOLEAN[required]
|
||||||
|
ATTRIBUTE[input:required]
|
||||||
|
ATTRIBUTE[select:required]
|
||||||
|
ATTRIBUTE[textarea:required]
|
||||||
|
|
||||||
|
BOOLEAN[reversed]
|
||||||
|
ATTRIBUTE[ol:reversed]
|
||||||
|
|
||||||
|
STRING[rows]
|
||||||
|
ATTRIBUTE[textarea:rows]
|
||||||
|
|
||||||
|
STRING[rowspan]
|
||||||
|
ATTRIBUTE[td:rowspan]
|
||||||
|
ATTRIBUTE[th:rowspan]
|
||||||
|
|
||||||
|
BOOLEAN[sandbox]
|
||||||
|
ATTRIBUTE[iframe:sandbox]
|
||||||
|
|
||||||
|
STRING[scope]
|
||||||
|
ATTRIBUTE[th:scope]
|
||||||
|
|
||||||
|
BOOLEAN[selected]
|
||||||
|
ATTRIBUTE[option:selected]
|
||||||
|
|
||||||
|
STRING[shape]
|
||||||
|
ATTRIBUTE[area:shape]
|
||||||
|
|
||||||
|
STRING[size]
|
||||||
|
ATTRIBUTE[input:size]
|
||||||
|
ATTRIBUTE[select:size]
|
||||||
|
|
||||||
|
STRING[sizes]
|
||||||
|
ATTRIBUTE[img:sizes]
|
||||||
|
ATTRIBUTE[link:sizes]
|
||||||
|
ATTRIBUTE[source:sizes]
|
||||||
|
|
||||||
|
STRING[span]
|
||||||
|
ATTRIBUTE[col:span]
|
||||||
|
ATTRIBUTE[colgroup:span]
|
||||||
|
|
||||||
|
STRING[src]
|
||||||
|
ATTRIBUTE[audio:src]
|
||||||
|
ATTRIBUTE[embed:src]
|
||||||
|
ATTRIBUTE[iframe:src]
|
||||||
|
ATTRIBUTE[img:src]
|
||||||
|
ATTRIBUTE[input:src]
|
||||||
|
ATTRIBUTE[script:src]
|
||||||
|
ATTRIBUTE[source:src]
|
||||||
|
ATTRIBUTE[track:src]
|
||||||
|
ATTRIBUTE[video:src]
|
||||||
|
|
||||||
|
STRING[srcdoc]
|
||||||
|
ATTRIBUTE[iframe:srcdoc]
|
||||||
|
|
||||||
|
STRING[srclang]
|
||||||
|
ATTRIBUTE[track:srclang]
|
||||||
|
|
||||||
|
STRING[srcset]
|
||||||
|
ATTRIBUTE[img:srcset]
|
||||||
|
ATTRIBUTE[source:srcset]
|
||||||
|
|
||||||
|
STRING[start]
|
||||||
|
ATTRIBUTE[ol:start]
|
||||||
|
|
||||||
|
STRING[step]
|
||||||
|
ATTRIBUTE[input:step]
|
||||||
|
|
||||||
|
STRING[target]
|
||||||
|
ATTRIBUTE[a:target]
|
||||||
|
ATTRIBUTE[area:target]
|
||||||
|
ATTRIBUTE[base:target]
|
||||||
|
ATTRIBUTE[form:target]
|
||||||
|
|
||||||
|
STRING[type]
|
||||||
|
ATTRIBUTE[a:type]
|
||||||
|
ATTRIBUTE[button:type]
|
||||||
|
ATTRIBUTE[embed:type]
|
||||||
|
ATTRIBUTE[input:type]
|
||||||
|
ATTRIBUTE[link:type]
|
||||||
|
ATTRIBUTE[menu:type]
|
||||||
|
ATTRIBUTE[object:type]
|
||||||
|
ATTRIBUTE[script:type]
|
||||||
|
ATTRIBUTE[source:type]
|
||||||
|
ATTRIBUTE[style:type]
|
||||||
|
|
||||||
|
STRING[usemap]
|
||||||
|
ATTRIBUTE[img:usemap]
|
||||||
|
ATTRIBUTE[object:usemap]
|
||||||
|
|
||||||
|
STRING[value]
|
||||||
|
ATTRIBUTE[button:value]
|
||||||
|
ATTRIBUTE[data:value]
|
||||||
|
ATTRIBUTE[input:value]
|
||||||
|
ATTRIBUTE[li:value]
|
||||||
|
ATTRIBUTE[option:value]
|
||||||
|
ATTRIBUTE[meter:value]
|
||||||
|
ATTRIBUTE[progress:value]
|
||||||
|
ATTRIBUTE[param:value]
|
||||||
|
|
||||||
|
STRING[width]
|
||||||
|
ATTRIBUTE[canvas:width]
|
||||||
|
ATTRIBUTE[embed:width]
|
||||||
|
ATTRIBUTE[iframe:width]
|
||||||
|
ATTRIBUTE[img:width]
|
||||||
|
ATTRIBUTE[input:width]
|
||||||
|
ATTRIBUTE[object:width]
|
||||||
|
ATTRIBUTE[video:width]
|
||||||
|
|
||||||
|
STRING[wrap]
|
||||||
|
ATTRIBUTE[textarea:wrap]
|
10
gradle-plugin-jdeps/NOTICE.txt
Normal file
10
gradle-plugin-jdeps/NOTICE.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
This work is based on
|
||||||
|
|
||||||
|
https://github.com/kordamp/jdeps-gradle-plugin
|
||||||
|
|
||||||
|
by Andres Almiray
|
||||||
|
|
||||||
|
as of Feb 17, 2024
|
||||||
|
|
||||||
|
License: Apache 2.0
|
32
gradle-plugin-jdeps/build.gradle
Executable file
32
gradle-plugin-jdeps/build.gradle
Executable file
|
@ -0,0 +1,32 @@
|
||||||
|
plugins {
|
||||||
|
id 'java-gradle-plugin'
|
||||||
|
alias(libs.plugins.publish)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'java-gradle-plugin'
|
||||||
|
apply plugin: 'com.gradle.plugin-publish'
|
||||||
|
|
||||||
|
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
||||||
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api gradleApi()
|
||||||
|
testImplementation gradleTestKit()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.hasProperty('gradle.publish.key')) {
|
||||||
|
gradlePlugin {
|
||||||
|
website = 'https://xbib.org/joerg/gradle-plugins/src/branch/main/gradle-plugin-jdeps'
|
||||||
|
vcsUrl = 'https://xbib.org/joerg/gradle-plugins'
|
||||||
|
plugins {
|
||||||
|
jdepsPlugin {
|
||||||
|
id = 'org.xbib.gradle.plugin.jdeps'
|
||||||
|
implementationClass = 'org.xbib.gradle.plugin.jdeps.JDepsPlugin'
|
||||||
|
version = project.version
|
||||||
|
description = 'Gradle JDeps plugin'
|
||||||
|
displayName = 'Gradle JDeps plugin'
|
||||||
|
tags.set(['jdeps'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps
|
||||||
|
|
||||||
|
import org.gradle.api.Action
|
||||||
|
import org.gradle.api.Plugin
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.plugins.BasePlugin
|
||||||
|
import org.gradle.api.plugins.JavaPlugin
|
||||||
|
import org.gradle.api.plugins.JavaPluginConvention
|
||||||
|
import org.gradle.api.tasks.TaskProvider
|
||||||
|
import org.gradle.api.tasks.compile.JavaCompile
|
||||||
|
|
||||||
|
class JDepsPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
void apply(Project project) {
|
||||||
|
project.plugins.apply(JavaPlugin)
|
||||||
|
TaskProvider<JDepsReportTask> report = project.tasks.register('jdepsReport', JDepsReportTask,
|
||||||
|
new Action<JDepsReportTask>() {
|
||||||
|
@Override
|
||||||
|
void execute(JDepsReportTask t) {
|
||||||
|
t.dependsOn(project.tasks.named('classes'))
|
||||||
|
t.group = BasePlugin.BUILD_GROUP
|
||||||
|
t.description = 'Generate a jdeps report on project classes and dependencies'
|
||||||
|
t.compileJava = project.tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile)
|
||||||
|
t.javaPluginConvention.set(project.convention.getPlugin(JavaPluginConvention))
|
||||||
|
t.projectName.set(project.name)
|
||||||
|
t.reportDir.convention(project.layout.buildDirectory.dir('reports/jdeps'))
|
||||||
|
t.projectConfigurations = project.configurations
|
||||||
|
}
|
||||||
|
})
|
||||||
|
project.tasks.named('check').configure(new Action<Task>() {
|
||||||
|
@Override
|
||||||
|
void execute(Task t) {
|
||||||
|
t.dependsOn(report)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,674 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.JavaVersion
|
||||||
|
import org.gradle.api.artifacts.Configuration
|
||||||
|
import org.gradle.api.artifacts.ConfigurationContainer
|
||||||
|
import org.gradle.api.file.Directory
|
||||||
|
import org.gradle.api.file.DirectoryProperty
|
||||||
|
import org.gradle.api.model.ObjectFactory
|
||||||
|
import org.gradle.api.plugins.JavaPluginConvention
|
||||||
|
import org.gradle.api.provider.ListProperty
|
||||||
|
import org.gradle.api.provider.MapProperty
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.tasks.Input
|
||||||
|
import org.gradle.api.tasks.InputDirectory
|
||||||
|
import org.gradle.api.tasks.Internal
|
||||||
|
import org.gradle.api.tasks.Optional
|
||||||
|
import org.gradle.api.tasks.OutputDirectory
|
||||||
|
import org.gradle.api.tasks.SourceSet
|
||||||
|
import org.gradle.api.tasks.TaskAction
|
||||||
|
import org.gradle.api.tasks.TaskProvider
|
||||||
|
import org.gradle.api.tasks.compile.JavaCompile
|
||||||
|
import org.gradle.api.tasks.options.Option
|
||||||
|
import org.gradle.process.ExecOperations
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.BooleanState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.DirectoryState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.ListState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.MapState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.SimpleBooleanState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.SimpleDirectoryState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.SimpleListState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.SimpleMapState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.SimpleStringState
|
||||||
|
import org.xbib.gradle.plugin.jdeps.util.StringState
|
||||||
|
|
||||||
|
import javax.inject.Inject
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
class JDepsReportTask extends DefaultTask {
|
||||||
|
|
||||||
|
private static final Pattern WARNING = Pattern.compile("^(?:Warning:.*)|(?:.+->\\s([a-zA-Z\\.]+)\\s+JDK internal API.*)")
|
||||||
|
|
||||||
|
private static final Pattern ERROR = Pattern.compile("^(?:Error:.*)|Exception in thread.*")
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected ExecOperations execOperations;
|
||||||
|
|
||||||
|
private final BooleanState listDeps
|
||||||
|
|
||||||
|
private final BooleanState listReducedDeps
|
||||||
|
|
||||||
|
private final BooleanState printModuleDeps
|
||||||
|
|
||||||
|
private final BooleanState verbose
|
||||||
|
|
||||||
|
private final BooleanState modular
|
||||||
|
|
||||||
|
private final BooleanState summary
|
||||||
|
|
||||||
|
private final BooleanState profile
|
||||||
|
|
||||||
|
private final BooleanState recursive
|
||||||
|
|
||||||
|
private final BooleanState jdkinternals
|
||||||
|
|
||||||
|
private final BooleanState consoleOutput
|
||||||
|
|
||||||
|
private final BooleanState apionly
|
||||||
|
|
||||||
|
private final BooleanState failOnWarning
|
||||||
|
|
||||||
|
private final BooleanState missingDeps
|
||||||
|
|
||||||
|
private final BooleanState ignoreMissingDeps
|
||||||
|
|
||||||
|
private final ListState pkgs
|
||||||
|
|
||||||
|
private final ListState requires
|
||||||
|
|
||||||
|
private final StringState include
|
||||||
|
|
||||||
|
private final StringState regex
|
||||||
|
|
||||||
|
private final StringState filter
|
||||||
|
|
||||||
|
private final ListState configurations
|
||||||
|
|
||||||
|
private final ListState classpaths
|
||||||
|
|
||||||
|
private final ListState sourceSets
|
||||||
|
|
||||||
|
private final StringState multiRelease
|
||||||
|
|
||||||
|
private final MapState multiReleaseJars
|
||||||
|
|
||||||
|
private final DirectoryState dotOutput
|
||||||
|
|
||||||
|
@OutputDirectory
|
||||||
|
final DirectoryProperty reportDir
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
TaskProvider<JavaCompile> compileJava
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
final Property<JavaPluginConvention> javaPluginConvention
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
final Property<String> projectName
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
ConfigurationContainer projectConfigurations
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
JDepsReportTask(ObjectFactory objects) {
|
||||||
|
extensions.create('moduleOptions', ModuleOptions)
|
||||||
|
reportDir = objects.directoryProperty()
|
||||||
|
projectName = objects.property(String)
|
||||||
|
javaPluginConvention = objects.property(JavaPluginConvention)
|
||||||
|
listDeps = SimpleBooleanState.of(this, 'jdeps.list.deps', false)
|
||||||
|
listReducedDeps = SimpleBooleanState.of(this, 'jdeps.list.reduced.deps', false)
|
||||||
|
printModuleDeps = SimpleBooleanState.of(this, 'jdeps.print.module.deps', false)
|
||||||
|
verbose = SimpleBooleanState.of(this, 'jdeps.verbose', false)
|
||||||
|
modular = SimpleBooleanState.of(this, 'jdeps.modular', false)
|
||||||
|
summary = SimpleBooleanState.of(this, 'jdeps.summary', false)
|
||||||
|
profile = SimpleBooleanState.of(this, 'jdeps.profile', false)
|
||||||
|
recursive = SimpleBooleanState.of(this, 'jdeps.recursive', false)
|
||||||
|
jdkinternals = SimpleBooleanState.of(this, 'jdeps.jdkinternals', false)
|
||||||
|
consoleOutput = SimpleBooleanState.of(this, 'jdeps.console.output', true)
|
||||||
|
apionly = SimpleBooleanState.of(this, 'jdeps.apionly', false)
|
||||||
|
failOnWarning = SimpleBooleanState.of(this, 'jdeps.fail.on.warning', false)
|
||||||
|
missingDeps = SimpleBooleanState.of(this, 'jdeps.missing.deps', false)
|
||||||
|
ignoreMissingDeps = SimpleBooleanState.of(this, 'jdeps.ignore.missing.deps', false)
|
||||||
|
include = SimpleStringState.of(this, 'jdeps.include', "")
|
||||||
|
regex = SimpleStringState.of(this, 'jdeps.regex', "")
|
||||||
|
filter = SimpleStringState.of(this, 'jdeps.filter', "")
|
||||||
|
pkgs = SimpleListState.of(this, 'jdeps.package', [])
|
||||||
|
requires = SimpleListState.of(this, 'jdeps.require', [])
|
||||||
|
configurations = SimpleListState.of(this, 'jdeps.configurations', [])
|
||||||
|
classpaths = SimpleListState.of(this, 'jdeps.classpaths', [])
|
||||||
|
sourceSets = SimpleListState.of(this, 'jdeps.sourcesets', ['main'])
|
||||||
|
multiRelease = SimpleStringState.of(this, 'jdeps.multi.release', 'base')
|
||||||
|
multiReleaseJars = SimpleMapState.of(this, 'jdeps.multi.release.jars', [:])
|
||||||
|
dotOutput = SimpleDirectoryState.of(this, 'jdeps.dot.output', (Directory) null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Option(option = 'list-deps', description = 'Lists the module dependences')
|
||||||
|
void setListDeps(boolean value) { listDeps.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'list-reduced-deps', description = 'Lists the module dependences')
|
||||||
|
void setListReducedDeps(boolean value) { listReducedDeps.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'print-module-deps', description = 'Comma-separated list of module dependences')
|
||||||
|
void setPrintModuleDeps(boolean value) { printModuleDeps.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'verbose', description = 'Print all class level dependences')
|
||||||
|
void setVerbose(boolean value) { verbose.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'modular', description = 'Uses the module path instead of the classpath')
|
||||||
|
void setModular(boolean value) { modular.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'summary', description = 'Print dependency summary only')
|
||||||
|
void setSummary(boolean value) { summary.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'profile', description = 'Show profile containing a package')
|
||||||
|
void setProfile(boolean value) { profile.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'recursive', description = 'Recursively traverse all run-time dependences')
|
||||||
|
void setRecursive(boolean value) { recursive.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'jdkinternals', description = 'Finds class-level dependences on JDK internal APIs')
|
||||||
|
void setJdkinternals(boolean value) { jdkinternals.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'console-output', description = 'Print out report to console')
|
||||||
|
void setConsoleOutput(boolean value) { consoleOutput.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'apionly', description = 'Restrict analysis to APIs')
|
||||||
|
void setApionly(boolean value) { apionly.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'fail-on-warning', description = 'Fails the build if jdeps finds any warnings')
|
||||||
|
void setFailOnWarning(boolean value) { failOnWarning.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'missing-deps', description = 'Finds missing dependences')
|
||||||
|
void setMissingDeps(boolean value) { missingDeps.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'ignore-missing-deps', description = 'Ignore missing dependences')
|
||||||
|
void setIgnoreMissingDeps(boolean value) { ignoreMissingDeps.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'include', description = 'Restrict analysis to classes matching pattern')
|
||||||
|
void setInclude(String value) { include.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'regex', description = 'Finds dependences matching the given pattern')
|
||||||
|
void setRegex(String value) { regex.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'filter', description = 'Filter dependences matching the given pattern')
|
||||||
|
void setFilter(String value) { filter.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'package', description = 'Finds dependences matching the given package name. REPEATABLE')
|
||||||
|
void setPackage(String value) { pkgs.property.add(value) }
|
||||||
|
|
||||||
|
@Option(option = 'require', description = 'Finds dependences matching the given module name. REPEATABLE')
|
||||||
|
void setRequire(String value) { requires.property.add(value) }
|
||||||
|
|
||||||
|
@Option(option = 'configurations', description = 'Configurations to be analyzed')
|
||||||
|
void setConfigurations(String value) { configurations.property.set(value.split(',').toList()) }
|
||||||
|
|
||||||
|
@Option(option = 'classpaths', description = 'Classpaths to be analyzed')
|
||||||
|
void setClasspaths(String value) { classpaths.property.set(value.split(',').toList()) }
|
||||||
|
|
||||||
|
@Option(option = 'sourcesets', description = 'SourceSets to be analyzed')
|
||||||
|
void setSourceSets(String value) { sourceSets.property.set(value.split(',').toList()) }
|
||||||
|
|
||||||
|
@Option(option = 'multi-release', description = 'Set the multi-release level')
|
||||||
|
void setMultiRelease(String value) { multiRelease.property.set(value) }
|
||||||
|
|
||||||
|
@Option(option = 'dot-output', description = 'Destination directory for DOT file output')
|
||||||
|
void setDotOutput(String value) { dotOutput.property.set(new File(value)) }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getListDeps() { listDeps.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedListDeps() { listDeps.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getListReducedDeps() { listReducedDeps.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedListReducedDeps() { listReducedDeps.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getPrintModuleDeps() { printModuleDeps.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedPrintModuleDeps() { printModuleDeps.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getVerbose() { verbose.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedVerbose() { verbose.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getModular() { modular.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedModular() { modular.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getSummary() { summary.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedSummary() { summary.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getProfile() { profile.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedProfile() { profile.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getRecursive() { recursive.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedRecursive() { recursive.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getJdkinternals() { jdkinternals.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedJdkinternals() { jdkinternals.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getConsoleOutput() { consoleOutput.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedConsoleOutput() { consoleOutput.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getApionly() { apionly.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedApionly() { apionly.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getFailOnWarning() { failOnWarning.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedFailOnWarning() { failOnWarning.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getMissingDeps() { missingDeps.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedMissingDeps() { missingDeps.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<Boolean> getIgnoreMissingDeps() { ignoreMissingDeps.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<Boolean> getResolvedIgnoreMissingDeps() { ignoreMissingDeps.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<String> getInclude() { include.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<String> getResolvedInclude() { include.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<String> getRegex() { regex.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<String> getResolvedRegex() { regex.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<String> getFilter() { filter.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<String> getResolvedFilter() { filter.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
ListProperty<String> getPackages() { pkgs.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<List<String>> getResolvedPackages() { pkgs.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
ListProperty<String> getRequires() { requires.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<List<String>> getResolvedRequires() { requires.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
ListProperty<String> getConfigurations() { configurations.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<List<String>> getResolvedConfigurations() { configurations.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
ListProperty<String> getClasspaths() { classpaths.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<List<String>> getResolvedClasspaths() { classpaths.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
ListProperty<String> getSourceSets() { sourceSets.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<List<String>> getResolvedSourceSets() { sourceSets.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
Property<String> getMultiRelease() { multiRelease.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
Provider<String> getResolvedMultiRelease() { multiRelease.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
MapProperty<String, String> getMultiReleaseJars() { multiReleaseJars.property }
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
Provider<Map<String, String>> getResolvedMultiReleaseJars() { multiReleaseJars.provider }
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
DirectoryProperty getDotOutput() { dotOutput.property }
|
||||||
|
|
||||||
|
@InputDirectory
|
||||||
|
@Optional
|
||||||
|
Provider<Directory> getResolvedDotOutput() { dotOutput.provider }
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void evaluate() {
|
||||||
|
ModuleOptions moduleOptions = extensions.getByType(ModuleOptions)
|
||||||
|
String classpath = compileJava.get().classpath.asPath
|
||||||
|
List<String> compilerArgs = compileJava.get().options.compilerArgs
|
||||||
|
List<String> commandOutput = []
|
||||||
|
int explicitCommand = 0
|
||||||
|
if (resolvedListDeps.get()) {
|
||||||
|
explicitCommand++
|
||||||
|
}
|
||||||
|
if (resolvedListReducedDeps.get()) {
|
||||||
|
explicitCommand++
|
||||||
|
}
|
||||||
|
if (resolvedPrintModuleDeps.get()) {
|
||||||
|
explicitCommand++
|
||||||
|
}
|
||||||
|
if (explicitCommand > 1) {
|
||||||
|
throw new IllegalArgumentException("--list-deps, --list-reduced-deps, --print-module-deps are mutually exclusive")
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<String> baseCmd = ['jdeps']
|
||||||
|
if (resolvedSummary.get()) {
|
||||||
|
if (resolvedMissingDeps.get()) {
|
||||||
|
throw new IllegalArgumentException("-s, --missing-deps are mutually exclusive")
|
||||||
|
}
|
||||||
|
baseCmd << '-s'
|
||||||
|
}
|
||||||
|
if (resolvedVerbose.get()) {
|
||||||
|
baseCmd << '-v'
|
||||||
|
}
|
||||||
|
if (resolvedProfile.get()) {
|
||||||
|
baseCmd << '-P'
|
||||||
|
}
|
||||||
|
if (resolvedRecursive.get()) {
|
||||||
|
baseCmd << '-R'
|
||||||
|
}
|
||||||
|
if (resolvedJdkinternals.get()) {
|
||||||
|
baseCmd << '-jdkinternals'
|
||||||
|
}
|
||||||
|
if (resolvedApionly.get()) {
|
||||||
|
baseCmd << '-apionly'
|
||||||
|
}
|
||||||
|
if (getResolvedDotOutput().present) {
|
||||||
|
baseCmd << '-dotoutput'
|
||||||
|
baseCmd << resolvedDotOutput.get().asFile.absolutePath
|
||||||
|
resolvedDotOutput.get().asFile.mkdirs()
|
||||||
|
}
|
||||||
|
if (resolvedIgnoreMissingDeps.get()) {
|
||||||
|
baseCmd << '--ignore-missing-deps'
|
||||||
|
}
|
||||||
|
if (JavaVersion.current().java9Compatible) {
|
||||||
|
if (resolvedMultiRelease.present) {
|
||||||
|
baseCmd << '--multi-release'
|
||||||
|
baseCmd << resolvedMultiRelease.get()
|
||||||
|
}
|
||||||
|
if (resolvedModular.get()) {
|
||||||
|
int modulePathIndex = compilerArgs.indexOf('--module-path')
|
||||||
|
if (modulePathIndex > -1) {
|
||||||
|
baseCmd << '--module-path'
|
||||||
|
baseCmd << compilerArgs[modulePathIndex + 1]
|
||||||
|
} else {
|
||||||
|
baseCmd << '--module-path'
|
||||||
|
baseCmd << classpath
|
||||||
|
}
|
||||||
|
} else if (classpath) {
|
||||||
|
baseCmd << '--class-path'
|
||||||
|
baseCmd << classpath
|
||||||
|
} else {
|
||||||
|
int modulePathIndex = compilerArgs.indexOf('--module-path')
|
||||||
|
if (modulePathIndex > -1) {
|
||||||
|
baseCmd << '--module-path'
|
||||||
|
baseCmd << compilerArgs[modulePathIndex + 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!moduleOptions.addModules.empty) {
|
||||||
|
baseCmd << '--add-modules'
|
||||||
|
baseCmd << moduleOptions.addModules.join(',')
|
||||||
|
} else {
|
||||||
|
int addModulesIndex = compilerArgs.indexOf('--add-modules')
|
||||||
|
if (addModulesIndex > -1) {
|
||||||
|
baseCmd << '--add-modules'
|
||||||
|
baseCmd << compilerArgs[addModulesIndex + 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<String> requires = resolvedRequires.get()
|
||||||
|
List<String> packages = resolvedPackages.get()
|
||||||
|
String regex = resolvedRegex.orNull
|
||||||
|
int exclusive = 0
|
||||||
|
if (!requires.isEmpty()) {
|
||||||
|
exclusive++
|
||||||
|
}
|
||||||
|
if (!packages.isEmpty()) {
|
||||||
|
exclusive++
|
||||||
|
}
|
||||||
|
if (regex) {
|
||||||
|
exclusive++
|
||||||
|
}
|
||||||
|
if (exclusive > 1) {
|
||||||
|
throw new IllegalArgumentException("--package, --regex, --require are mutually exclusive")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolvedMissingDeps.get()) {
|
||||||
|
exclusive = 1
|
||||||
|
if (!packages.isEmpty()) {
|
||||||
|
exclusive++
|
||||||
|
}
|
||||||
|
if (regex) {
|
||||||
|
exclusive++
|
||||||
|
}
|
||||||
|
if (exclusive > 1) {
|
||||||
|
throw new IllegalArgumentException("--package, --regex, --missing-deps are mutually exclusive")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requires.each { s ->
|
||||||
|
baseCmd << '--require'
|
||||||
|
baseCmd << s
|
||||||
|
}
|
||||||
|
packages.each { s ->
|
||||||
|
baseCmd << '--package'
|
||||||
|
baseCmd << s
|
||||||
|
}
|
||||||
|
if (regex) {
|
||||||
|
baseCmd << '--regex'
|
||||||
|
baseCmd << regex
|
||||||
|
}
|
||||||
|
if (resolvedFilter.get()) {
|
||||||
|
String filter = resolvedFilter.get()
|
||||||
|
if (filter in [':package', ':archive', ':module', ':none']) {
|
||||||
|
baseCmd << '-filter' + filter
|
||||||
|
} else {
|
||||||
|
baseCmd << '-filter'
|
||||||
|
baseCmd << filter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// compileJava.get().classpath = project.files()
|
||||||
|
//logger.info("jdeps version is ${executeCommand(['jdeps', '-version'])}")
|
||||||
|
//Map<String, String> outputs = [:]
|
||||||
|
resolvedSourceSets.get().each { sc ->
|
||||||
|
SourceSet sourceSet = javaPluginConvention.get().sourceSets.findByName(sc)
|
||||||
|
logger.info("Running jdeps on sourceSet ${sourceSet.name}")
|
||||||
|
sourceSet.output.files.each { File file ->
|
||||||
|
if (!file.exists()) {
|
||||||
|
return // skip
|
||||||
|
}
|
||||||
|
List<String> cmd = applyExplicitCommand(baseCmd)
|
||||||
|
logger.info("jdeps command set to ${cmd.join(' ')}")
|
||||||
|
executeCommandOn(cmd, file.absolutePath)
|
||||||
|
/*if (output) {
|
||||||
|
commandOutput << "\nProject: ${projectName.get()}\n${output}".toString()
|
||||||
|
outputs[sourceSet.name] = output
|
||||||
|
}*/
|
||||||
|
List<String> warnings = getWarnings(output)
|
||||||
|
if (warnings && getResolvedFailOnWarning().get()) {
|
||||||
|
throw new IllegalStateException("jdeps reported warnings: " +
|
||||||
|
System.lineSeparator() +
|
||||||
|
warnings.join(System.lineSeparator()))
|
||||||
|
}
|
||||||
|
List<String> errors = getErrors(output)
|
||||||
|
if (errors) {
|
||||||
|
throw new IllegalStateException("jdeps reported errors: " +
|
||||||
|
System.lineSeparator() +
|
||||||
|
errors.join(System.lineSeparator()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String c : resolvedConfigurations.get()) {
|
||||||
|
inspectConfiguration(projectConfigurations[c.trim()], baseCmd)
|
||||||
|
}
|
||||||
|
for (String c : resolvedClasspaths.get()) {
|
||||||
|
inspectConfiguration(projectConfigurations[c.trim()], baseCmd)
|
||||||
|
}
|
||||||
|
/*if (commandOutput) {
|
||||||
|
commandOutput = commandOutput.unique()
|
||||||
|
if (resolvedConsoleOutput.get()) {
|
||||||
|
println commandOutput.join('\n')
|
||||||
|
}
|
||||||
|
File parentFile = reportDir.get().asFile
|
||||||
|
if (!parentFile.exists()) {
|
||||||
|
parentFile.mkdirs()
|
||||||
|
}
|
||||||
|
File logFile = new File(parentFile, 'jdeps-report.txt')
|
||||||
|
logFile.append(commandOutput.join('\n'))
|
||||||
|
String prefix = 'jdeps-'
|
||||||
|
if (resolvedListDeps.get()) {
|
||||||
|
prefix = 'list-deps-'
|
||||||
|
} else if (resolvedListReducedDeps.get()) {
|
||||||
|
prefix = 'list-reduced-deps-'
|
||||||
|
} else if (resolvedPrintModuleDeps.get()) {
|
||||||
|
prefix = 'print-module-deps-'
|
||||||
|
}
|
||||||
|
outputs.each { k, v ->
|
||||||
|
logFile = new File(parentFile, prefix + k + '.txt')
|
||||||
|
logFile.append(v)
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private void inspectConfiguration(Configuration configuration,
|
||||||
|
List<String> baseCmd) {
|
||||||
|
logger.info("Running jdeps on configuration ${configuration.name}")
|
||||||
|
configuration.resolve().each { File file ->
|
||||||
|
if (!file.exists()) {
|
||||||
|
return // skip
|
||||||
|
}
|
||||||
|
List<String> command = new ArrayList<>(baseCmd)
|
||||||
|
if (JavaVersion.current().java9Compatible) {
|
||||||
|
String multiReleaseVersion = resolveMultiReleaseVersion(file.name, resolvedMultiReleaseJars.get())
|
||||||
|
if (multiReleaseVersion) {
|
||||||
|
command.add(1, multiReleaseVersion)
|
||||||
|
command.add(1, '--multi-release')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command = applyExplicitCommand(command)
|
||||||
|
logger.info("jdeps command set to: ${command.join(' ')} ${file.absolutePath}")
|
||||||
|
executeCommandOn(command, file.absolutePath)
|
||||||
|
//if (output) {
|
||||||
|
//commandOutput << "\nDependency: ${file.name}\n${output}".toString()
|
||||||
|
//outputs[configuration.name] = output
|
||||||
|
//}
|
||||||
|
/*List<String> warnings = getWarnings(output)
|
||||||
|
if (warnings && getResolvedFailOnWarning().get()) {
|
||||||
|
throw new IllegalStateException("jdeps reported errors/warnings: " +
|
||||||
|
System.lineSeparator() +
|
||||||
|
warnings.join(System.lineSeparator()))
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> applyExplicitCommand(List<String> cmd) {
|
||||||
|
List<String> c = new ArrayList<>(cmd)
|
||||||
|
String subcommand = ''
|
||||||
|
if (resolvedListDeps.get()) {
|
||||||
|
subcommand = '--list-deps'
|
||||||
|
} else if (resolvedListReducedDeps.get()) {
|
||||||
|
subcommand = '--list-reduced-deps'
|
||||||
|
} else if (resolvedPrintModuleDeps.get()) {
|
||||||
|
subcommand = '--print-module-deps'
|
||||||
|
}
|
||||||
|
if (subcommand) {
|
||||||
|
if (c.contains('--class-path')) {
|
||||||
|
c.add(c.indexOf('--class-path'), subcommand)
|
||||||
|
} else if (c.contains('--module-path')) {
|
||||||
|
c.add(c.indexOf('--module-path'), subcommand)
|
||||||
|
} else {
|
||||||
|
c << subcommand
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
private String executeCommandOn(List<String> baseCmd, String path) {
|
||||||
|
List<String> cmd = []
|
||||||
|
cmd.addAll(baseCmd)
|
||||||
|
cmd.add(path)
|
||||||
|
return executeCommand(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
private String executeCommand(List<String> cmd) {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream()
|
||||||
|
execOperations.exec(e -> {
|
||||||
|
e.setErrorOutput(out)
|
||||||
|
e.commandLine(cmd)
|
||||||
|
})
|
||||||
|
return out.toString().trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String resolveMultiReleaseVersion(String artifactName, Map<String, String> multiReleaseJars) {
|
||||||
|
for (Map.Entry<String, String> e : multiReleaseJars.entrySet()) {
|
||||||
|
if (artifactName.matches(e.key.trim())) {
|
||||||
|
return e.value.trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> getWarnings(String output) {
|
||||||
|
List<String> warnings = []
|
||||||
|
output.eachLine { String line ->
|
||||||
|
if (WARNING.matcher(line).matches()) {
|
||||||
|
warnings.add(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
warnings
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> getErrors(String output) {
|
||||||
|
List<String> errors = []
|
||||||
|
output.eachLine { String line ->
|
||||||
|
if (ERROR.matcher(line).matches()) {
|
||||||
|
errors.add(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errors
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps
|
||||||
|
|
||||||
|
import groovy.transform.Canonical
|
||||||
|
|
||||||
|
@Canonical
|
||||||
|
class ModuleOptions {
|
||||||
|
|
||||||
|
List<String> addModules = []
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
|
||||||
|
interface BooleanState {
|
||||||
|
|
||||||
|
Property<Boolean> getProperty()
|
||||||
|
|
||||||
|
Provider<Boolean> getProvider()
|
||||||
|
|
||||||
|
boolean getValue()
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.file.Directory
|
||||||
|
import org.gradle.api.file.DirectoryProperty
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
|
||||||
|
interface DirectoryState {
|
||||||
|
|
||||||
|
DirectoryProperty getProperty()
|
||||||
|
|
||||||
|
Provider<Directory> getProvider()
|
||||||
|
|
||||||
|
Directory getValue()
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.provider.ListProperty
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
|
||||||
|
interface ListState {
|
||||||
|
|
||||||
|
ListProperty<String> getProperty()
|
||||||
|
|
||||||
|
Provider<List<String>> getProvider()
|
||||||
|
|
||||||
|
List<String> getValue()
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util;
|
||||||
|
|
||||||
|
import org.gradle.api.provider.MapProperty
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
|
||||||
|
interface MapState {
|
||||||
|
|
||||||
|
MapProperty<String, String> getProperty()
|
||||||
|
|
||||||
|
Provider<Map<String, String>> getProvider()
|
||||||
|
|
||||||
|
Map<String, String> getValue()
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.internal.provider.Providers
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.provider.ProviderFactory
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull
|
||||||
|
|
||||||
|
final class SimpleBooleanState implements BooleanState {
|
||||||
|
|
||||||
|
final Property<Boolean> property
|
||||||
|
|
||||||
|
final Provider<Boolean> provider
|
||||||
|
|
||||||
|
private final Project project
|
||||||
|
|
||||||
|
SimpleBooleanState(Project project,
|
||||||
|
Property<Boolean> property,
|
||||||
|
Provider<Boolean> provider) {
|
||||||
|
this.project = requireNonNull(project, "Argument 'project' must not be null")
|
||||||
|
this.property = requireNonNull(property, "Argument 'property' must not be null")
|
||||||
|
this.provider = requireNonNull(provider, "Argument 'provider' must not be null")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean getValue() {
|
||||||
|
booleanProvider(project.providers, property, provider, false).get()
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleBooleanState of(Task task,
|
||||||
|
String key,
|
||||||
|
boolean defaultValue) {
|
||||||
|
requireNonNull(task, "Argument 'task' must not be null")
|
||||||
|
Project project = task.project
|
||||||
|
Property<Boolean> property = project.objects.property(Boolean).convention(Providers.<Boolean>notDefined())
|
||||||
|
Provider<Boolean> provider = booleanProvider(key, property, project, defaultValue)
|
||||||
|
new SimpleBooleanState(project, property, provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<Boolean> booleanProvider(ProviderFactory providers,
|
||||||
|
Property<Boolean> property,
|
||||||
|
Provider<Boolean> provider,
|
||||||
|
boolean defaultValue) {
|
||||||
|
providers.provider {
|
||||||
|
return provider ? provider.getOrElse(property.getOrElse(defaultValue)) : property.getOrElse(defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<Boolean> booleanProvider(String key,
|
||||||
|
Property<Boolean> property,
|
||||||
|
Project project,
|
||||||
|
boolean defaultValue) {
|
||||||
|
booleanProvider(toEnv(key), toProperty(key), property, project, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<Boolean> booleanProvider(String envKey,
|
||||||
|
String propertyKey,
|
||||||
|
Property<Boolean> property,
|
||||||
|
Project project,
|
||||||
|
boolean defaultValue) {
|
||||||
|
project.providers.provider {
|
||||||
|
String value = resolveValue(envKey, propertyKey, project)
|
||||||
|
return !isBlank(value) ? Boolean.parseBoolean(value) : property.getOrElse(defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String resolveValue(String envKey, String propertyKey, Project project) {
|
||||||
|
String value = System.getenv(envKey)
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = System.getProperty(propertyKey)
|
||||||
|
}
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = project.findProperty(propertyKey) as String
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBlank(String str) {
|
||||||
|
if (str == null || str.length() == 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for (char c : str.toCharArray()) {
|
||||||
|
if (!Character.isWhitespace(c)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toEnv(String key) {
|
||||||
|
key.toUpperCase().replace('.', '_')
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toProperty(String key) {
|
||||||
|
key.uncapitalize().replace('_', '.')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.file.Directory
|
||||||
|
import org.gradle.api.file.DirectoryProperty
|
||||||
|
import org.gradle.api.internal.provider.Providers
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.provider.ProviderFactory
|
||||||
|
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull
|
||||||
|
|
||||||
|
final class SimpleDirectoryState implements DirectoryState {
|
||||||
|
|
||||||
|
final DirectoryProperty property
|
||||||
|
|
||||||
|
final Provider<Directory> provider
|
||||||
|
|
||||||
|
private final Project project
|
||||||
|
|
||||||
|
SimpleDirectoryState(Project project,
|
||||||
|
DirectoryProperty property,
|
||||||
|
Provider<Directory> provider) {
|
||||||
|
this.project = requireNonNull(project, "Argument 'project' must not be null")
|
||||||
|
this.property = requireNonNull(property, "Argument 'property' must not be null")
|
||||||
|
this.provider = requireNonNull(provider, "Argument 'provider' must not be null")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Directory getValue() {
|
||||||
|
directoryProvider(project.providers, property, provider, (Directory) null).get()
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleDirectoryState of(Task task, String key, Directory defaultValue) {
|
||||||
|
requireNonNull(task, "Argument 'task' must not be null")
|
||||||
|
Project project = task.project
|
||||||
|
DirectoryProperty property = project.objects.directoryProperty().convention(Providers.<Directory>notDefined())
|
||||||
|
Provider<Directory> provider = directoryProvider(key, property, project, defaultValue)
|
||||||
|
new SimpleDirectoryState(project, property, provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<Directory> directoryProvider(ProviderFactory providers,
|
||||||
|
DirectoryProperty property,
|
||||||
|
Provider<Directory> provider,
|
||||||
|
Directory defaultValue) {
|
||||||
|
providers.provider {
|
||||||
|
return provider ? provider.getOrElse(property.getOrElse(defaultValue)) : property.getOrElse(defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<Directory> directoryProvider(String key,
|
||||||
|
DirectoryProperty property,
|
||||||
|
Project project,
|
||||||
|
Directory defaultValue) {
|
||||||
|
directoryProvider(toEnv(key), toProperty(key), property, project, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<Directory> directoryProvider(String envKey,
|
||||||
|
String propertyKey,
|
||||||
|
DirectoryProperty property,
|
||||||
|
Project project,
|
||||||
|
Directory defaultValue) {
|
||||||
|
project.providers.provider {
|
||||||
|
String value = resolveValue(envKey, propertyKey, project)
|
||||||
|
if (!isBlank(value)) {
|
||||||
|
DirectoryProperty p = project.objects.directoryProperty()
|
||||||
|
p.set(Paths.get(value).toFile())
|
||||||
|
return p.get()
|
||||||
|
}
|
||||||
|
return property.getOrElse(defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String resolveValue(String envKey, String propertyKey, Project project) {
|
||||||
|
String value = System.getenv(envKey)
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = System.getProperty(propertyKey)
|
||||||
|
}
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = project.findProperty(propertyKey) as String
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBlank(String str) {
|
||||||
|
if (str == null || str.length() == 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for (char c : str.toCharArray()) {
|
||||||
|
if (!Character.isWhitespace(c)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toEnv(String key) {
|
||||||
|
key.toUpperCase().replace('.', '_')
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toProperty(String key) {
|
||||||
|
key.uncapitalize().replace('_', '.')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.internal.provider.Providers
|
||||||
|
import org.gradle.api.provider.ListProperty
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.provider.ProviderFactory
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull
|
||||||
|
|
||||||
|
final class SimpleListState implements ListState {
|
||||||
|
|
||||||
|
final ListProperty<String> property
|
||||||
|
|
||||||
|
final Provider<List<String>> provider
|
||||||
|
|
||||||
|
private final Project project
|
||||||
|
|
||||||
|
SimpleListState(Project project,
|
||||||
|
ListProperty<String> property,
|
||||||
|
Provider<List<String>> provider) {
|
||||||
|
this.project = requireNonNull(project, "Argument 'project' must not be null")
|
||||||
|
this.property = requireNonNull(property, "Argument 'property' must not be null")
|
||||||
|
this.provider = requireNonNull(provider, "Argument 'provider' must not be null")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
List<String> getValue() {
|
||||||
|
listProvider(project.providers, property, provider, Collections.<String> emptyList()).get()
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleListState of(Task task, String key, List<String> defaultValue) {
|
||||||
|
requireNonNull(task, "Argument 'task' must not be null")
|
||||||
|
Project project = task.project
|
||||||
|
ListProperty<String> property = project.objects.listProperty(String).convention(Providers.<List<String>>notDefined())
|
||||||
|
Provider<List<String>> provider = listProvider(key, property, project, defaultValue)
|
||||||
|
new SimpleListState(project, property, provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<List<String>> listProvider(ProviderFactory providers,
|
||||||
|
ListProperty<String> property,
|
||||||
|
Provider<List<String>> provider,
|
||||||
|
List<String> defaultValue) {
|
||||||
|
providers.provider {
|
||||||
|
return provider ? provider.getOrElse(property.getOrElse(defaultValue)) : property.getOrElse(defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<List<String>> listProvider(String key,
|
||||||
|
ListProperty<String> property,
|
||||||
|
Project project,
|
||||||
|
List<String> defaultValue) {
|
||||||
|
listProvider(toEnv(key), toProperty(key), property, project, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<List<String>> listProvider(String envKey,
|
||||||
|
String propertyKey,
|
||||||
|
ListProperty<String> property,
|
||||||
|
Project project,
|
||||||
|
List<String> defaultValue) {
|
||||||
|
project.providers.provider {
|
||||||
|
if (property.present) {
|
||||||
|
return property.get()
|
||||||
|
}
|
||||||
|
String value = resolveValue(envKey, propertyKey, project)
|
||||||
|
if (!isBlank(value)) {
|
||||||
|
List<String> list = new ArrayList<>()
|
||||||
|
for (String v : value.split(',')) {
|
||||||
|
if (!isBlank(v)) list.add(v.trim())
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String resolveValue(String envKey, String propertyKey, Project project) {
|
||||||
|
String value = System.getenv(envKey)
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = System.getProperty(propertyKey)
|
||||||
|
}
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = project.findProperty(propertyKey) as String
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBlank(String str) {
|
||||||
|
if (str == null || str.length() == 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for (char c : str.toCharArray()) {
|
||||||
|
if (!Character.isWhitespace(c)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toEnv(String key) {
|
||||||
|
key.toUpperCase().replace('.', '_')
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toProperty(String key) {
|
||||||
|
key.uncapitalize().replace('_', '.')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.internal.provider.Providers
|
||||||
|
import org.gradle.api.provider.MapProperty
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.provider.ProviderFactory
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull
|
||||||
|
|
||||||
|
final class SimpleMapState implements MapState {
|
||||||
|
|
||||||
|
final MapProperty<String, String> property
|
||||||
|
|
||||||
|
final Provider<Map<String, String>> provider
|
||||||
|
|
||||||
|
private final Project project
|
||||||
|
|
||||||
|
SimpleMapState(Project project,
|
||||||
|
MapProperty<String, String> property,
|
||||||
|
Provider<Map<String, String>> provider) {
|
||||||
|
this.project = requireNonNull(project, "Argument 'project' must not be null")
|
||||||
|
this.property = requireNonNull(property, "Argument 'property' must not be null")
|
||||||
|
this.provider = requireNonNull(provider, "Argument 'provider' must not be null")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Map<String, String> getValue() {
|
||||||
|
mapProvider(project.providers, property, provider, Collections.<String, String> emptyMap()).get()
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleMapState of(Task task, String key, Map<String, String> defaultValue) {
|
||||||
|
requireNonNull(task, "Argument 'task' must not be null")
|
||||||
|
Project project = task.project
|
||||||
|
MapProperty<String, String> property = project.objects.mapProperty(String, String).convention(Providers.<Map<String, String>>notDefined())
|
||||||
|
Provider<Map<String, String>> provider = mapProvider(key, property, project, defaultValue)
|
||||||
|
new SimpleMapState(project, property, provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
static <K, V> Provider<Map<K, V>> mapProvider(ProviderFactory providers,
|
||||||
|
MapProperty<K, V> property,
|
||||||
|
Provider<Map<K, V>> provider,
|
||||||
|
Map<K, V> defaultValue) {
|
||||||
|
providers.provider {
|
||||||
|
Map<K, V> map = new LinkedHashMap<>(defaultValue)
|
||||||
|
if (property.present) {
|
||||||
|
map.putAll(property.get())
|
||||||
|
}
|
||||||
|
if (provider) {
|
||||||
|
map.putAll(provider.get())
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static <K,V> Provider<Map<K, V>> mapProvider(String key,
|
||||||
|
MapProperty<K, V> property,
|
||||||
|
Project project,
|
||||||
|
Map<K, V> defaultValue) {
|
||||||
|
mapProvider(toEnv(key), toProperty(key), property, project, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
static <K, V> Provider<Map<K, V>> mapProvider(String envKey,
|
||||||
|
String propertyKey,
|
||||||
|
MapProperty<K, V> property,
|
||||||
|
Project project,
|
||||||
|
Map<K, V> defaultValue) {
|
||||||
|
project.providers.provider {
|
||||||
|
String value = resolveValue(envKey, propertyKey, project)
|
||||||
|
if (i!sBlank(value)) {
|
||||||
|
Map<K, V> map = new LinkedHashMap<>()
|
||||||
|
for (String val : value.split(',')) {
|
||||||
|
String[] kv = val.split('=')
|
||||||
|
if (kv.length == 2) {
|
||||||
|
map.put(kv[0] as K, kv[1] as V)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
return property.orElse(defaultValue).get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String resolveValue(String envKey, String propertyKey, Project project) {
|
||||||
|
String value = System.getenv(envKey)
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = System.getProperty(propertyKey)
|
||||||
|
}
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = project.findProperty(propertyKey) as String
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBlank(String str) {
|
||||||
|
if (str == null || str.length() == 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for (char c : str.toCharArray()) {
|
||||||
|
if (!Character.isWhitespace(c)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toEnv(String key) {
|
||||||
|
key.toUpperCase().replace('.', '_')
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toProperty(String key) {
|
||||||
|
key.uncapitalize().replace('_', '.')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.internal.provider.Providers
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.provider.ProviderFactory
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull
|
||||||
|
|
||||||
|
final class SimpleStringState implements StringState {
|
||||||
|
|
||||||
|
final Property<String> property
|
||||||
|
|
||||||
|
final Provider<String> provider
|
||||||
|
|
||||||
|
private final Project project
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String getValue() {
|
||||||
|
stringProvider(project.providers, property, provider, '').get()
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleStringState(Project project,
|
||||||
|
Property<String> property,
|
||||||
|
Provider<String> provider) {
|
||||||
|
this.project = requireNonNull(project, "Argument 'project' must not be null")
|
||||||
|
this.property = requireNonNull(property, "Argument 'property' must not be null")
|
||||||
|
this.provider = requireNonNull(provider, "Argument 'provider' must not be null")
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleStringState of(Task task, String key, String defaultValue) {
|
||||||
|
requireNonNull(task, "Argument 'task' must not be null")
|
||||||
|
Project project = task.project
|
||||||
|
Property<String> property = project.objects.property(String).convention(Providers.<String>notDefined())
|
||||||
|
Provider<String> provider = stringProvider(key, property, project, defaultValue)
|
||||||
|
new SimpleStringState(project, property, provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<String> stringProvider(ProviderFactory providers,
|
||||||
|
Property<String> property,
|
||||||
|
Provider<String> provider,
|
||||||
|
String defaultValue) {
|
||||||
|
providers.provider {
|
||||||
|
return provider ? provider.getOrElse(property.getOrElse(defaultValue)) : property.getOrElse(defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<String> stringProvider(String key,
|
||||||
|
Property<String> property,
|
||||||
|
Project project,
|
||||||
|
String defaultValue) {
|
||||||
|
stringProvider(toEnv(key), toProperty(key), property, project, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Provider<String> stringProvider(String envKey,
|
||||||
|
String propertyKey,
|
||||||
|
Property<String> property,
|
||||||
|
Project project,
|
||||||
|
String defaultValue) {
|
||||||
|
project.providers.provider {
|
||||||
|
String value = resolveValue(envKey, propertyKey, project)
|
||||||
|
return !isBlank(value) ? value : property.getOrElse(defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String resolveValue(String envKey, String propertyKey, Project project) {
|
||||||
|
String value = System.getenv(envKey)
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = System.getProperty(propertyKey)
|
||||||
|
}
|
||||||
|
if (isBlank(value)) {
|
||||||
|
value = project.findProperty(propertyKey) as String
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBlank(String str) {
|
||||||
|
if (str == null || str.length() == 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for (char c : str.toCharArray()) {
|
||||||
|
if (!Character.isWhitespace(c)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toEnv(String key) {
|
||||||
|
key.toUpperCase().replace('.', '_')
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toProperty(String key) {
|
||||||
|
key.uncapitalize().replace('_', '.')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.xbib.gradle.plugin.jdeps.util
|
||||||
|
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
|
||||||
|
interface StringState {
|
||||||
|
|
||||||
|
Property<String> getProperty()
|
||||||
|
|
||||||
|
Provider<String> getProvider()
|
||||||
|
|
||||||
|
String getValue()
|
||||||
|
}
|
32
gradle-plugin-jlink/build.gradle
Executable file
32
gradle-plugin-jlink/build.gradle
Executable file
|
@ -0,0 +1,32 @@
|
||||||
|
plugins {
|
||||||
|
id 'java-gradle-plugin'
|
||||||
|
alias(libs.plugins.publish)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'java-gradle-plugin'
|
||||||
|
apply plugin: 'com.gradle.plugin-publish'
|
||||||
|
|
||||||
|
apply from: rootProject.file('gradle/compile/groovy.gradle')
|
||||||
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api gradleApi()
|
||||||
|
testImplementation gradleTestKit()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.hasProperty('gradle.publish.key')) {
|
||||||
|
gradlePlugin {
|
||||||
|
website = 'https://xbib.org/joerg/gradle-plugins/src/branch/main/gradle-plugin-jlink'
|
||||||
|
vcsUrl = 'https://xbib.org/joerg/gradle-plugins'
|
||||||
|
plugins {
|
||||||
|
jlinkPlugin {
|
||||||
|
id = 'org.xbib.gradle.plugin.jlink'
|
||||||
|
implementationClass = 'org.xbib.gradle.plugin.jlink.JlinkPlugin'
|
||||||
|
version = project.version
|
||||||
|
description = 'Gradle Jlink plugin'
|
||||||
|
displayName = 'Gradle Jlink plugin'
|
||||||
|
tags.set(['jlink'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
gradle-plugin-jpackage/NOTICE.txt
Normal file
9
gradle-plugin-jpackage/NOTICE.txt
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
This work is based on
|
||||||
|
|
||||||
|
https://github.com/gradlex-org/java-module-packaging
|
||||||
|
|
||||||
|
by Jendrik Johannes
|
||||||
|
|
||||||
|
as of Feb 11, 2024
|
||||||
|
|
||||||
|
License: Apache 2.0
|
|
@ -12,6 +12,8 @@ apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
dependencies {
|
dependencies {
|
||||||
api gradleApi()
|
api gradleApi()
|
||||||
testImplementation gradleTestKit()
|
testImplementation gradleTestKit()
|
||||||
|
testImplementation testLibs.spock.core
|
||||||
|
testImplementation testLibs.spock.junit4
|
||||||
}
|
}
|
||||||
|
|
||||||
if (project.hasProperty('gradle.publish.key')) {
|
if (project.hasProperty('gradle.publish.key')) {
|
||||||
|
@ -19,7 +21,7 @@ if (project.hasProperty('gradle.publish.key')) {
|
||||||
website = 'https://xbib.org/joerg/gradle-plugins/src/branch/main/gradle-plugin-jpackage'
|
website = 'https://xbib.org/joerg/gradle-plugins/src/branch/main/gradle-plugin-jpackage'
|
||||||
vcsUrl = 'https://xbib.org/joerg/gradle-plugins'
|
vcsUrl = 'https://xbib.org/joerg/gradle-plugins'
|
||||||
plugins {
|
plugins {
|
||||||
jaccPlugin {
|
jpackagePlugin {
|
||||||
id = 'org.xbib.gradle.plugin.jpackage'
|
id = 'org.xbib.gradle.plugin.jpackage'
|
||||||
implementationClass = 'org.xbib.gradle.plugin.jpackage.JPackagePlugin'
|
implementationClass = 'org.xbib.gradle.plugin.jpackage.JPackagePlugin'
|
||||||
version = project.version
|
version = project.version
|
||||||
|
|
|
@ -94,7 +94,7 @@ public abstract class JPackageExtension {
|
||||||
target.getPackageTypes().convention(target.getOperatingSystem().map(os -> switch (os) {
|
target.getPackageTypes().convention(target.getOperatingSystem().map(os -> switch (os) {
|
||||||
case WINDOWS -> Arrays.asList("exe", "msi");
|
case WINDOWS -> Arrays.asList("exe", "msi");
|
||||||
case MACOS -> Arrays.asList("pkg", "dmg");
|
case MACOS -> Arrays.asList("pkg", "dmg");
|
||||||
case LINUX -> Arrays.asList("rpm", "deb");
|
case LINUX -> Arrays.asList("rpm");
|
||||||
default -> Collections.emptyList();
|
default -> Collections.emptyList();
|
||||||
}));
|
}));
|
||||||
ConfigurationContainer configurations = getProject().getConfigurations();
|
ConfigurationContainer configurations = getProject().getConfigurations();
|
||||||
|
|
|
@ -1,19 +1,3 @@
|
||||||
/*
|
|
||||||
* Copyright the GradleX team.
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
*
|
|
||||||
* http://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 org.xbib.gradle.plugin.jpackage;
|
package org.xbib.gradle.plugin.jpackage;
|
||||||
|
|
||||||
import org.gradle.api.NonNullApi;
|
import org.gradle.api.NonNullApi;
|
||||||
|
|
|
@ -115,7 +115,9 @@ public abstract class JPackageTask extends DefaultTask {
|
||||||
c.rename(f -> f.replace("icon", getApplicationName().get()));
|
c.rename(f -> f.replace("icon", getApplicationName().get()));
|
||||||
});
|
});
|
||||||
String executableName = WINDOWS.equals(os) ? "jpackage.exe" : "jpackage";
|
String executableName = WINDOWS.equals(os) ? "jpackage.exe" : "jpackage";
|
||||||
String jpackage = getJavaInstallation().get().getInstallationPath().file("bin/" + executableName).getAsFile().getAbsolutePath();
|
String jpackage = getJavaInstallation().get()
|
||||||
|
.getInstallationPath().file("bin/" + executableName)
|
||||||
|
.getAsFile().getAbsolutePath();
|
||||||
getExec().exec(e -> {
|
getExec().exec(e -> {
|
||||||
e.commandLine(
|
e.commandLine(
|
||||||
jpackage,
|
jpackage,
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package org.xbib.gradle.plugin.jpackage
|
||||||
|
|
||||||
|
import org.xbib.gradle.plugin.jpackage.fixture.GradleBuild
|
||||||
|
import spock.lang.Specification
|
||||||
|
|
||||||
|
import static org.gradle.testkit.runner.TaskOutcome.FAILED
|
||||||
|
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
|
||||||
|
import static org.xbib.gradle.plugin.jpackage.fixture.GradleBuild.hostOs
|
||||||
|
import static org.xbib.gradle.plugin.jpackage.fixture.GradleBuild.runsOnLinux
|
||||||
|
import static org.xbib.gradle.plugin.jpackage.fixture.GradleBuild.runsOnMacos
|
||||||
|
import static org.xbib.gradle.plugin.jpackage.fixture.GradleBuild.runsOnWindows
|
||||||
|
|
||||||
|
class JPackageTest extends Specification {
|
||||||
|
|
||||||
|
@Delegate
|
||||||
|
GradleBuild build = new GradleBuild()
|
||||||
|
|
||||||
|
def "can use plugin on #os with success=#success"() {
|
||||||
|
given:
|
||||||
|
def taskToRun = ":app:assemble${label.capitalize()}"
|
||||||
|
def taskToCheck = ":app:jpackage${label.capitalize()}"
|
||||||
|
def macosArch = System.getProperty('os.arch').contains('aarch') ? 'aarch64' : 'x86-64'
|
||||||
|
appBuildFile << """
|
||||||
|
version = "1.0"
|
||||||
|
jpackage {
|
||||||
|
target("fedora") {
|
||||||
|
operatingSystem = "linux"
|
||||||
|
architecture = "x86-64"
|
||||||
|
}
|
||||||
|
target("macos") {
|
||||||
|
operatingSystem = "macos"
|
||||||
|
architecture = "$macosArch"
|
||||||
|
}
|
||||||
|
target("windows") {
|
||||||
|
operatingSystem = "windows"
|
||||||
|
architecture = "x86-64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
appModuleInfoFile << '''
|
||||||
|
module org.example.app {
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
when:
|
||||||
|
def result = success ? build(taskToRun) : fail(taskToRun)
|
||||||
|
|
||||||
|
then:
|
||||||
|
result.task(taskToCheck).outcome == (success ? SUCCESS : FAILED)
|
||||||
|
success || result.output.contains("> Running on ${hostOs()}; cannot build for $os")
|
||||||
|
|
||||||
|
where:
|
||||||
|
label | os | success
|
||||||
|
'fedora' | 'linux' | runsOnLinux()
|
||||||
|
'windows' | 'windows' | runsOnWindows()
|
||||||
|
'macos' | 'macos' | runsOnMacos()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
package org.xbib.gradle.plugin.jpackage.fixture
|
||||||
|
|
||||||
|
import org.gradle.testkit.runner.BuildResult
|
||||||
|
import org.gradle.testkit.runner.GradleRunner
|
||||||
|
|
||||||
|
import java.lang.management.ManagementFactory
|
||||||
|
import java.nio.file.Files
|
||||||
|
|
||||||
|
class GradleBuild {
|
||||||
|
|
||||||
|
final File projectDir
|
||||||
|
final File settingsFile
|
||||||
|
final File appBuildFile
|
||||||
|
final File appModuleInfoFile
|
||||||
|
final File libBuildFile
|
||||||
|
final File libModuleInfoFile
|
||||||
|
|
||||||
|
final String gradleVersionUnderTest = System.getProperty('gradleVersionUnderTest')
|
||||||
|
|
||||||
|
GradleBuild(File projectDir = Files.createTempDirectory('gradle-build').toFile()) {
|
||||||
|
this.projectDir = projectDir
|
||||||
|
this.settingsFile = file('settings.gradle.kts')
|
||||||
|
this.appBuildFile = file('app/build.gradle.kts')
|
||||||
|
this.appModuleInfoFile = file('app/src/main/java/module-info.java')
|
||||||
|
this.libBuildFile = file('lib/build.gradle.kts')
|
||||||
|
this.libModuleInfoFile = file('lib/src/main/java/module-info.java')
|
||||||
|
|
||||||
|
file('app/src/main/resourcesPackage/windows').mkdirs()
|
||||||
|
file('app/src/main/resourcesPackage/macos').mkdirs()
|
||||||
|
file('app/src/main/resourcesPackage/linux').mkdirs()
|
||||||
|
|
||||||
|
settingsFile << '''
|
||||||
|
dependencyResolutionManagement { repositories.mavenCentral() }
|
||||||
|
includeBuild(".")
|
||||||
|
rootProject.name = "test-project"
|
||||||
|
include("app", "lib")
|
||||||
|
'''
|
||||||
|
appBuildFile << '''
|
||||||
|
plugins {
|
||||||
|
id("org.xbib.gradle.plugin.jpackage")
|
||||||
|
id("application")
|
||||||
|
}
|
||||||
|
group = "org.example"
|
||||||
|
application {
|
||||||
|
mainModule.set("org.example.app")
|
||||||
|
mainClass.set("org.example.app.Main")
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
file("app/src/main/java/org/example/app/Main.java") << '''
|
||||||
|
package org.example.app;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String... args) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
file("app/src/test/java/org/example/app/test/MainTest.java") << '''
|
||||||
|
package org.example.app.test;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.example.app.Main;
|
||||||
|
|
||||||
|
public class MainTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testApp() {
|
||||||
|
new Main();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
libBuildFile << '''
|
||||||
|
plugins {
|
||||||
|
id("org.xbib.gradle.plugin.jpackage")
|
||||||
|
id("java-library")
|
||||||
|
}
|
||||||
|
group = "org.example"
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
|
||||||
|
File file(String path) {
|
||||||
|
new File(projectDir, path).tap {
|
||||||
|
it.getParentFile().mkdirs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean runsOnWindows() {
|
||||||
|
hostOs().contains('win')
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean runsOnMacos() {
|
||||||
|
hostOs().contains('mac')
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean runsOnLinux() {
|
||||||
|
!runsOnWindows() && !runsOnMacos()
|
||||||
|
}
|
||||||
|
|
||||||
|
static String hostOs() {
|
||||||
|
System.getProperty("os.name").replace(" ", "").toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildResult build(taskToRun) {
|
||||||
|
runner(taskToRun).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildResult fail(taskToRun) {
|
||||||
|
runner(taskToRun).buildAndFail()
|
||||||
|
}
|
||||||
|
|
||||||
|
GradleRunner runner(String... args) {
|
||||||
|
GradleRunner.create()
|
||||||
|
.forwardOutput()
|
||||||
|
.withPluginClasspath()
|
||||||
|
.withProjectDir(projectDir)
|
||||||
|
.withArguments(Arrays.asList(args) + '-s')
|
||||||
|
.withDebug(ManagementFactory.getRuntimeMXBean().getInputArguments().toString().contains("-agentlib:jdwp")).with {
|
||||||
|
gradleVersionUnderTest ? it.withGradleVersion(gradleVersionUnderTest) : it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,8 @@ dependencyResolutionManagement {
|
||||||
library('rpm', 'org.xbib', 'rpm-core').version('4.0.0')
|
library('rpm', 'org.xbib', 'rpm-core').version('4.0.0')
|
||||||
// MUST be groovy 3 dependency
|
// MUST be groovy 3 dependency
|
||||||
library('groovy-git', 'org.xbib.groovy', 'groovy-git').version('3.0.17.0')
|
library('groovy-git', 'org.xbib.groovy', 'groovy-git').version('3.0.17.0')
|
||||||
|
library('jsoup', 'org.jsoup', 'jsoup').version('1.17.2')
|
||||||
|
library('javapoet', 'com.squareup', 'javapoet').version('1.13.0')
|
||||||
plugin('publish', 'com.gradle.plugin-publish').version('1.2.1')
|
plugin('publish', 'com.gradle.plugin-publish').version('1.2.1')
|
||||||
}
|
}
|
||||||
testLibs {
|
testLibs {
|
||||||
|
@ -47,8 +49,11 @@ include 'gradle-plugin-c'
|
||||||
include 'gradle-plugin-cmake'
|
include 'gradle-plugin-cmake'
|
||||||
include 'gradle-plugin-docker'
|
include 'gradle-plugin-docker'
|
||||||
include 'gradle-plugin-git'
|
include 'gradle-plugin-git'
|
||||||
|
include 'gradle-plugin-j2html'
|
||||||
include 'gradle-plugin-jacc'
|
include 'gradle-plugin-jacc'
|
||||||
|
include 'gradle-plugin-jdeps'
|
||||||
include 'gradle-plugin-jflex'
|
include 'gradle-plugin-jflex'
|
||||||
|
include 'gradle-plugin-jlink'
|
||||||
include 'gradle-plugin-jpackage'
|
include 'gradle-plugin-jpackage'
|
||||||
include 'gradle-plugin-rpm'
|
include 'gradle-plugin-rpm'
|
||||||
include 'gradle-plugin-shadow'
|
include 'gradle-plugin-shadow'
|
||||||
|
|
Loading…
Reference in a new issue