Web component support (#211)
* Foundational components for reading/processing Wattsi files into classes. * Introducing tests for compliance with wattsi definitions. - Several elements have been found to be missing from the code generator. - A long-term strategy for dealing with obsolete elements will be needed. Should we remove and change the API? Or should we deprecate and leave in-place? - Found one element that was accidentally added to the code generator but never used. Hopefully. - Fixes will be applied in following commits. * Added newer elements/tags and cleaned up invalid Tag class. - Added DataTag. - Added SlotTag and TemplateTag to support web components. - Added global attributes to support web components. - Removed GenerateTag. Accidentally introduced by string replacement in code generator. * Upgrading revapi and maven plugin. Configured revapi to ignore removal of GenerateTag. - The GenerateTag class was created accidentally. It does not have any representation in the HTML standard is should not be provided in this library.
This commit is contained in:
141
code_gen/pom.xml
141
code_gen/pom.xml
@@ -1,74 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.j2html</groupId>
|
||||
<artifactId>j2htmlcodegen</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<groupId>com.j2html</groupId>
|
||||
<artifactId>j2htmlcodegen</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<name>j2htmlcodegen</name>
|
||||
<url>https://j2html.com/</url>
|
||||
<name>j2htmlcodegen</name>
|
||||
<url>https://j2html.com/</url>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.14.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup</groupId>
|
||||
<artifactId>javapoet</artifactId>
|
||||
<version>1.9.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
|
||||
<plugins>
|
||||
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
|
||||
<plugin>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</plugin>
|
||||
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<version>2.5.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</plugin>
|
||||
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
|
||||
<plugin>
|
||||
<artifactId>maven-site-plugin</artifactId>
|
||||
<version>3.7.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
<build>
|
||||
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
|
||||
<plugins>
|
||||
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
|
||||
<plugin>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</plugin>
|
||||
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<version>2.5.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</plugin>
|
||||
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
|
||||
<plugin>
|
||||
<artifactId>maven-site-plugin</artifactId>
|
||||
<version>3.7.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -63,7 +63,7 @@ public final class TagCreatorCodeGenerator {
|
||||
}
|
||||
|
||||
// This is a method that contains all ContainerTags, there is nothing below it
|
||||
static List<String> emptyTags() {
|
||||
public static List<String> emptyTags() {
|
||||
return Arrays.asList(
|
||||
"area",
|
||||
"base",
|
||||
@@ -84,7 +84,7 @@ public final class TagCreatorCodeGenerator {
|
||||
);
|
||||
}
|
||||
|
||||
static List<String> containerTags() {
|
||||
public static List<String> containerTags() {
|
||||
return Arrays.asList(
|
||||
"a",
|
||||
"abbr",
|
||||
@@ -103,6 +103,7 @@ public final class TagCreatorCodeGenerator {
|
||||
"cite",
|
||||
"code",
|
||||
"colgroup",
|
||||
"data",
|
||||
"datalist",
|
||||
"dd",
|
||||
"del",
|
||||
@@ -134,7 +135,6 @@ public final class TagCreatorCodeGenerator {
|
||||
"label",
|
||||
"legend",
|
||||
"li",
|
||||
"generate",
|
||||
"main",
|
||||
"map",
|
||||
"mark",
|
||||
@@ -161,6 +161,7 @@ public final class TagCreatorCodeGenerator {
|
||||
"script",
|
||||
"section",
|
||||
"select",
|
||||
"slot",
|
||||
"small",
|
||||
"span",
|
||||
"strong",
|
||||
@@ -171,6 +172,7 @@ public final class TagCreatorCodeGenerator {
|
||||
"table",
|
||||
"tbody",
|
||||
"td",
|
||||
"template",
|
||||
"textarea",
|
||||
"tfoot",
|
||||
"th",
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package j2html_codegen.generators;
|
||||
|
||||
import com.squareup.javapoet.ClassName;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import j2html_codegen.wattsi.AttributeDefinition;
|
||||
import j2html_codegen.wattsi.ElementDefinition;
|
||||
import j2html_codegen.wattsi.WattsiSource;
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ public final class AttributesList {
|
||||
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","textarea"),
|
||||
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"),
|
||||
@@ -174,7 +174,7 @@ public final class AttributesList {
|
||||
//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","input","li","option","meter","progress","param"),
|
||||
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 j2html_codegen.wattsi;
|
||||
|
||||
public interface AttributeDefinition {
|
||||
|
||||
String name();
|
||||
|
||||
boolean appliesTo(ElementDefinition element);
|
||||
|
||||
boolean isObsolete();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package j2html_codegen.wattsi;
|
||||
|
||||
public interface ElementDefinition {
|
||||
|
||||
String name();
|
||||
|
||||
boolean isObsolete();
|
||||
|
||||
}
|
||||
204
code_gen/src/main/java/j2html_codegen/wattsi/WattsiSource.java
Normal file
204
code_gen/src/main/java/j2html_codegen/wattsi/WattsiSource.java
Normal file
@@ -0,0 +1,204 @@
|
||||
package 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 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,77 @@
|
||||
package j2html_codegen;
|
||||
|
||||
import j2html_codegen.generators.TagCreatorCodeGenerator;
|
||||
import j2html_codegen.wattsi.ElementDefinition;
|
||||
import 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);
|
||||
}
|
||||
|
||||
}
|
||||
128553
code_gen/src/test/resources/2022-01.wattsi
Normal file
128553
code_gen/src/test/resources/2022-01.wattsi
Normal file
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@
|
||||
|
||||
<groupId>com.j2html</groupId>
|
||||
<artifactId>j2html</artifactId>
|
||||
<version>1.5.1-SNAPSHOT</version>
|
||||
<version>1.6.0-SNAPSHOT</version>
|
||||
|
||||
<name>j2html</name>
|
||||
<description>Java to HTML builder with a fluent API</description>
|
||||
@@ -168,18 +168,33 @@
|
||||
<plugin>
|
||||
<groupId>org.revapi</groupId>
|
||||
<artifactId>revapi-maven-plugin</artifactId>
|
||||
<version>0.14.3</version>
|
||||
<version>0.14.6</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.revapi</groupId>
|
||||
<artifactId>revapi-java</artifactId>
|
||||
<version>0.24.2</version>
|
||||
<version>0.26.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
<oldArtifacts>
|
||||
<artifact>com.j2html:j2html:1.5.0</artifact>
|
||||
</oldArtifacts>
|
||||
<analysisConfiguration>
|
||||
<revapi.differences>
|
||||
<differences>
|
||||
<item>
|
||||
<ignore>true</ignore>
|
||||
<code>java.class.removed</code>
|
||||
<old>class j2html.tags.specialized.GenerateTag</old>
|
||||
<justification>
|
||||
This class should never have been used. It was introduced
|
||||
accidentally by find+replace in the code generator class.
|
||||
</justification>
|
||||
</item>
|
||||
</differences>
|
||||
</revapi.differences>
|
||||
</analysisConfiguration>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
|
||||
@@ -461,6 +461,13 @@ public class TagCreator {
|
||||
public static ColgroupTag colgroup (Attr.ShortForm shortAttr, String text) { return Attr.addTo( new ColgroupTag().withText(text), shortAttr); }
|
||||
public static ColgroupTag colgroup (Attr.ShortForm shortAttr, DomContent... dc) { return Attr.addTo( new ColgroupTag().with(dc), shortAttr); }
|
||||
|
||||
public static DataTag data () { return new DataTag(); }
|
||||
public static DataTag data (String text) { return new DataTag().withText(text); }
|
||||
public static DataTag data (DomContent... dc) { return new DataTag().with(dc); }
|
||||
public static DataTag data (Attr.ShortForm shortAttr) { return Attr.addTo( new DataTag(), shortAttr); }
|
||||
public static DataTag data (Attr.ShortForm shortAttr, String text) { return Attr.addTo( new DataTag().withText(text), shortAttr); }
|
||||
public static DataTag data (Attr.ShortForm shortAttr, DomContent... dc) { return Attr.addTo( new DataTag().with(dc), shortAttr); }
|
||||
|
||||
public static DatalistTag datalist () { return new DatalistTag(); }
|
||||
public static DatalistTag datalist (String text) { return new DatalistTag().withText(text); }
|
||||
public static DatalistTag datalist (DomContent... dc) { return new DatalistTag().with(dc); }
|
||||
@@ -846,6 +853,13 @@ public class TagCreator {
|
||||
public static SelectTag select (Attr.ShortForm shortAttr, String text) { return Attr.addTo( new SelectTag().withText(text), shortAttr); }
|
||||
public static SelectTag select (Attr.ShortForm shortAttr, DomContent... dc) { return Attr.addTo( new SelectTag().with(dc), shortAttr); }
|
||||
|
||||
public static SlotTag slot () { return new SlotTag(); }
|
||||
public static SlotTag slot (String text) { return new SlotTag().withText(text); }
|
||||
public static SlotTag slot (DomContent... dc) { return new SlotTag().with(dc); }
|
||||
public static SlotTag slot (Attr.ShortForm shortAttr) { return Attr.addTo( new SlotTag(), shortAttr); }
|
||||
public static SlotTag slot (Attr.ShortForm shortAttr, String text) { return Attr.addTo( new SlotTag().withText(text), shortAttr); }
|
||||
public static SlotTag slot (Attr.ShortForm shortAttr, DomContent... dc) { return Attr.addTo( new SlotTag().with(dc), shortAttr); }
|
||||
|
||||
public static SmallTag small () { return new SmallTag(); }
|
||||
public static SmallTag small (String text) { return new SmallTag().withText(text); }
|
||||
public static SmallTag small (DomContent... dc) { return new SmallTag().with(dc); }
|
||||
@@ -916,6 +930,13 @@ public class TagCreator {
|
||||
public static TdTag td (Attr.ShortForm shortAttr, String text) { return Attr.addTo( new TdTag().withText(text), shortAttr); }
|
||||
public static TdTag td (Attr.ShortForm shortAttr, DomContent... dc) { return Attr.addTo( new TdTag().with(dc), shortAttr); }
|
||||
|
||||
public static TemplateTag template () { return new TemplateTag(); }
|
||||
public static TemplateTag template (String text) { return new TemplateTag().withText(text); }
|
||||
public static TemplateTag template (DomContent... dc) { return new TemplateTag().with(dc); }
|
||||
public static TemplateTag template (Attr.ShortForm shortAttr) { return Attr.addTo( new TemplateTag(), shortAttr); }
|
||||
public static TemplateTag template (Attr.ShortForm shortAttr, String text) { return Attr.addTo( new TemplateTag().withText(text), shortAttr); }
|
||||
public static TemplateTag template (Attr.ShortForm shortAttr, DomContent... dc) { return Attr.addTo( new TemplateTag().with(dc), shortAttr); }
|
||||
|
||||
public static TextareaTag textarea () { return new TextareaTag(); }
|
||||
public static TextareaTag textarea (String text) { return new TextareaTag().withText(text); }
|
||||
public static TextareaTag textarea (DomContent... dc) { return new TextareaTag().with(dc); }
|
||||
|
||||
@@ -53,6 +53,7 @@ public abstract class Attr {
|
||||
public static final String HTTP_EQUIV = "http-equiv";
|
||||
public static final String ICON = "icon";
|
||||
public static final String ID = "id";
|
||||
public static final String IS = "is";
|
||||
public static final String ISMAP = "ismap";
|
||||
public static final String ITEMPROP = "itemprop";
|
||||
public static final String KEYTYPE = "keytype";
|
||||
@@ -96,6 +97,7 @@ public abstract class Attr {
|
||||
public static final String SHAPE = "shape";
|
||||
public static final String SIZE = "size";
|
||||
public static final String SIZES = "sizes";
|
||||
public static final String SLOT = "slot";
|
||||
public static final String SPAN = "span";
|
||||
public static final String SPELLCHECK = "spellcheck";
|
||||
public static final String SRC = "src";
|
||||
|
||||
@@ -162,8 +162,12 @@ public abstract class Tag<T extends Tag<T>> extends DomContent implements IInsta
|
||||
|
||||
public T withId(String id) { return attr(Attr.ID, id); }
|
||||
|
||||
public T withIs(String element){ return attr(Attr.IS, element); }
|
||||
|
||||
public T withLang(String lang) { return attr(Attr.LANG, lang); }
|
||||
|
||||
public T withSlot(String name){ return attr(Attr.SLOT, name); }
|
||||
|
||||
public T isSpellcheck(){ return attr(Attr.SPELLCHECK, "true"); }
|
||||
|
||||
public T withStyle(String style) { return attr(Attr.STYLE, style); }
|
||||
@@ -191,8 +195,12 @@ public abstract class Tag<T extends Tag<T>> extends DomContent implements IInsta
|
||||
|
||||
public T withCondId(boolean condition, String id) { return condAttr(condition, Attr.ID, id); }
|
||||
|
||||
public T withCondIs(boolean condition, String element){ return condAttr(condition, Attr.IS, element); }
|
||||
|
||||
public T withCondLang(boolean condition, String lang) { return condAttr(condition, Attr.LANG, lang); }
|
||||
|
||||
public T withCondSlot(boolean condition, String name){ return condAttr(condition, Attr.SLOT, name); }
|
||||
|
||||
public T withCondSpellcheck(boolean condition){ return attr(Attr.SPELLCHECK, (condition)?"true":"false"); }
|
||||
|
||||
public T withCondStyle(boolean condition, String style) { return condAttr(condition, Attr.STYLE, style); }
|
||||
|
||||
11
library/src/main/java/j2html/tags/specialized/DataTag.java
Normal file
11
library/src/main/java/j2html/tags/specialized/DataTag.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package j2html.tags.specialized;
|
||||
|
||||
import j2html.tags.ContainerTag;
|
||||
import j2html.tags.attributes.IValue;
|
||||
|
||||
public final class DataTag extends ContainerTag<DataTag>
|
||||
implements IValue<DataTag> {
|
||||
public DataTag() {
|
||||
super("data");
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package j2html.tags.specialized;
|
||||
|
||||
import j2html.tags.ContainerTag;
|
||||
|
||||
public final class GenerateTag extends ContainerTag<GenerateTag> {
|
||||
public GenerateTag() {
|
||||
super("generate");
|
||||
}
|
||||
}
|
||||
11
library/src/main/java/j2html/tags/specialized/SlotTag.java
Normal file
11
library/src/main/java/j2html/tags/specialized/SlotTag.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package j2html.tags.specialized;
|
||||
|
||||
import j2html.tags.ContainerTag;
|
||||
import j2html.tags.attributes.IName;
|
||||
|
||||
public final class SlotTag extends ContainerTag<SlotTag>
|
||||
implements IName<SlotTag> {
|
||||
public SlotTag() {
|
||||
super("slot");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package j2html.tags.specialized;
|
||||
|
||||
import j2html.tags.ContainerTag;
|
||||
|
||||
public final class TemplateTag extends ContainerTag<TemplateTag> {
|
||||
public TemplateTag() {
|
||||
super("template");
|
||||
}
|
||||
}
|
||||
@@ -222,6 +222,7 @@ public class TagCreatorTest {
|
||||
assertThat(cite("Text").render(), is("<cite>Text</cite>"));
|
||||
assertThat(code().render(), is("<code></code>"));
|
||||
assertThat(colgroup().render(), is("<colgroup></colgroup>"));
|
||||
assertThat(data().render(), is("<data></data>"));
|
||||
assertThat(datalist().render(), is("<datalist></datalist>"));
|
||||
assertThat(dd().render(), is("<dd></dd>"));
|
||||
assertThat(dd("Text").render(), is("<dd>Text</dd>"));
|
||||
@@ -287,6 +288,7 @@ public class TagCreatorTest {
|
||||
assertThat(output().render(), is("<output></output>"));
|
||||
assertThat(p().render(), is("<p></p>"));
|
||||
assertThat(p("Text").render(), is("<p>Text</p>"));
|
||||
assertThat(picture().render(), is("<picture></picture>"));
|
||||
assertThat(pre().render(), is("<pre></pre>"));
|
||||
assertThat(progress().render(), is("<progress></progress>"));
|
||||
assertThat(q().render(), is("<q></q>"));
|
||||
@@ -300,6 +302,8 @@ public class TagCreatorTest {
|
||||
assertThat(script().render(), is("<script></script>"));
|
||||
assertThat(section().render(), is("<section></section>"));
|
||||
assertThat(select().render(), is("<select></select>"));
|
||||
assertThat(slot().render(), is("<slot></slot>"));
|
||||
assertThat(slot("Text").render(), is("<slot>Text</slot>"));
|
||||
assertThat(small().render(), is("<small></small>"));
|
||||
assertThat(small("Text").render(), is("<small>Text</small>"));
|
||||
assertThat(span().render(), is("<span></span>"));
|
||||
@@ -317,6 +321,7 @@ public class TagCreatorTest {
|
||||
assertThat(tbody().render(), is("<tbody></tbody>"));
|
||||
assertThat(td().render(), is("<td></td>"));
|
||||
assertThat(td("Text").render(), is("<td>Text</td>"));
|
||||
assertThat(template("Text").render(), is("<template>Text</template>"));
|
||||
assertThat(textarea().render(), is("<textarea></textarea>"));
|
||||
assertThat(tfoot().render(), is("<tfoot></tfoot>"));
|
||||
assertThat(th().render(), is("<th></th>"));
|
||||
|
||||
@@ -40,6 +40,17 @@ public class NewsView {
|
||||
"News and releases",
|
||||
section(attrs("#news"),
|
||||
|
||||
newsPost(
|
||||
"j2html 1.6.0 adds support for web components and newer HTML elements. (Jun 2022)",
|
||||
"1.6.0",
|
||||
false,
|
||||
join("Added", code("TagCreator::data"), "to provide machine-readable translations."),
|
||||
join("Added", code("TagCreator::picture"), "to provide alternative versions of an image for different display/device scenarios."),
|
||||
join("Added", code("TagCreator::slot"), "to provide placeholders for web components."),
|
||||
join("Added", code("TagCreator::template"), "to provide templating of content fragments for web components."),
|
||||
join("Added global attributes used for web components.")
|
||||
),
|
||||
|
||||
newsPost(
|
||||
"j2html 1.5.0 enhances factory methods and class types, overhauls HTML rendering, introduces support for Java modules and fixes several bugs (Jun 2021)",
|
||||
"1.5.0",
|
||||
|
||||
Reference in New Issue
Block a user