initial commit
This commit is contained in:
commit
f5d00d8913
342 changed files with 140728 additions and 0 deletions
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/.settings
|
||||||
|
/.classpath
|
||||||
|
/.project
|
||||||
|
/.gradle
|
||||||
|
**/data
|
||||||
|
**/work
|
||||||
|
**/logs
|
||||||
|
**/.idea
|
||||||
|
**/target
|
||||||
|
**/out
|
||||||
|
**/build
|
||||||
|
.DS_Store
|
||||||
|
*.iml
|
||||||
|
*~
|
||||||
|
*.key
|
||||||
|
*.crt
|
202
LICENSE.txt
Normal file
202
LICENSE.txt
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
35
build.gradle
Normal file
35
build.gradle
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'maven-publish'
|
||||||
|
id 'signing'
|
||||||
|
id "io.github.gradle-nexus.publish-plugin" version "2.0.0-rc-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper {
|
||||||
|
gradleVersion = libs.versions.gradle.get()
|
||||||
|
distributionType = Wrapper.DistributionType.BIN
|
||||||
|
}
|
||||||
|
|
||||||
|
ext {
|
||||||
|
user = 'joerg'
|
||||||
|
name = 'j2html'
|
||||||
|
description = 'Java to HTML generator'
|
||||||
|
inceptionYear = '2024'
|
||||||
|
url = 'https://xbib.org/' + user + '/' + name
|
||||||
|
scmUrl = 'https://xbib.org/' + user + '/' + name
|
||||||
|
scmConnection = 'scm:git:git://xbib.org/' + user + '/' + name + '.git'
|
||||||
|
scmDeveloperConnection = 'scm:git:ssh://forgejo@xbib.org:' + user + '/' + name + '.git'
|
||||||
|
issueManagementSystem = 'Github'
|
||||||
|
issueManagementUrl = ext.scmUrl + '/issues'
|
||||||
|
licenseName = 'The Apache License, Version 2.0'
|
||||||
|
licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||||
|
}
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
apply from: rootProject.file('gradle/repositories/maven.gradle')
|
||||||
|
apply from: rootProject.file('gradle/compile/java.gradle')
|
||||||
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
|
apply from: rootProject.file('gradle/publish/maven.gradle')
|
||||||
|
apply from: rootProject.file('gradle/publish/forgejo.gradle')
|
||||||
|
}
|
||||||
|
apply from: rootProject.file('gradle/publish/sonatype.gradle')
|
3
gradle.properties
Normal file
3
gradle.properties
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
group = org.xbib
|
||||||
|
name = j2html
|
||||||
|
version = 1.6.0
|
29
gradle/compile/java.gradle
Normal file
29
gradle/compile/java.gradle
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
apply plugin: 'java-library'
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(21)
|
||||||
|
}
|
||||||
|
modularity.inferModulePath.set(true)
|
||||||
|
withSourcesJar()
|
||||||
|
withJavadocJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
manifest {
|
||||||
|
attributes('Implementation-Version': project.version)
|
||||||
|
}
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
options.fork = true
|
||||||
|
options.forkOptions.jvmArgs += ['-Duser.language=en','-Duser.country=US']
|
||||||
|
options.compilerArgs.add('-Xlint:all')
|
||||||
|
options.encoding = 'UTF-8'
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(Javadoc) {
|
||||||
|
options.addStringOption('Xdoclint:none', '-quiet')
|
||||||
|
options.encoding = 'UTF-8'
|
||||||
|
}
|
13
gradle/documentation/asciidoc.gradle
Normal file
13
gradle/documentation/asciidoc.gradle
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
apply plugin: 'org.xbib.gradle.plugin.asciidoctor'
|
||||||
|
|
||||||
|
asciidoctor {
|
||||||
|
attributes 'source-highlighter': 'coderay',
|
||||||
|
toc: 'left',
|
||||||
|
doctype: 'book',
|
||||||
|
icons: 'font',
|
||||||
|
encoding: 'utf-8',
|
||||||
|
sectlink: true,
|
||||||
|
sectanchors: true,
|
||||||
|
linkattrs: true,
|
||||||
|
imagesdir: 'img'
|
||||||
|
}
|
8
gradle/ide/idea.gradle
Normal file
8
gradle/ide/idea.gradle
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
apply plugin: 'idea'
|
||||||
|
|
||||||
|
idea {
|
||||||
|
module {
|
||||||
|
outputDir file('build/classes/java/main')
|
||||||
|
testOutputDir file('build/classes/java/test')
|
||||||
|
}
|
||||||
|
}
|
16
gradle/publish/forgejo.gradle
Normal file
16
gradle/publish/forgejo.gradle
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
if (project.hasProperty('forgeJoToken')) {
|
||||||
|
publishing {
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
url 'https://xbib.org/api/packages/joerg/maven'
|
||||||
|
credentials(HttpHeaderCredentials) {
|
||||||
|
name = "Authorization"
|
||||||
|
value = "token ${project.property('forgeJoToken')}"
|
||||||
|
}
|
||||||
|
authentication {
|
||||||
|
header(HttpHeaderAuthentication)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
gradle/publish/ivy.gradle
Normal file
27
gradle/publish/ivy.gradle
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
apply plugin: 'ivy-publish'
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
repositories {
|
||||||
|
ivy {
|
||||||
|
url = "https://xbib.org/repo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
publications {
|
||||||
|
ivy(IvyPublication) {
|
||||||
|
from components.java
|
||||||
|
descriptor {
|
||||||
|
license {
|
||||||
|
name = 'The Apache License, Version 2.0'
|
||||||
|
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||||
|
}
|
||||||
|
author {
|
||||||
|
name = 'Jörg Prante'
|
||||||
|
url = 'http://example.com/users/jane'
|
||||||
|
}
|
||||||
|
descriptor.description {
|
||||||
|
text = rootProject.ext.description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
gradle/publish/maven.gradle
Normal file
51
gradle/publish/maven.gradle
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
"${project.name}"(MavenPublication) {
|
||||||
|
from components.java
|
||||||
|
pom {
|
||||||
|
artifactId = project.name
|
||||||
|
name = project.name
|
||||||
|
description = rootProject.ext.description
|
||||||
|
url = rootProject.ext.url
|
||||||
|
inceptionYear = rootProject.ext.inceptionYear
|
||||||
|
packaging = 'jar'
|
||||||
|
organization {
|
||||||
|
name = 'xbib'
|
||||||
|
url = 'https://xbib.org'
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id = 'jprante'
|
||||||
|
name = 'Jörg Prante'
|
||||||
|
email = 'joergprante@gmail.com'
|
||||||
|
url = 'https://xbib.org/joerg'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
url = rootProject.ext.scmUrl
|
||||||
|
connection = rootProject.ext.scmConnection
|
||||||
|
developerConnection = rootProject.ext.scmDeveloperConnection
|
||||||
|
}
|
||||||
|
issueManagement {
|
||||||
|
system = rootProject.ext.issueManagementSystem
|
||||||
|
url = rootProject.ext.issueManagementUrl
|
||||||
|
}
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name = rootProject.ext.licenseName
|
||||||
|
url = rootProject.ext.licenseUrl
|
||||||
|
distribution = 'repo'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.hasProperty("signing.keyId")) {
|
||||||
|
apply plugin: 'signing'
|
||||||
|
signing {
|
||||||
|
sign publishing.publications."${project.name}"
|
||||||
|
}
|
||||||
|
}
|
12
gradle/publish/sonatype.gradle
Normal file
12
gradle/publish/sonatype.gradle
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
if (project.hasProperty('ossrhUsername') && project.hasProperty('ossrhPassword')) {
|
||||||
|
nexusPublishing {
|
||||||
|
repositories {
|
||||||
|
sonatype {
|
||||||
|
username = project.property('ossrhUsername')
|
||||||
|
password = project.property('ossrhPassword')
|
||||||
|
packageGroup = "org.xbib"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
gradle/quality/checkstyle.gradle
Normal file
19
gradle/quality/checkstyle.gradle
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
apply plugin: 'checkstyle'
|
||||||
|
|
||||||
|
tasks.withType(Checkstyle) {
|
||||||
|
ignoreFailures = true
|
||||||
|
reports {
|
||||||
|
xml.getRequired().set(true)
|
||||||
|
html.getRequired().set(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkstyle {
|
||||||
|
configFile = rootProject.file('gradle/quality/checkstyle.xml')
|
||||||
|
ignoreFailures = true
|
||||||
|
showViolations = true
|
||||||
|
checkstyleMain {
|
||||||
|
source = sourceSets.main.allSource
|
||||||
|
}
|
||||||
|
}
|
333
gradle/quality/checkstyle.xml
Normal file
333
gradle/quality/checkstyle.xml
Normal file
|
@ -0,0 +1,333 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE module PUBLIC
|
||||||
|
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||||
|
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
|
||||||
|
|
||||||
|
<!-- This is a checkstyle configuration file. For descriptions of
|
||||||
|
what the following rules do, please see the checkstyle configuration
|
||||||
|
page at http://checkstyle.sourceforge.net/config.html -->
|
||||||
|
|
||||||
|
<module name="Checker">
|
||||||
|
|
||||||
|
<module name="BeforeExecutionExclusionFileFilter">
|
||||||
|
<property name="fileNamePattern" value=".*(Example|Test|module-info)(\$.*)?"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="FileTabCharacter">
|
||||||
|
<!-- Checks that there are no tab characters in the file.
|
||||||
|
-->
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="NewlineAtEndOfFile">
|
||||||
|
<property name="lineSeparator" value="lf"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="RegexpSingleline">
|
||||||
|
<!-- Checks that FIXME is not used in comments. TODO is preferred.
|
||||||
|
-->
|
||||||
|
<property name="format" value="((//.*)|(\*.*))FIXME" />
|
||||||
|
<property name="message" value='TODO is preferred to FIXME. e.g. "TODO(johndoe): Refactor when v2 is released."' />
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="RegexpSingleline">
|
||||||
|
<!-- Checks that TODOs are named. (Actually, just that they are followed
|
||||||
|
by an open paren.)
|
||||||
|
-->
|
||||||
|
<property name="format" value="((//.*)|(\*.*))TODO[^(]" />
|
||||||
|
<property name="message" value='All TODOs should be named. e.g. "TODO(johndoe): Refactor when v2 is released."' />
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="JavadocPackage">
|
||||||
|
<!-- Checks that each Java package has a Javadoc file used for commenting.
|
||||||
|
Only allows a package-info.java, not package.html. -->
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<!-- All Java AST specific tests live under TreeWalker module. -->
|
||||||
|
<module name="TreeWalker">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
IMPORT CHECKS
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<module name="RedundantImport">
|
||||||
|
<!-- Checks for redundant import statements. -->
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="ImportOrder">
|
||||||
|
<!-- Checks for out of order import statements. -->
|
||||||
|
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
<!-- <property name="tokens" value="IMPORT, STATIC_IMPORT"/> -->
|
||||||
|
<property name="separated" value="false"/>
|
||||||
|
<property name="groups" value="*"/>
|
||||||
|
<!-- <property name="option" value="above"/> -->
|
||||||
|
<property name="sortStaticImportsAlphabetically" value="true"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="CustomImportOrder">
|
||||||
|
<!-- <property name="customImportOrderRules" value="THIRD_PARTY_PACKAGE###SPECIAL_IMPORTS###STANDARD_JAVA_PACKAGE###STATIC"/> -->
|
||||||
|
<!-- <property name="specialImportsRegExp" value="^javax\."/> -->
|
||||||
|
<!-- <property name="standardPackageRegExp" value="^java\."/> -->
|
||||||
|
<property name="sortImportsInGroupAlphabetically" value="true"/>
|
||||||
|
<property name="separateLineBetweenGroups" value="false"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
JAVADOC CHECKS
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Checks for Javadoc comments. -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
|
||||||
|
<module name="JavadocMethod">
|
||||||
|
<property name="accessModifiers" value="protected"/>
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
<property name="allowMissingParamTags" value="true"/>
|
||||||
|
<property name="allowMissingReturnTag" value="true"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="JavadocType">
|
||||||
|
<property name="scope" value="protected"/>
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="JavadocStyle">
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
NAMING CHECKS
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Item 38 - Adhere to generally accepted naming conventions -->
|
||||||
|
|
||||||
|
<module name="PackageName">
|
||||||
|
<!-- Validates identifiers for package names against the
|
||||||
|
supplied expression. -->
|
||||||
|
<!-- Here the default checkstyle rule restricts package name parts to
|
||||||
|
seven characters, this is not in line with common practice at Google.
|
||||||
|
-->
|
||||||
|
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]{1,})*$"/>
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="TypeNameCheck">
|
||||||
|
<!-- Validates static, final fields against the
|
||||||
|
expression "^[A-Z][a-zA-Z0-9]*$". -->
|
||||||
|
<metadata name="altname" value="TypeName"/>
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="ConstantNameCheck">
|
||||||
|
<!-- Validates non-private, static, final fields against the supplied
|
||||||
|
public/package final fields "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$". -->
|
||||||
|
<metadata name="altname" value="ConstantName"/>
|
||||||
|
<property name="applyToPublic" value="true"/>
|
||||||
|
<property name="applyToProtected" value="true"/>
|
||||||
|
<property name="applyToPackage" value="true"/>
|
||||||
|
<property name="applyToPrivate" value="false"/>
|
||||||
|
<property name="format" value="^([A-Z][A-Z0-9]*(_[A-Z0-9]+)*|FLAG_.*)$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Variable ''{0}'' should be in ALL_CAPS (if it is a constant) or be private (otherwise)."/>
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="StaticVariableNameCheck">
|
||||||
|
<!-- Validates static, non-final fields against the supplied
|
||||||
|
expression "^[a-z][a-zA-Z0-9]*_?$". -->
|
||||||
|
<metadata name="altname" value="StaticVariableName"/>
|
||||||
|
<property name="applyToPublic" value="true"/>
|
||||||
|
<property name="applyToProtected" value="true"/>
|
||||||
|
<property name="applyToPackage" value="true"/>
|
||||||
|
<property name="applyToPrivate" value="true"/>
|
||||||
|
<property name="format" value="^[a-z][a-zA-Z0-9]*_?$"/>
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="MemberNameCheck">
|
||||||
|
<!-- Validates non-static members against the supplied expression. -->
|
||||||
|
<metadata name="altname" value="MemberName"/>
|
||||||
|
<property name="applyToPublic" value="true"/>
|
||||||
|
<property name="applyToProtected" value="true"/>
|
||||||
|
<property name="applyToPackage" value="true"/>
|
||||||
|
<property name="applyToPrivate" value="true"/>
|
||||||
|
<property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="MethodNameCheck">
|
||||||
|
<!-- Validates identifiers for method names. -->
|
||||||
|
<metadata name="altname" value="MethodName"/>
|
||||||
|
<property name="format" value="^[a-z][a-zA-Z0-9]*(_[a-zA-Z0-9]+)*$"/>
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="ParameterName">
|
||||||
|
<!-- Validates identifiers for method parameters against the
|
||||||
|
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="LocalFinalVariableName">
|
||||||
|
<!-- Validates identifiers for local final variables against the
|
||||||
|
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="LocalVariableName">
|
||||||
|
<!-- Validates identifiers for local variables against the
|
||||||
|
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
LENGTH and CODING CHECKS
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
<module name="LeftCurly">
|
||||||
|
<!-- Checks for placement of the left curly brace ('{'). -->
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="RightCurly">
|
||||||
|
<!-- Checks right curlies on CATCH, ELSE, and TRY blocks are on
|
||||||
|
the same line. e.g., the following example is fine:
|
||||||
|
<pre>
|
||||||
|
if {
|
||||||
|
...
|
||||||
|
} else
|
||||||
|
</pre>
|
||||||
|
-->
|
||||||
|
<!-- This next example is not fine:
|
||||||
|
<pre>
|
||||||
|
if {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
else
|
||||||
|
</pre>
|
||||||
|
-->
|
||||||
|
<property name="option" value="same"/>
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<!-- Checks for braces around if and else blocks -->
|
||||||
|
<module name="NeedBraces">
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
<property name="tokens" value="LITERAL_IF, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="UpperEll">
|
||||||
|
<!-- Checks that long constants are defined with an upper ell.-->
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="FallThrough">
|
||||||
|
<!-- Warn about falling through to the next case statement. Similar to
|
||||||
|
javac -Xlint:fallthrough, but the check is suppressed if a single-line comment
|
||||||
|
on the last non-blank line preceding the fallen-into case contains 'fall through' (or
|
||||||
|
some other variants which we don't publicized to promote consistency).
|
||||||
|
-->
|
||||||
|
<property name="reliefPattern"
|
||||||
|
value="fall through|Fall through|fallthru|Fallthru|falls through|Falls through|fallthrough|Fallthrough|No break|NO break|no break|continue on"/>
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
MODIFIERS CHECKS
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<module name="ModifierOrder">
|
||||||
|
<!-- Warn if modifier order is inconsistent with JLS3 8.1.1, 8.3.1, and
|
||||||
|
8.4.3. The prescribed order is:
|
||||||
|
public, protected, private, abstract, static, final, transient, volatile,
|
||||||
|
synchronized, native, strictfp
|
||||||
|
-->
|
||||||
|
</module>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
WHITESPACE CHECKS
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<module name="WhitespaceAround">
|
||||||
|
<!-- Checks that various tokens are surrounded by whitespace.
|
||||||
|
This includes most binary operators and keywords followed
|
||||||
|
by regular or curly braces.
|
||||||
|
-->
|
||||||
|
<property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR,
|
||||||
|
BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN,
|
||||||
|
EQUAL, GE, GT, LAND, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE,
|
||||||
|
LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN,
|
||||||
|
LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS,
|
||||||
|
MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION,
|
||||||
|
SL, SL_ASSIGN, SR_ASSIGN, STAR, STAR_ASSIGN"/>
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="WhitespaceAfter">
|
||||||
|
<!-- Checks that commas, semicolons and typecasts are followed by
|
||||||
|
whitespace.
|
||||||
|
-->
|
||||||
|
<property name="tokens" value="COMMA, SEMI, TYPECAST"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="NoWhitespaceAfter">
|
||||||
|
<!-- Checks that there is no whitespace after various unary operators.
|
||||||
|
Linebreaks are allowed.
|
||||||
|
-->
|
||||||
|
<property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS,
|
||||||
|
UNARY_PLUS"/>
|
||||||
|
<property name="allowLineBreaks" value="true"/>
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="NoWhitespaceBefore">
|
||||||
|
<!-- Checks that there is no whitespace before various unary operators.
|
||||||
|
Linebreaks are allowed.
|
||||||
|
-->
|
||||||
|
<property name="tokens" value="SEMI, DOT, POST_DEC, POST_INC"/>
|
||||||
|
<property name="allowLineBreaks" value="true"/>
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="ParenPad">
|
||||||
|
<!-- Checks that there is no whitespace before close parens or after
|
||||||
|
open parens.
|
||||||
|
-->
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="LineLength">
|
||||||
|
<!-- Checks if a line is too long. -->
|
||||||
|
<property name="max" value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.max}" default="128"/>
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
The default ignore pattern exempts the following elements:
|
||||||
|
- import statements
|
||||||
|
- long URLs inside comments
|
||||||
|
-->
|
||||||
|
|
||||||
|
<property name="ignorePattern"
|
||||||
|
value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.ignorePattern}"
|
||||||
|
default="^(package .*;\s*)|(import .*;\s*)|( *(\*|//).*https?://.*)$"/>
|
||||||
|
</module>
|
||||||
|
</module>
|
||||||
|
|
11
gradle/quality/cyclonedx.gradle
Normal file
11
gradle/quality/cyclonedx.gradle
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
cyclonedxBom {
|
||||||
|
includeConfigs = [ 'runtimeClasspath' ]
|
||||||
|
skipConfigs = [ 'compileClasspath', 'testCompileClasspath' ]
|
||||||
|
projectType = "library"
|
||||||
|
schemaVersion = "1.4"
|
||||||
|
destination = file("build/reports")
|
||||||
|
outputName = "bom"
|
||||||
|
outputFormat = "json"
|
||||||
|
includeBomSerialNumber = true
|
||||||
|
componentVersion = "2.0.0"
|
||||||
|
}
|
17
gradle/quality/pmd.gradle
Normal file
17
gradle/quality/pmd.gradle
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
apply plugin: 'pmd'
|
||||||
|
|
||||||
|
tasks.withType(Pmd) {
|
||||||
|
ignoreFailures = true
|
||||||
|
reports {
|
||||||
|
xml.getRequired().set(true)
|
||||||
|
html.getRequired().set(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pmd {
|
||||||
|
ignoreFailures = true
|
||||||
|
consoleOutput = false
|
||||||
|
toolVersion = "6.51.0"
|
||||||
|
ruleSetFiles = rootProject.files('gradle/quality/pmd/category/java/bestpractices.xml')
|
||||||
|
}
|
10
gradle/quality/sonarqube.gradle
Normal file
10
gradle/quality/sonarqube.gradle
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
sonarqube {
|
||||||
|
properties {
|
||||||
|
property "sonar.projectName", "${project.group} ${project.name}"
|
||||||
|
property "sonar.sourceEncoding", "UTF-8"
|
||||||
|
property "sonar.tests", "src/test/java"
|
||||||
|
property "sonar.scm.provider", "git"
|
||||||
|
property "sonar.junit.reportsPath", "build/test-results/test/"
|
||||||
|
}
|
||||||
|
}
|
15
gradle/quality/spotbugs.gradle
Normal file
15
gradle/quality/spotbugs.gradle
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
apply plugin: 'com.github.spotbugs'
|
||||||
|
|
||||||
|
spotbugs {
|
||||||
|
effort = "max"
|
||||||
|
reportLevel = "low"
|
||||||
|
ignoreFailures = true
|
||||||
|
}
|
||||||
|
|
||||||
|
spotbugsMain {
|
||||||
|
reports {
|
||||||
|
xml.getRequired().set(false)
|
||||||
|
html.getRequired().set(true)
|
||||||
|
}
|
||||||
|
}
|
4
gradle/repositories/maven.gradle
Normal file
4
gradle/repositories/maven.gradle
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
22
gradle/test/jmh.gradle
Normal file
22
gradle/test/jmh.gradle
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
sourceSets {
|
||||||
|
jmh {
|
||||||
|
java.srcDirs = ['src/jmh/java']
|
||||||
|
resources.srcDirs = ['src/jmh/resources']
|
||||||
|
compileClasspath += sourceSets.main.runtimeClasspath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
jmhImplementation 'org.openjdk.jmh:jmh-core:1.34'
|
||||||
|
jmhAnnotationProcessor 'org.openjdk.jmh:jmh-generator-annprocess:1.34'
|
||||||
|
}
|
||||||
|
|
||||||
|
task jmh(type: JavaExec, group: 'jmh', dependsOn: jmhClasses) {
|
||||||
|
mainClass.set('org.openjdk.jmh.Main')
|
||||||
|
classpath = sourceSets.jmh.compileClasspath + sourceSets.jmh.runtimeClasspath
|
||||||
|
project.file('build/reports/jmh').mkdirs()
|
||||||
|
args '-rf', 'json'
|
||||||
|
args '-rff', project.file('build/reports/jmh/result.json')
|
||||||
|
}
|
||||||
|
|
||||||
|
classes.finalizedBy(jmhClasses)
|
35
gradle/test/junit5.gradle
Normal file
35
gradle/test/junit5.gradle
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
dependencies {
|
||||||
|
testImplementation testLibs.junit.jupiter.api
|
||||||
|
testImplementation testLibs.hamcrest
|
||||||
|
testRuntimeOnly testLibs.junit.jupiter.engine
|
||||||
|
testRuntimeOnly testLibs.junit.jupiter.platform.launcher
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
failFast = false
|
||||||
|
jvmArgs '--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED',
|
||||||
|
'--add-exports=java.base/jdk.internal.ref=ALL-UNNAMED',
|
||||||
|
'--add-exports=java.base/sun.nio.ch=ALL-UNNAMED',
|
||||||
|
'--add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED',
|
||||||
|
'--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED',
|
||||||
|
'--add-opens=jdk.compiler/com.sun.tools.javac=ALL-UNNAMED',
|
||||||
|
'--add-opens=java.base/java.lang=ALL-UNNAMED',
|
||||||
|
'--add-opens=java.base/java.lang.reflect=ALL-UNNAMED',
|
||||||
|
'--add-opens=java.base/java.io=ALL-UNNAMED',
|
||||||
|
'--add-opens=java.base/java.nio=ALL-UNNAMED',
|
||||||
|
'--add-opens=java.base/java.util=ALL-UNNAMED'
|
||||||
|
systemProperty 'java.util.logging.config.file', 'src/test/resources/logging.properties'
|
||||||
|
testLogging {
|
||||||
|
events 'STARTED', 'PASSED', 'FAILED', 'SKIPPED'
|
||||||
|
}
|
||||||
|
afterSuite { desc, result ->
|
||||||
|
if (!desc.parent) {
|
||||||
|
println "\nTest result: ${result.resultType}"
|
||||||
|
println "Test summary: ${result.testCount} tests, " +
|
||||||
|
"${result.successfulTestCount} succeeded, " +
|
||||||
|
"${result.failedTestCount} failed, " +
|
||||||
|
"${result.skippedTestCount} skipped"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
249
gradlew
vendored
Executable file
249
gradlew
vendored
Executable file
|
@ -0,0 +1,249 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
92
gradlew.bat
vendored
Normal file
92
gradlew.bat
vendored
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%"=="" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
5
j2html-codegen/build.gradle
Normal file
5
j2html-codegen/build.gradle
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
dependencies {
|
||||||
|
implementation libs.jsoup
|
||||||
|
implementation libs.javapoet
|
||||||
|
testImplementation testLibs.mockito.core
|
||||||
|
}
|
|
@ -0,0 +1,172 @@
|
||||||
|
package org.xbib.j2html.codegen;
|
||||||
|
|
||||||
|
import com.squareup.javapoet.ClassName;
|
||||||
|
import com.squareup.javapoet.JavaFile;
|
||||||
|
import com.squareup.javapoet.MethodSpec;
|
||||||
|
import com.squareup.javapoet.ParameterizedTypeName;
|
||||||
|
import com.squareup.javapoet.TypeName;
|
||||||
|
import com.squareup.javapoet.TypeSpec;
|
||||||
|
import com.squareup.javapoet.TypeVariableName;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.lang.model.element.Modifier;
|
||||||
|
import org.xbib.j2html.codegen.Model.Node;
|
||||||
|
import static org.xbib.j2html.codegen.Model.Metadata.ON_OFF;
|
||||||
|
import static org.xbib.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<>();
|
||||||
|
for (Node element : model.elements()) {
|
||||||
|
ClassName className = ClassName.get(pkg, capitalize(element.name) + "Tag");
|
||||||
|
TypeSpec.Builder type = defineElementClass(element, className);
|
||||||
|
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<>();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
179
j2html-codegen/src/main/java/org/xbib/j2html/codegen/Model.java
Normal file
179
j2html-codegen/src/main/java/org/xbib/j2html/codegen/Model.java
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
package org.xbib.j2html.codegen;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import static org.xbib.j2html.codegen.Model.Metadata.ON_OFF;
|
||||||
|
import static org.xbib.j2html.codegen.Model.Metadata.SELF_CLOSING;
|
||||||
|
import static org.xbib.j2html.codegen.Model.Node.Type.BOOLEAN;
|
||||||
|
import static org.xbib.j2html.codegen.Model.Node.Type.ELEMENT;
|
||||||
|
import static org.xbib.j2html.codegen.Model.Node.Type.STRING;
|
||||||
|
|
||||||
|
public class Model implements Parser.Listener {
|
||||||
|
|
||||||
|
private final Map<String, Node> elements;
|
||||||
|
private final 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
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public static class InvalidModel extends RuntimeException {
|
||||||
|
public InvalidModel(Exception cause, int line) {
|
||||||
|
super(cause.getMessage() + ". At line " + line, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public static class NodeAlreadyExists extends RuntimeException {
|
||||||
|
public NodeAlreadyExists(String name) {
|
||||||
|
super("Node already exists: " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public static class NodeDoesNotExist extends RuntimeException {
|
||||||
|
public NodeDoesNotExist(String name) {
|
||||||
|
super("Node does not exist: " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package org.xbib.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,127 @@
|
||||||
|
package org.xbib.j2html.codegen.generators;
|
||||||
|
|
||||||
|
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 org.xbib.j2html.codegen.model.AttrD;
|
||||||
|
import org.xbib.j2html.codegen.model.AttributesList;
|
||||||
|
|
||||||
|
public final class AttributeInterfaceCodeGenerator {
|
||||||
|
|
||||||
|
private static final String relPath = "tags/attributes/";
|
||||||
|
|
||||||
|
public static void generate(final Path absPath,
|
||||||
|
final boolean delete) throws IOException {
|
||||||
|
for (final AttrD attr : AttributesList.attributesDescriptive()) {
|
||||||
|
final Path path = makePath(attr.attr, absPath);
|
||||||
|
final String interfaceName = interfaceNameFromAttribute(attr.attr) + "<T extends Tag<T>>";
|
||||||
|
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) {
|
||||||
|
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");
|
||||||
|
final String attrName = interfaceNameSimple.toLowerCase();
|
||||||
|
final String paramName = attrName + "_";
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
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 {
|
||||||
|
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) {
|
||||||
|
sb.append("(final String ").append(paramName).append(") {")
|
||||||
|
.append("return self().attr(\"").append(attrName).append("\", ").append(paramName).append(");\n");
|
||||||
|
} else {
|
||||||
|
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,109 @@
|
||||||
|
package org.xbib.j2html.codegen.generators;
|
||||||
|
|
||||||
|
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 org.xbib.j2html.codegen.model.AttributesList;
|
||||||
|
|
||||||
|
public final class SpecializedTagClassCodeGenerator {
|
||||||
|
|
||||||
|
private static final String relPath = "tags/specialized";
|
||||||
|
|
||||||
|
public static void generate(final Path absPath, final boolean delete) throws IOException {
|
||||||
|
for (final String tag : TagCreatorCodeGenerator.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
|
||||||
|
);
|
||||||
|
if (!delete) {
|
||||||
|
Files.write(path, classString.getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (final String tag : TagCreatorCodeGenerator.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)) {
|
||||||
|
Files.delete(path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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(" "));
|
||||||
|
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");
|
||||||
|
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,130 @@
|
||||||
|
package org.xbib.j2html.codegen.generators;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class TagCreatorCodeGenerator {
|
||||||
|
|
||||||
|
public static List<String> emptyTags() {
|
||||||
|
return Arrays.asList(
|
||||||
|
"area",
|
||||||
|
"base",
|
||||||
|
"br",
|
||||||
|
"col",
|
||||||
|
"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,22 @@
|
||||||
|
package org.xbib.j2html.codegen.model;
|
||||||
|
|
||||||
|
public final class AttrD {
|
||||||
|
|
||||||
|
public final String attr;
|
||||||
|
|
||||||
|
public final boolean hasArgument;
|
||||||
|
|
||||||
|
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,142 @@
|
||||||
|
package org.xbib.j2html.codegen.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class AttributesList {
|
||||||
|
|
||||||
|
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("action", true, "form"),
|
||||||
|
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"),
|
||||||
|
new AttrD("charset", true, "meta", "script"),
|
||||||
|
new AttrD("checked", false, "input"),
|
||||||
|
new AttrD("cite", true, "blockquote", "del", "ins", "q"),
|
||||||
|
new AttrD("cols", true, "textarea"),
|
||||||
|
new AttrD("colspan", true, "td", "th"),
|
||||||
|
new AttrD("content", true, "meta"),
|
||||||
|
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("dirname", true, "input", "textarea"),
|
||||||
|
new AttrD("disabled", false, "button", "fieldset", "input", "optgroup", "option", "select", "textarea"),
|
||||||
|
new AttrD("download", false, "a", "area"),
|
||||||
|
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("high", true, "meter"),
|
||||||
|
new AttrD("href", true, "a", "area", "base", "link"),
|
||||||
|
new AttrD("hreflang", true, "a", "area", "link"),
|
||||||
|
new AttrD("ismap", false, "img"),
|
||||||
|
new AttrD("kind", true, "track"),
|
||||||
|
new AttrD("label", true, "track", "option", "optgroup"),
|
||||||
|
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("oncanplay", true, "audio", "embed", "object", "video"),
|
||||||
|
new AttrD("oncanplaythrough", true, "audio", "video"),
|
||||||
|
new AttrD("oncuechange", true, "track"),
|
||||||
|
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("onhashchange", true, "body"),
|
||||||
|
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"),
|
||||||
|
new AttrD("onoffline", true, "body"),
|
||||||
|
new AttrD("ononline", true, "body"),
|
||||||
|
new AttrD("onpagehide", true, "body"),
|
||||||
|
new AttrD("onpageshow", true, "body"),
|
||||||
|
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("onsearch", true, "input"),
|
||||||
|
new AttrD("onseeked", true, "audio", "video"),
|
||||||
|
new AttrD("onseeking", true, "audio", "video"),
|
||||||
|
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("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("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("target", true, "a", "area", "base", "form"),
|
||||||
|
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,73 @@
|
||||||
|
package org.xbib.j2html.codegen;
|
||||||
|
|
||||||
|
import org.xbib.j2html.codegen.wattsi.WattsiSource;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.xbib.j2html.codegen.generators.TagCreatorCodeGenerator;
|
||||||
|
import org.xbib.j2html.codegen.wattsi.ElementDefinition;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
|
||||||
|
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.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
public class CodeGeneratorComplianceTests {
|
||||||
|
|
||||||
|
private static WattsiSource specification;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public static 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
|
||||||
|
@Disabled
|
||||||
|
// 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(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(emptyList(), invalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package org.xbib.j2html.codegen;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class GenerateTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generate() throws IOException {
|
||||||
|
try (InputStream inputStream = getClass().getResourceAsStream("/html.model")) {
|
||||||
|
String definitions = new String(inputStream.readAllBytes());
|
||||||
|
Model model = new Model();
|
||||||
|
Parser.parse(definitions, model);
|
||||||
|
Path dir = Paths.get("build/generated/sources/j2html");
|
||||||
|
Files.createDirectories(dir);
|
||||||
|
Generator.generate(dir, "j2html.tags.attributes", "j2html.tags.specialized", model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
package org.xbib.j2html.codegen;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.xbib.j2html.codegen.Parser;
|
||||||
|
|
||||||
|
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("", Mockito::verifyNoInteractions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whitespace_has_no_events() {
|
||||||
|
verifyParsing(" \t\t\t\t", Mockito::verifyNoInteractions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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...");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.xbib.j2html.codegen.wattsi;
|
||||||
|
|
||||||
|
public interface AttributeDefinition {
|
||||||
|
|
||||||
|
String name();
|
||||||
|
|
||||||
|
boolean appliesTo(ElementDefinition element);
|
||||||
|
|
||||||
|
boolean isObsolete();
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package org.xbib.j2html.codegen.wattsi;
|
||||||
|
|
||||||
|
public interface ElementDefinition {
|
||||||
|
|
||||||
|
String name();
|
||||||
|
|
||||||
|
boolean isSelfClosing();
|
||||||
|
|
||||||
|
boolean isObsolete();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package org.xbib.j2html.codegen.wattsi;
|
||||||
|
|
||||||
|
import com.squareup.javapoet.ClassName;
|
||||||
|
import com.squareup.javapoet.MethodSpec;
|
||||||
|
import com.squareup.javapoet.TypeSpec;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.lang.model.element.Modifier;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String methodName(String prefix, String... words) {
|
||||||
|
String[] tmp = new String[words.length + 1];
|
||||||
|
tmp[0] = prefix;
|
||||||
|
System.arraycopy(words, 0, tmp, 1, words.length);
|
||||||
|
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,190 @@
|
||||||
|
package org.xbib.j2html.codegen.wattsi;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
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 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);
|
||||||
|
Elements obsoleteAttributes = doc.select("p:contains(The following attributes are obsolete) + dl");
|
||||||
|
obsoleteAttributes.select("dt > dfn[element-attr]").stream()
|
||||||
|
.map(WattsiAttribute::new)
|
||||||
|
.map(WattsiAttribute::reference)
|
||||||
|
.forEach(obsolete::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
128553
j2html-codegen/src/test/resources/2022-01.wattsi
Normal file
128553
j2html-codegen/src/test/resources/2022-01.wattsi
Normal file
File diff suppressed because it is too large
Load diff
622
j2html-codegen/src/test/resources/html.model
Normal file
622
j2html-codegen/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]
|
5
j2html/build.gradle
Normal file
5
j2html/build.gradle
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation testLibs.velocity
|
||||||
|
testImplementation testLibs.junit.benchmarks
|
||||||
|
}
|
9
j2html/src/main/java/module-info.java
Normal file
9
j2html/src/main/java/module-info.java
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
module org.xbib.j2html {
|
||||||
|
exports org.xbib.j2html;
|
||||||
|
exports org.xbib.j2html.attributes;
|
||||||
|
exports org.xbib.j2html.rendering;
|
||||||
|
exports org.xbib.j2html.tags;
|
||||||
|
exports org.xbib.j2html.tags.attributes;
|
||||||
|
exports org.xbib.j2html.tags.specialized;
|
||||||
|
exports org.xbib.j2html.utils;
|
||||||
|
}
|
152
j2html/src/main/java/org/xbib/j2html/Config.java
Normal file
152
j2html/src/main/java/org/xbib/j2html/Config.java
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
package org.xbib.j2html;
|
||||||
|
|
||||||
|
import org.xbib.j2html.utils.CSSMin;
|
||||||
|
import org.xbib.j2html.utils.EscapeUtil;
|
||||||
|
import org.xbib.j2html.utils.Indenter;
|
||||||
|
import org.xbib.j2html.utils.JSMin;
|
||||||
|
import org.xbib.j2html.utils.Minifier;
|
||||||
|
import org.xbib.j2html.utils.TextEscaper;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
public class Config {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change this to configure text-escaping
|
||||||
|
* For example, to disable escaping, do <code>{@code Config.textEscaper = text -> text;}</code>
|
||||||
|
*/
|
||||||
|
public static TextEscaper textEscaper = EscapeUtil::escape;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change this to configure css-minification.
|
||||||
|
* The default minifier is <a href="https://github.com/barryvan/CSSMin">CSSMin</a>
|
||||||
|
*/
|
||||||
|
public static Minifier cssMinifier = CSSMin::compressCss;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change this to configure js-minification.
|
||||||
|
* The default minifier is a simple whitespace/newline stripper
|
||||||
|
*/
|
||||||
|
public static Minifier jsMinifier = JSMin::compressJs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change this to configure enable/disable closing empty tags
|
||||||
|
* The default is to NOT close them
|
||||||
|
*/
|
||||||
|
public static boolean closeEmptyTags = false;
|
||||||
|
|
||||||
|
private static final String FOUR_SPACES = " ";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change this to configure indentation when rendering formatted html
|
||||||
|
* The default is four spaces
|
||||||
|
*/
|
||||||
|
public static Indenter indenter = (level, text) -> String.join("", Collections.nCopies(level, FOUR_SPACES)) + text;
|
||||||
|
|
||||||
|
private TextEscaper _textEscaper;
|
||||||
|
private Minifier _cssMinifier;
|
||||||
|
private Minifier _jsMinifier;
|
||||||
|
private boolean _closeEmptyTags;
|
||||||
|
private Indenter _indenter;
|
||||||
|
|
||||||
|
|
||||||
|
private Config(
|
||||||
|
TextEscaper _textEscaper,
|
||||||
|
Minifier _cssMinifier,
|
||||||
|
Minifier _jsMinifier,
|
||||||
|
boolean _closeEmptyTags,
|
||||||
|
Indenter _indenter
|
||||||
|
) {
|
||||||
|
this._textEscaper = _textEscaper;
|
||||||
|
this._cssMinifier = _cssMinifier;
|
||||||
|
this._jsMinifier = _jsMinifier;
|
||||||
|
this._closeEmptyTags = _closeEmptyTags;
|
||||||
|
this._indenter = _indenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A copy constructor.
|
||||||
|
*
|
||||||
|
* @param original The Config to copy fields from.
|
||||||
|
*/
|
||||||
|
private Config(Config original) {
|
||||||
|
this._textEscaper = original._textEscaper;
|
||||||
|
this._cssMinifier = original._cssMinifier;
|
||||||
|
this._jsMinifier = original._jsMinifier;
|
||||||
|
this._closeEmptyTags = original._closeEmptyTags;
|
||||||
|
this._indenter = original._indenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextEscaper textEscaper() {
|
||||||
|
return _textEscaper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Minifier cssMinifier() {
|
||||||
|
return _cssMinifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Minifier jsMinifier() {
|
||||||
|
return _jsMinifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean closeEmptyTags() {
|
||||||
|
return _closeEmptyTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Indenter indenter() {
|
||||||
|
return _indenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config withTextEscaper(TextEscaper textEscaper){
|
||||||
|
Config copy = new Config(this);
|
||||||
|
copy._textEscaper = textEscaper;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config withCssMinifier(Minifier cssMinifier){
|
||||||
|
Config copy = new Config(this);
|
||||||
|
copy._cssMinifier = cssMinifier;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config withJsMinifier(Minifier jsMinifier){
|
||||||
|
Config copy = new Config(this);
|
||||||
|
copy._jsMinifier = jsMinifier;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config withEmptyTagsClosed(boolean closeEmptyTags){
|
||||||
|
Config copy = new Config(this);
|
||||||
|
copy._closeEmptyTags = closeEmptyTags;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config withIndenter(Indenter indenter){
|
||||||
|
Config copy = new Config(this);
|
||||||
|
copy._indenter = indenter;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Config DEFAULTS = new Config(
|
||||||
|
EscapeUtil::escape,
|
||||||
|
CSSMin::compressCss,
|
||||||
|
JSMin::compressJs,
|
||||||
|
false,
|
||||||
|
(level, text) -> String.join("", Collections.nCopies(level, FOUR_SPACES)) + text
|
||||||
|
);
|
||||||
|
|
||||||
|
public static Config defaults() {
|
||||||
|
return DEFAULTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Config global() {
|
||||||
|
return new Config(
|
||||||
|
textEscaper,
|
||||||
|
cssMinifier,
|
||||||
|
jsMinifier,
|
||||||
|
closeEmptyTags,
|
||||||
|
indenter
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
1017
j2html/src/main/java/org/xbib/j2html/TagCreator.java
Normal file
1017
j2html/src/main/java/org/xbib/j2html/TagCreator.java
Normal file
File diff suppressed because it is too large
Load diff
182
j2html/src/main/java/org/xbib/j2html/attributes/Attr.java
Normal file
182
j2html/src/main/java/org/xbib/j2html/attributes/Attr.java
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
package org.xbib.j2html.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public abstract class Attr {
|
||||||
|
|
||||||
|
public static final String ACCEPT = "accept";
|
||||||
|
public static final String ACCEPT_CHARSET = "accept-charset";
|
||||||
|
public static final String ACCESSKEY = "accesskey";
|
||||||
|
public static final String ACTION = "action";
|
||||||
|
public static final String ALIGN = "align";
|
||||||
|
public static final String ALT = "alt";
|
||||||
|
public static final String ASYNC = "async";
|
||||||
|
public static final String AUTOCOMPLETE = "autocomplete";
|
||||||
|
public static final String AUTOFOCUS = "autofocus";
|
||||||
|
public static final String AUTOPLAY = "autoplay";
|
||||||
|
public static final String AUTOSAVE = "autosave";
|
||||||
|
public static final String BORDER = "border";
|
||||||
|
public static final String BUFFERED = "buffered";
|
||||||
|
public static final String CHALLENGE = "challenge";
|
||||||
|
public static final String CHARSET = "charset";
|
||||||
|
public static final String CHECKED = "checked";
|
||||||
|
public static final String CITE = "cite";
|
||||||
|
public static final String CLASS = "class";
|
||||||
|
public static final String COLOR = "color";
|
||||||
|
public static final String COLS = "cols";
|
||||||
|
public static final String COLSPAN = "colspan";
|
||||||
|
public static final String CONTENT = "content";
|
||||||
|
public static final String CONTENTEDITABLE = "contenteditable";
|
||||||
|
public static final String CONTEXTMENU = "contextmenu";
|
||||||
|
public static final String CONTROLS = "controls";
|
||||||
|
public static final String COORDS = "coords";
|
||||||
|
public static final String DATA = "data";
|
||||||
|
public static final String DATETIME = "datetime";
|
||||||
|
public static final String DEFAULT = "default";
|
||||||
|
public static final String DEFER = "defer";
|
||||||
|
public static final String DIR = "dir";
|
||||||
|
public static final String DIRNAME = "dirname";
|
||||||
|
public static final String DISABLED = "disabled";
|
||||||
|
public static final String DOWNLOAD = "download";
|
||||||
|
public static final String DRAGGABLE = "draggable";
|
||||||
|
public static final String DROPZONE = "dropzone";
|
||||||
|
public static final String ENCTYPE = "enctype";
|
||||||
|
public static final String FOR = "for";
|
||||||
|
public static final String FORM = "form";
|
||||||
|
public static final String FORMACTION = "formaction";
|
||||||
|
public static final String HEADERS = "headers";
|
||||||
|
public static final String HEIGHT = "height";
|
||||||
|
public static final String HIDDEN = "hidden";
|
||||||
|
public static final String HIGH = "high";
|
||||||
|
public static final String HREF = "href";
|
||||||
|
public static final String HREFLANG = "hreflang";
|
||||||
|
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";
|
||||||
|
public static final String KIND = "kind";
|
||||||
|
public static final String LABEL = "label";
|
||||||
|
public static final String LANG = "lang";
|
||||||
|
public static final String LANGUAGE = "language";
|
||||||
|
public static final String LIST = "list";
|
||||||
|
public static final String LOOP = "loop";
|
||||||
|
public static final String LOW = "low";
|
||||||
|
public static final String MANIFEST = "manifest";
|
||||||
|
public static final String MAX = "max";
|
||||||
|
public static final String MAXLENGTH = "maxlength";
|
||||||
|
public static final String MEDIA = "media";
|
||||||
|
public static final String METHOD = "method";
|
||||||
|
public static final String MIN = "min";
|
||||||
|
public static final String MULTIPLE = "multiple";
|
||||||
|
public static final String NAME = "name";
|
||||||
|
public static final String NOVALIDATE = "novalidate";
|
||||||
|
public static final String OPEN = "open";
|
||||||
|
public static final String OPTIMUM = "optimum";
|
||||||
|
public static final String PATTERN = "pattern";
|
||||||
|
public static final String PING = "ping";
|
||||||
|
public static final String PLACEHOLDER = "placeholder";
|
||||||
|
public static final String POSTER = "poster";
|
||||||
|
public static final String PRELOAD = "preload";
|
||||||
|
public static final String PUBDATE = "pubdate";
|
||||||
|
public static final String RADIOGROUP = "radiogroup";
|
||||||
|
public static final String READONLY = "readonly";
|
||||||
|
public static final String REL = "rel";
|
||||||
|
public static final String REQUIRED = "required";
|
||||||
|
public static final String REVERSED = "reversed";
|
||||||
|
public static final String ROLE = "role";
|
||||||
|
public static final String ROWS = "rows";
|
||||||
|
public static final String ROWSPAN = "rowspan";
|
||||||
|
public static final String SANDBOX = "sandbox";
|
||||||
|
public static final String SCOPE = "scope";
|
||||||
|
public static final String SCOPED = "scoped";
|
||||||
|
public static final String SEAMLESS = "seamless";
|
||||||
|
public static final String SELECTED = "selected";
|
||||||
|
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";
|
||||||
|
public static final String SRCDOC = "srcdoc";
|
||||||
|
public static final String SRCLANG = "srclang";
|
||||||
|
public static final String SRCSET = "srcset";
|
||||||
|
public static final String START = "start";
|
||||||
|
public static final String STEP = "step";
|
||||||
|
public static final String STYLE = "style";
|
||||||
|
public static final String SUMMARY = "summary";
|
||||||
|
public static final String TABINDEX = "tabindex";
|
||||||
|
public static final String TARGET = "target";
|
||||||
|
public static final String TITLE = "title";
|
||||||
|
public static final String TYPE = "type";
|
||||||
|
public static final String USEMAP = "usemap";
|
||||||
|
public static final String VALUE = "value";
|
||||||
|
public static final String WIDTH = "width";
|
||||||
|
public static final String WRAP = "wrap";
|
||||||
|
public static final String TRANSLATE = "translate";
|
||||||
|
|
||||||
|
public static ShortForm shortFormFromAttrsString(String attrs) {
|
||||||
|
if (!attrs.contains(".") && !attrs.contains(("#"))) {
|
||||||
|
throw new IllegalArgumentException("String must contain either id (#) or class (.)");
|
||||||
|
}
|
||||||
|
if (attrs.split("#").length > 2) {
|
||||||
|
throw new IllegalArgumentException("Only one id (#) allowed");
|
||||||
|
}
|
||||||
|
String id = "";
|
||||||
|
StringBuilder classes = new StringBuilder();
|
||||||
|
for (String attr : attrs.split("\\.")) {
|
||||||
|
if (attr.contains("#")) {
|
||||||
|
if (!attr.startsWith("#")) {
|
||||||
|
throw new IllegalArgumentException("# cannot be in the middle of string");
|
||||||
|
}
|
||||||
|
id = attr.replace("#", "");
|
||||||
|
} else {
|
||||||
|
classes.append(attr).append(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ShortForm(id.trim(), classes.toString().trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Tag<T>> T addTo(T tag, Attr attr) {
|
||||||
|
if (null != attr)
|
||||||
|
attr.addTo(tag);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract <T extends Tag<T>> T addTo(T tag);
|
||||||
|
|
||||||
|
public static class ShortForm extends Attr {
|
||||||
|
String id;
|
||||||
|
String classes;
|
||||||
|
|
||||||
|
private ShortForm(String id, String classes) {
|
||||||
|
this.id = id;
|
||||||
|
this.classes = classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasId() {
|
||||||
|
return id != null && !"".equals(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasClasses() {
|
||||||
|
return classes != null && !"".equals(classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends Tag<T>> T addTo(T tag) {
|
||||||
|
if (hasId() && hasClasses()) {
|
||||||
|
return tag.withId(id).withClass(classes);
|
||||||
|
}
|
||||||
|
if (hasId()) {
|
||||||
|
return tag.withId(id);
|
||||||
|
}
|
||||||
|
if (hasClasses()) {
|
||||||
|
return tag.withClass(classes);
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package org.xbib.j2html.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.Config;
|
||||||
|
import org.xbib.j2html.rendering.TagBuilder;
|
||||||
|
import org.xbib.j2html.tags.Renderable;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class Attribute implements Renderable {
|
||||||
|
private String name;
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public Attribute(String name, String value) {
|
||||||
|
this.name = name;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Attribute(String name) {
|
||||||
|
this.name = name;
|
||||||
|
this.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public void renderModel(Appendable writer, Object model) throws IOException {
|
||||||
|
if (writer instanceof TagBuilder) {
|
||||||
|
if (name != null) {
|
||||||
|
if (value != null) {
|
||||||
|
((TagBuilder) writer).appendAttribute(name, value);
|
||||||
|
} else {
|
||||||
|
((TagBuilder) writer).appendBooleanAttribute(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (name == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
writer.append(' ');
|
||||||
|
writer.append(name);
|
||||||
|
if (value != null) {
|
||||||
|
writer.append("=\"");
|
||||||
|
writer.append(Config.textEscaper.escape(value));
|
||||||
|
writer.append('"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render(TagBuilder builder, Object model) throws IOException {
|
||||||
|
// Maintain compatibility with classes that extend Attribute, for now...
|
||||||
|
renderModel(builder, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
182
j2html/src/main/java/org/xbib/j2html/rendering/FlatHtml.java
Normal file
182
j2html/src/main/java/org/xbib/j2html/rendering/FlatHtml.java
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
package org.xbib.j2html.rendering;
|
||||||
|
|
||||||
|
import org.xbib.j2html.Config;
|
||||||
|
import org.xbib.j2html.utils.TextEscaper;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composes HTML without any extra line breaks or indentation.
|
||||||
|
*
|
||||||
|
* @param <T> The type of the Appendable to which HTML will be appended.
|
||||||
|
*/
|
||||||
|
public class FlatHtml<T extends Appendable> implements HtmlBuilder<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an HtmlBuilder that will generate flat HTML using
|
||||||
|
* Config defaults.
|
||||||
|
*
|
||||||
|
* @param out The Appendable to which HTML will be appended.
|
||||||
|
* @param <T> The type of the Appendable to which HTML will be appended.
|
||||||
|
* @return An HtmlBuilder for flat HTML.
|
||||||
|
*/
|
||||||
|
public static <T extends Appendable> FlatHtml<T> into(T out) {
|
||||||
|
return new FlatHtml<>(out, Config.defaults());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an HtmlBuilder that will generate flat HTML using
|
||||||
|
* the given Config.
|
||||||
|
*
|
||||||
|
* @param out The Appendable to which HTML will be appended.
|
||||||
|
* @param config The Config which will specify text escapement, tag closing, etc.
|
||||||
|
* @param <T> The type of the Appendable to which HTML will be appended.
|
||||||
|
* @return An HtmlBuilder for flat HTML.
|
||||||
|
*/
|
||||||
|
public static <T extends Appendable> FlatHtml<T> into(T out, Config config) {
|
||||||
|
return new FlatHtml<>(out, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an HtmlBuilder that will generate flat HTML in memory
|
||||||
|
* using Config defaults.
|
||||||
|
*
|
||||||
|
* @return An HtmlBuilder for flat HTML.
|
||||||
|
*/
|
||||||
|
public static FlatHtml<StringBuilder> inMemory() {
|
||||||
|
return into(new StringBuilder());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an HtmlBuilder that will generate flat HTML in memory
|
||||||
|
* using the given Config.
|
||||||
|
* @param config The Config which will specify text escapement, tag closing, etc.
|
||||||
|
* @return An HtmlBuilder for flat HTML.
|
||||||
|
*/
|
||||||
|
public static FlatHtml<StringBuilder> inMemory(Config config) {
|
||||||
|
return into(new StringBuilder(), config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final T out;
|
||||||
|
private final TextEscaper textEscaper;
|
||||||
|
private final TagBuilder enclosingElementAttributes;
|
||||||
|
private final TagBuilder emptyElementAttributes;
|
||||||
|
|
||||||
|
private FlatHtml(T out, Config config) {
|
||||||
|
this.out = out;
|
||||||
|
this.textEscaper = config.textEscaper();
|
||||||
|
this.enclosingElementAttributes = new FlatTagBuilder(false);
|
||||||
|
this.emptyElementAttributes = new FlatTagBuilder(config.closeEmptyTags());
|
||||||
|
}
|
||||||
|
|
||||||
|
public T output() {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public HtmlBuilder<T> append(CharSequence csq) throws IOException {
|
||||||
|
out.append(csq);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public HtmlBuilder<T> append(CharSequence csq, int start, int end) throws IOException {
|
||||||
|
out.append(csq, start, end);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public HtmlBuilder<T> append(char c) throws IOException {
|
||||||
|
out.append(c);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TagBuilder appendStartTag(String name) throws IOException {
|
||||||
|
out.append("<").append(name);
|
||||||
|
return enclosingElementAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HtmlBuilder<T> appendEndTag(String name) throws IOException {
|
||||||
|
out.append("</").append(name).append(">");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TagBuilder appendEmptyTag(String name) throws IOException {
|
||||||
|
out.append("<").append(name);
|
||||||
|
return emptyElementAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HtmlBuilder<T> appendEscapedText(String txt) throws IOException {
|
||||||
|
out.append(textEscaper.escape(txt));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HtmlBuilder<T> appendUnescapedText(String txt) throws IOException {
|
||||||
|
out.append(txt);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FlatTagBuilder implements TagBuilder {
|
||||||
|
|
||||||
|
private final boolean closeTag;
|
||||||
|
|
||||||
|
private FlatTagBuilder(boolean closeTag) {
|
||||||
|
this.closeTag = closeTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TagBuilder appendAttribute(String name, String value) throws IOException {
|
||||||
|
out.append(" ")
|
||||||
|
.append(name)
|
||||||
|
.append("=\"")
|
||||||
|
.append(textEscaper.escape(value))
|
||||||
|
.append("\"");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TagBuilder appendBooleanAttribute(String name) throws IOException {
|
||||||
|
out.append(" ").append(name);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HtmlBuilder<T> completeTag() throws IOException {
|
||||||
|
if (closeTag) {
|
||||||
|
out.append("/");
|
||||||
|
}
|
||||||
|
out.append(">");
|
||||||
|
|
||||||
|
return FlatHtml.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public TagBuilder append(CharSequence csq) throws IOException {
|
||||||
|
out.append(csq);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public TagBuilder append(CharSequence csq, int start, int end) throws IOException {
|
||||||
|
out.append(csq, start, end);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public TagBuilder append(char c) throws IOException {
|
||||||
|
out.append(c);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package org.xbib.j2html.rendering;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementations of HtmlBuilder are wrappers around an
|
||||||
|
* Appendable, and support appending HTML-specific character
|
||||||
|
* sequences to that Appendable.
|
||||||
|
* <p>
|
||||||
|
* Note: HtmlBuilder extends Appendable for compatibility with
|
||||||
|
* previous version of this library. This extension will probably be
|
||||||
|
* removed in the future, so avoid relying on the deprecated methods
|
||||||
|
* of this interface.
|
||||||
|
*
|
||||||
|
* @param <T> The type of the Appendable. Used so that the
|
||||||
|
* same type can be returned to the caller, allowing
|
||||||
|
* for additional work to be done on the Appendable
|
||||||
|
* without the need for manual casting.
|
||||||
|
*/
|
||||||
|
public interface HtmlBuilder<T extends Appendable> extends Appendable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a start tag with the given name to the output. The
|
||||||
|
* returned TagBuilder is then used to append attributes
|
||||||
|
* and eventually complete the start tag.
|
||||||
|
*
|
||||||
|
* @param name The name of the start tag.
|
||||||
|
* @return An TagBuilder which can append attributes to the start tag.
|
||||||
|
* @throws IOException When the Appendable throws an IOException.
|
||||||
|
*/
|
||||||
|
TagBuilder appendStartTag(String name) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends an end tag with the given name to the output.
|
||||||
|
*
|
||||||
|
* @param name The name of the end tag.
|
||||||
|
* @return An HtmlBuilder that can continue appending HTML to the output.
|
||||||
|
* @throws IOException When the Appendable throws an IOException.
|
||||||
|
*/
|
||||||
|
HtmlBuilder<T> appendEndTag(String name) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends an empty tag with the given name to the output. The
|
||||||
|
* returned TagBuilder is then used to append attributes
|
||||||
|
* and eventually complete the empty tag.
|
||||||
|
*
|
||||||
|
* @param name The name of the empty tag.
|
||||||
|
* @return An TagBuilder which can append attributes to the empty tag.
|
||||||
|
* @throws IOException When the Appendable throws an IOException.
|
||||||
|
*/
|
||||||
|
TagBuilder appendEmptyTag(String name) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends escaped text to the output.
|
||||||
|
*
|
||||||
|
* @param txt The text to append.
|
||||||
|
* @return An HtmlBuilder that can continue appending HTML to the output.
|
||||||
|
* @throws IOException When the Appendable throws an IOException.
|
||||||
|
*/
|
||||||
|
HtmlBuilder<T> appendEscapedText(String txt) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends unescaped text to the output.
|
||||||
|
*
|
||||||
|
* @param txt The text to append.
|
||||||
|
* @return An HtmlBuilder that can continue appending HTML to the output.
|
||||||
|
* @throws IOException When the Appendable throws an IOException.
|
||||||
|
*/
|
||||||
|
HtmlBuilder<T> appendUnescapedText(String txt) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Appendable that was being wrapped.
|
||||||
|
*
|
||||||
|
* @return The original Appendable.
|
||||||
|
*/
|
||||||
|
T output();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
HtmlBuilder<T> append(CharSequence csq) throws IOException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
HtmlBuilder<T> append(CharSequence csq, int start, int end) throws IOException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
HtmlBuilder<T> append(char c) throws IOException;
|
||||||
|
}
|
246
j2html/src/main/java/org/xbib/j2html/rendering/IndentedHtml.java
Normal file
246
j2html/src/main/java/org/xbib/j2html/rendering/IndentedHtml.java
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
package org.xbib.j2html.rendering;
|
||||||
|
|
||||||
|
import org.xbib.j2html.Config;
|
||||||
|
import org.xbib.j2html.utils.Indenter;
|
||||||
|
import org.xbib.j2html.utils.TextEscaper;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Deque;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composes HTML with lines breaks and indentation between tags and text.
|
||||||
|
*
|
||||||
|
* @param <T> The type of the Appendable to which HTML will be appended.
|
||||||
|
*/
|
||||||
|
public class IndentedHtml<T extends Appendable> implements HtmlBuilder<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an HtmlBuilder that will generate indented HTML using
|
||||||
|
* Config defaults.
|
||||||
|
*
|
||||||
|
* @param out The Appendable to which HTML will be appended.
|
||||||
|
* @param <T> The type of the Appendable to which HTML will be appended.
|
||||||
|
* @return An HtmlBuilder for indented HTML.
|
||||||
|
*/
|
||||||
|
public static final <T extends Appendable> IndentedHtml<T> into(T out) {
|
||||||
|
return new IndentedHtml<>(out, Config.defaults());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an HtmlBuilder that will generate indented HTML using
|
||||||
|
* the given Config.
|
||||||
|
*
|
||||||
|
* @param out The Appendable to which HTML will be appended.
|
||||||
|
* @param config The Config which will specify indentation, text escapement, tag closing, etc.
|
||||||
|
* @param <T> The type of the Appendable to which HTML will be appended.
|
||||||
|
* @return An HtmlBuilder for indented HTML.
|
||||||
|
*/
|
||||||
|
public static final <T extends Appendable> IndentedHtml<T> into(T out, Config config) {
|
||||||
|
return new IndentedHtml<>(out, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an HtmlBuilder that will generate indented HTML in memory using
|
||||||
|
* Config defaults.
|
||||||
|
*
|
||||||
|
* @return An HtmlBuilder for indented HTML.
|
||||||
|
*/
|
||||||
|
public static final IndentedHtml<StringBuilder> inMemory() {
|
||||||
|
return into(new StringBuilder());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an HtmlBuilder that will generate indented HTML in memory using
|
||||||
|
* the given Config.
|
||||||
|
*
|
||||||
|
* @param config The Config which will specify indentation, text escapement, tag closing, etc.
|
||||||
|
* @return An HtmlBuilder for indented HTML.
|
||||||
|
*/
|
||||||
|
public static final IndentedHtml<StringBuilder> inMemory(Config config) {
|
||||||
|
return into(new StringBuilder(), config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final T out;
|
||||||
|
private final Indenter indenter;
|
||||||
|
private final TextEscaper textEscaper;
|
||||||
|
private final TagBuilder enclosingElementAttributes;
|
||||||
|
private final TagBuilder emptyElementAttributes;
|
||||||
|
|
||||||
|
// Dealing with preformatted elements (pre and textarea) requires
|
||||||
|
// that we know what our parent elements are. To do that we use
|
||||||
|
// a stack; adding items as start tags are created, and removing them
|
||||||
|
// as those tags are closed. Determining whether or not we are
|
||||||
|
// currently rendering into a preformatted element is as simple as
|
||||||
|
// asking if any tags on the stack match a preformatted element name.
|
||||||
|
private final Deque<String> trace = new ArrayDeque<>();
|
||||||
|
|
||||||
|
private IndentedHtml(T out, Config config) {
|
||||||
|
this.out = out;
|
||||||
|
this.indenter = config.indenter();
|
||||||
|
this.textEscaper = config.textEscaper();
|
||||||
|
this.enclosingElementAttributes = new IndentedTagBuilder(false);
|
||||||
|
this.emptyElementAttributes = new IndentedTagBuilder(config.closeEmptyTags());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isContentSelfFormatting() {
|
||||||
|
return trace.contains("pre") || trace.contains("textarea");
|
||||||
|
}
|
||||||
|
|
||||||
|
private int lvl() {
|
||||||
|
return trace.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TagBuilder appendStartTag(String name) throws IOException {
|
||||||
|
if (!isContentSelfFormatting()) {
|
||||||
|
out.append(indenter.indent(lvl(), ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
trace.push(name);
|
||||||
|
|
||||||
|
out.append("<").append(name);
|
||||||
|
return enclosingElementAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HtmlBuilder<T> appendEndTag(String name) throws IOException {
|
||||||
|
if (!name.equals(trace.peek())) {
|
||||||
|
throw new RuntimeException("Incorrect element closed: " + name + ". Expected: " + trace.peek());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isContentSelfFormatting()) {
|
||||||
|
trace.pop();
|
||||||
|
out.append(indenter.indent(lvl(), ""));
|
||||||
|
} else {
|
||||||
|
trace.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
out.append("</").append(name).append(">");
|
||||||
|
|
||||||
|
if (!isContentSelfFormatting()) {
|
||||||
|
out.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TagBuilder appendEmptyTag(String name) throws IOException {
|
||||||
|
if (!isContentSelfFormatting()) {
|
||||||
|
out.append(indenter.indent(lvl(), ""));
|
||||||
|
}
|
||||||
|
out.append("<").append(name);
|
||||||
|
return emptyElementAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendLines(String txt) throws IOException {
|
||||||
|
if (!isContentSelfFormatting()) {
|
||||||
|
String[] lines = txt.split("\n");
|
||||||
|
for (String line : lines) {
|
||||||
|
out.append(indenter.indent(lvl(), line)).append("\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.append(txt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HtmlBuilder<T> appendEscapedText(String txt) throws IOException {
|
||||||
|
appendLines(textEscaper.escape(txt));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HtmlBuilder<T> appendUnescapedText(String txt) throws IOException {
|
||||||
|
appendLines(txt);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T output() {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public HtmlBuilder<T> append(CharSequence csq) throws IOException {
|
||||||
|
out.append(csq);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public HtmlBuilder<T> append(CharSequence csq, int start, int end) throws IOException {
|
||||||
|
out.append(csq, start, end);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public HtmlBuilder<T> append(char c) throws IOException {
|
||||||
|
out.append(c);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class IndentedTagBuilder implements TagBuilder {
|
||||||
|
|
||||||
|
private final boolean closeTag;
|
||||||
|
|
||||||
|
private IndentedTagBuilder(boolean closeTag) {
|
||||||
|
this.closeTag = closeTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TagBuilder appendAttribute(String name, String value) throws IOException {
|
||||||
|
out.append(" ")
|
||||||
|
.append(name)
|
||||||
|
.append("=\"")
|
||||||
|
.append(textEscaper.escape(value))
|
||||||
|
.append("\"");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TagBuilder appendBooleanAttribute(String name) throws IOException {
|
||||||
|
out.append(" ").append(name);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HtmlBuilder<T> completeTag() throws IOException {
|
||||||
|
if (closeTag) {
|
||||||
|
out.append("/");
|
||||||
|
}
|
||||||
|
out.append(">");
|
||||||
|
|
||||||
|
if (!isContentSelfFormatting()) {
|
||||||
|
out.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return IndentedHtml.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public TagBuilder append(CharSequence csq) throws IOException {
|
||||||
|
out.append(csq);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public TagBuilder append(CharSequence csq, int start, int end) throws IOException {
|
||||||
|
out.append(csq, start, end);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public TagBuilder append(char c) throws IOException {
|
||||||
|
out.append(c);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package org.xbib.j2html.rendering;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementations of TagBuilder are used to append HTML tag
|
||||||
|
* attributes to an Appendable. TagBuilders are scoped to the
|
||||||
|
* creation and completion of a specific tag, and should not be used
|
||||||
|
* outside of that tag.
|
||||||
|
* <p>
|
||||||
|
* Note: TagBuilder extends Appendable for compatibility with
|
||||||
|
* previous version of this library. This extension will probably be
|
||||||
|
* removed in the future, so avoid relying on the deprecated methods
|
||||||
|
* of this interface.
|
||||||
|
*/
|
||||||
|
public interface TagBuilder extends Appendable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends an key/value pair as an HTML attribute to the current tag.
|
||||||
|
*
|
||||||
|
* @param name The name of an attribute.
|
||||||
|
* @param value The value of an attribute.
|
||||||
|
* @return An TagBuilder which can continue appending attributes.
|
||||||
|
* @throws IOException When the Appendable throws an IOException.
|
||||||
|
*/
|
||||||
|
TagBuilder appendAttribute(String name, String value) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a name, as a boolean HTML attribute to the current tag.
|
||||||
|
*
|
||||||
|
* @param name The name of the boolean attribute.
|
||||||
|
* @return An TagBuilder which can continue appending attributes.
|
||||||
|
* @throws IOException When the Appendable throws an IOException.
|
||||||
|
*/
|
||||||
|
TagBuilder appendBooleanAttribute(String name) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends any characters which are necessary to close the current tag,
|
||||||
|
* and returns an HtmlBuilder that can continue appending to the output.
|
||||||
|
*
|
||||||
|
* @return An HtmlBuilder that can continue appending HTML to the output.
|
||||||
|
* @throws IOException When the Appendable throws an IOException.
|
||||||
|
*/
|
||||||
|
HtmlBuilder<? extends Appendable> completeTag() throws IOException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
TagBuilder append(CharSequence csq) throws IOException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
TagBuilder append(CharSequence csq, int start, int end) throws IOException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
TagBuilder append(char c) throws IOException;
|
||||||
|
}
|
186
j2html/src/main/java/org/xbib/j2html/tags/ContainerTag.java
Normal file
186
j2html/src/main/java/org/xbib/j2html/tags/ContainerTag.java
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
package org.xbib.j2html.tags;
|
||||||
|
|
||||||
|
import org.xbib.j2html.Config;
|
||||||
|
import org.xbib.j2html.attributes.Attribute;
|
||||||
|
import org.xbib.j2html.rendering.TagBuilder;
|
||||||
|
import org.xbib.j2html.rendering.FlatHtml;
|
||||||
|
import org.xbib.j2html.rendering.HtmlBuilder;
|
||||||
|
import org.xbib.j2html.rendering.IndentedHtml;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class ContainerTag<T extends ContainerTag<T>> extends Tag<T> {
|
||||||
|
|
||||||
|
protected List<DomContent> children;
|
||||||
|
|
||||||
|
public ContainerTag(String tagName) {
|
||||||
|
super(tagName);
|
||||||
|
this.children = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a DomContent-object to the end of this element
|
||||||
|
*
|
||||||
|
* @param child DomContent-object to be appended
|
||||||
|
* @return itself for easy chaining
|
||||||
|
*/
|
||||||
|
public T with(DomContent child) {
|
||||||
|
if (this == child) {
|
||||||
|
throw new RuntimeException("Cannot append a tag to itself.");
|
||||||
|
}
|
||||||
|
if (child != null) {
|
||||||
|
// in some cases, like when using iff(), we ignore null children
|
||||||
|
children.add(child);
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call with-method based on condition
|
||||||
|
* {@link #with(DomContent child)}
|
||||||
|
*
|
||||||
|
* @param condition the condition to use
|
||||||
|
* @param child DomContent-object to be appended if condition met
|
||||||
|
* @return itself for easy chaining
|
||||||
|
*/
|
||||||
|
public T condWith(boolean condition, DomContent child) {
|
||||||
|
return condition ? this.with(child) : self();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a list of DomContent-objects to the end of this element
|
||||||
|
*
|
||||||
|
* @param children DomContent-objects to be appended
|
||||||
|
* @return itself for easy chaining
|
||||||
|
*/
|
||||||
|
public T with(Iterable<? extends DomContent> children) {
|
||||||
|
if (children != null) {
|
||||||
|
for (DomContent child : children) {
|
||||||
|
this.with(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call with-method based on condition
|
||||||
|
* {@link #with(java.lang.Iterable)}
|
||||||
|
*
|
||||||
|
* @param condition the condition to use
|
||||||
|
* @param children DomContent-objects to be appended if condition met
|
||||||
|
* @return itself for easy chaining
|
||||||
|
*/
|
||||||
|
public T condWith(boolean condition, Iterable<? extends DomContent> children) {
|
||||||
|
return condition ? this.with(children) : self();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the DomContent-objects to the end of this element
|
||||||
|
*
|
||||||
|
* @param children DomContent-objects to be appended
|
||||||
|
* @return itself for easy chaining
|
||||||
|
*/
|
||||||
|
public T with(DomContent... children) {
|
||||||
|
for (DomContent child : children) {
|
||||||
|
with(child);
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the DomContent-objects in the stream to the end of this element
|
||||||
|
*
|
||||||
|
* @param children Stream of DomContent-objects to be appended
|
||||||
|
* @return itself for easy chaining
|
||||||
|
*/
|
||||||
|
public T with(Stream<DomContent> children) {
|
||||||
|
children.forEach(this::with);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call with-method based on condition
|
||||||
|
* {@link #with(DomContent... children)}
|
||||||
|
*
|
||||||
|
* @param condition the condition to use
|
||||||
|
* @param children DomContent-objects to be appended if condition met
|
||||||
|
* @return itself for easy chaining
|
||||||
|
*/
|
||||||
|
public T condWith(boolean condition, DomContent... children) {
|
||||||
|
return condition ? this.with(children) : self();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a Text-object to this element
|
||||||
|
*
|
||||||
|
* @param text the text to be appended
|
||||||
|
* @return itself for easy chaining
|
||||||
|
*/
|
||||||
|
public T withText(String text) {
|
||||||
|
return with(new Text(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets number of child nodes this tag element contains
|
||||||
|
*/
|
||||||
|
public int getNumChildren() {
|
||||||
|
return children.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the ContainerTag and its children, adding newlines before each
|
||||||
|
* child and using Config.indenter to indent child based on how deep
|
||||||
|
* in the tree it is
|
||||||
|
*
|
||||||
|
* @return the rendered and formatted string
|
||||||
|
*/
|
||||||
|
public String renderFormatted() {
|
||||||
|
try {
|
||||||
|
return render(IndentedHtml.into(new StringBuilder(), Config.global())).toString();
|
||||||
|
}catch (IOException e) {
|
||||||
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <A extends Appendable> A render(HtmlBuilder<A> builder, Object model) throws IOException {
|
||||||
|
if (hasTagName()) {
|
||||||
|
TagBuilder tagBuilder = builder.appendStartTag(getTagName());
|
||||||
|
for(Attribute attribute : getAttributes()){
|
||||||
|
attribute.render(tagBuilder, model);
|
||||||
|
}
|
||||||
|
tagBuilder.completeTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(DomContent child : children){
|
||||||
|
child.render(builder, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hasTagName()) {
|
||||||
|
builder.appendEndTag(getTagName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.output();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public void renderModel(Appendable writer, Object model) throws IOException {
|
||||||
|
HtmlBuilder<?> builder = (writer instanceof HtmlBuilder)
|
||||||
|
? (HtmlBuilder<?>) writer
|
||||||
|
: FlatHtml.into(writer, Config.global());
|
||||||
|
|
||||||
|
render(builder, model);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.xbib.j2html.tags;
|
||||||
|
|
||||||
|
public abstract class DomContent implements Renderable {
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return render();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package org.xbib.j2html.tags;
|
||||||
|
|
||||||
|
public class DomContentJoiner {
|
||||||
|
|
||||||
|
public static UnescapedText join(CharSequence delimiter, boolean fixPeriodAndCommaSpacing, Object... stringOrDomObjects) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < stringOrDomObjects.length; i++) {
|
||||||
|
Object o = stringOrDomObjects[i];
|
||||||
|
if (o instanceof String) {
|
||||||
|
sb.append(((String) o).trim());
|
||||||
|
} else if (o instanceof DomContent) {
|
||||||
|
sb.append(((DomContent) o).render().trim());
|
||||||
|
} else if (o == null) {
|
||||||
|
//Discard null objects so iff/iffelse can be used with join
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("You can only join DomContent and String objects");
|
||||||
|
}
|
||||||
|
if (i < stringOrDomObjects.length-1) {
|
||||||
|
sb.append(delimiter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String joined = sb.toString().trim();
|
||||||
|
if (fixPeriodAndCommaSpacing) {
|
||||||
|
joined = joined.replaceAll("\\s\\.", ".");
|
||||||
|
joined = joined.replaceAll("\\s\\,", ",");
|
||||||
|
}
|
||||||
|
return new UnescapedText(joined);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
42
j2html/src/main/java/org/xbib/j2html/tags/EmptyTag.java
Normal file
42
j2html/src/main/java/org/xbib/j2html/tags/EmptyTag.java
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package org.xbib.j2html.tags;
|
||||||
|
|
||||||
|
import org.xbib.j2html.Config;
|
||||||
|
import org.xbib.j2html.attributes.Attribute;
|
||||||
|
import org.xbib.j2html.rendering.TagBuilder;
|
||||||
|
import org.xbib.j2html.rendering.FlatHtml;
|
||||||
|
import org.xbib.j2html.rendering.HtmlBuilder;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class EmptyTag<T extends EmptyTag<T>> extends Tag<T> {
|
||||||
|
|
||||||
|
public EmptyTag(String tagName) {
|
||||||
|
super(tagName);
|
||||||
|
if (tagName == null) {
|
||||||
|
throw new IllegalArgumentException("Illegal tag name: null");
|
||||||
|
}
|
||||||
|
if ("".equals(tagName)) {
|
||||||
|
throw new IllegalArgumentException("Illegal tag name: \"\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <A extends Appendable> A render(HtmlBuilder<A> builder, Object model) throws IOException {
|
||||||
|
TagBuilder attrs = builder.appendEmptyTag(getTagName());
|
||||||
|
for (Attribute attr : getAttributes()) {
|
||||||
|
attr.render(attrs, model);
|
||||||
|
}
|
||||||
|
attrs.completeTag();
|
||||||
|
return builder.output();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public void renderModel(Appendable writer, Object model) throws IOException {
|
||||||
|
HtmlBuilder<?> builder = (writer instanceof HtmlBuilder)
|
||||||
|
? (HtmlBuilder<?>) writer
|
||||||
|
: FlatHtml.into(writer, Config.global());
|
||||||
|
|
||||||
|
render(builder, model);
|
||||||
|
}
|
||||||
|
}
|
20
j2html/src/main/java/org/xbib/j2html/tags/IInstance.java
Normal file
20
j2html/src/main/java/org/xbib/j2html/tags/IInstance.java
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package org.xbib.j2html.tags;
|
||||||
|
|
||||||
|
public interface IInstance<T> {
|
||||||
|
|
||||||
|
//to get the actual instance
|
||||||
|
// (every implementing class would have to implement: { return this; }
|
||||||
|
// public T self();
|
||||||
|
|
||||||
|
// this method shows up in autocomplete.
|
||||||
|
// this is really undesireable as it does not do anything.
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
default T self() {
|
||||||
|
//we know that the implementing class will supply
|
||||||
|
//its own type as the type argument.
|
||||||
|
//therefore every instance of IInstance can assume it
|
||||||
|
//is also of type T
|
||||||
|
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package org.xbib.j2html.tags;
|
||||||
|
|
||||||
|
import org.xbib.j2html.Config;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
import static org.xbib.j2html.TagCreator.rawHtml;
|
||||||
|
import static org.xbib.j2html.TagCreator.script;
|
||||||
|
import static org.xbib.j2html.TagCreator.style;
|
||||||
|
|
||||||
|
public class InlineStaticResource {
|
||||||
|
|
||||||
|
public static ContainerTag<? extends Tag<?>> get(String path, TargetFormat format) {
|
||||||
|
String fileString = getFileAsString(path);
|
||||||
|
return switch (format) {
|
||||||
|
case CSS_MIN -> style().with(rawHtml(Config.cssMinifier.minify(fileString)));
|
||||||
|
case JS_MIN -> script().with(rawHtml(Config.jsMinifier.minify((fileString))));
|
||||||
|
case CSS -> style().with(rawHtml(fileString));
|
||||||
|
case JS -> script().with(rawHtml(fileString));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFileAsString(String path) {
|
||||||
|
try {
|
||||||
|
return streamToString(InlineStaticResource.class.getResourceAsStream(path));
|
||||||
|
} catch (Exception expected) {
|
||||||
|
try {
|
||||||
|
return streamToString(new FileInputStream(path));
|
||||||
|
} catch (Exception exception) {
|
||||||
|
throw new RuntimeException("Couldn't find file with path='" + path + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String streamToString(InputStream inputStream) {
|
||||||
|
Scanner s = new Scanner(inputStream).useDelimiter("\\A");
|
||||||
|
return s.hasNext() ? s.next() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TargetFormat {CSS_MIN, CSS, JS_MIN, JS}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
81
j2html/src/main/java/org/xbib/j2html/tags/Renderable.java
Normal file
81
j2html/src/main/java/org/xbib/j2html/tags/Renderable.java
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
package org.xbib.j2html.tags;
|
||||||
|
|
||||||
|
import org.xbib.j2html.Config;
|
||||||
|
import org.xbib.j2html.rendering.FlatHtml;
|
||||||
|
import org.xbib.j2html.rendering.HtmlBuilder;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
|
||||||
|
public interface Renderable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the Renderable and it's children using the supplied builder.
|
||||||
|
*
|
||||||
|
* @param builder A builder that can compose HTML elements.
|
||||||
|
* @param model A model object to provide data for children to render.
|
||||||
|
* @param <T> The type of the Appendable which HTML is being appended to.
|
||||||
|
* @return The Appendable to which HTML has been appended.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
default <T extends Appendable> T render(HtmlBuilder<T> builder, Object model) throws IOException {
|
||||||
|
// This method should be overridden by any internal classes.
|
||||||
|
// renderModel() is only being called to support backwards
|
||||||
|
// compatibility.
|
||||||
|
renderModel(builder, model);
|
||||||
|
return builder.output();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the Renderable and it's children using the supplied builder.
|
||||||
|
*
|
||||||
|
* @param builder A builder that can compose HTML elements.
|
||||||
|
* @param <T> The type of the Appendable to which HTML is being appended.
|
||||||
|
* @return The Appendable to which HTML has been appended.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
default <T extends Appendable> T render(HtmlBuilder<T> builder) throws IOException {
|
||||||
|
return render(builder, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a StringBuilder and use it to render the Renderable and it's
|
||||||
|
* children
|
||||||
|
*/
|
||||||
|
default String render() {
|
||||||
|
try {
|
||||||
|
return render(FlatHtml.into(new StringBuilder(), Config.global())).toString();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the Renderable and it's children using the supplied writer
|
||||||
|
*
|
||||||
|
* @param writer the current writer
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
default void render(Appendable writer) throws IOException {
|
||||||
|
if (writer instanceof HtmlBuilder) {
|
||||||
|
render((HtmlBuilder<? extends Appendable>) writer);
|
||||||
|
} else {
|
||||||
|
render(FlatHtml.into(writer, Config.global()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the Renderable and it's children using the supplied writer and a model.
|
||||||
|
*
|
||||||
|
* @param writer the current writer
|
||||||
|
* @param model a model object to provide data for children to render
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
default void renderModel(Appendable writer, Object model) throws IOException {
|
||||||
|
// This method is a placeholder to support any client classes
|
||||||
|
// which previously extended Renderable implementers, such as Tags.
|
||||||
|
// No internal classes should implement this method; except to support
|
||||||
|
// compatibility. Instead they should implement rendering with an HtmlBuilder.
|
||||||
|
throw new RuntimeException("Renderable.renderModel(Appendable writer, Object model) has been deprecated. Please use Renderable.render(HtmlBuilder<T> builder, Object model) instead.");
|
||||||
|
}
|
||||||
|
}
|
213
j2html/src/main/java/org/xbib/j2html/tags/Tag.java
Normal file
213
j2html/src/main/java/org/xbib/j2html/tags/Tag.java
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
package org.xbib.j2html.tags;
|
||||||
|
|
||||||
|
import org.xbib.j2html.attributes.Attr;
|
||||||
|
import org.xbib.j2html.attributes.Attribute;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
public abstract class Tag<T extends Tag<T>> extends DomContent implements IInstance<T> {
|
||||||
|
private final String tagName;
|
||||||
|
private final ArrayList<Attribute> attributes;
|
||||||
|
|
||||||
|
protected Tag(String tagName) {
|
||||||
|
this.tagName = tagName;
|
||||||
|
this.attributes = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTagName() {
|
||||||
|
return this.tagName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean hasTagName() {
|
||||||
|
return tagName != null && !tagName.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ArrayList<Attribute> getAttributes() {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an attribute on an element
|
||||||
|
*
|
||||||
|
* @param name the attribute
|
||||||
|
* @param value the attribute value
|
||||||
|
*/
|
||||||
|
boolean setAttribute(String name, String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return attributes.add(new Attribute(name));
|
||||||
|
}
|
||||||
|
for (Attribute attribute : attributes) {
|
||||||
|
if (attribute.getName().equals(name)) {
|
||||||
|
attribute.setValue(value); // update with new value
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return attributes.add(new Attribute(name, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a custom attribute
|
||||||
|
*
|
||||||
|
* @param attribute the attribute name
|
||||||
|
* @param value the attribute value
|
||||||
|
* @return itself for easy chaining
|
||||||
|
*/
|
||||||
|
public T attr(String attribute, Object value) {
|
||||||
|
setAttribute(attribute, value == null ? null : String.valueOf(value));
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the specified attribute. If the Tag previously contained an attribute with the same name, the old attribute is replaced by the specified attribute.
|
||||||
|
*
|
||||||
|
* @param attribute the attribute
|
||||||
|
* @return itself for easy chaining
|
||||||
|
*/
|
||||||
|
public T attr(Attribute attribute) {
|
||||||
|
Iterator<Attribute> iterator = attributes.iterator();
|
||||||
|
String name = attribute.getName();
|
||||||
|
if (name != null) {
|
||||||
|
// name == null is allowed, but those Attributes are not rendered. So we add them anyway.
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Attribute existingAttribute = iterator.next();
|
||||||
|
if (existingAttribute.getName().equals(name)) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attributes.add(attribute);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a custom attribute without value
|
||||||
|
*
|
||||||
|
* @param attribute the attribute name
|
||||||
|
* @return itself for easy chaining
|
||||||
|
* @see Tag#attr(String, Object)
|
||||||
|
*/
|
||||||
|
public T attr(String attribute) {
|
||||||
|
return attr(attribute, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call attr-method based on condition
|
||||||
|
* {@link #attr(String attribute, Object value)}
|
||||||
|
*
|
||||||
|
* @param condition the condition
|
||||||
|
* @param attribute the attribute name
|
||||||
|
* @param value the attribute value
|
||||||
|
* @return itself for easy chaining
|
||||||
|
*/
|
||||||
|
public T condAttr(boolean condition, String attribute, String value) {
|
||||||
|
return (condition ? attr(attribute, value) : self());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof Tag)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return ((Tag<?>) obj).render().equals(this.render());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience methods that call attr with predefined attributes
|
||||||
|
*
|
||||||
|
* @return itself for easy chaining
|
||||||
|
*/
|
||||||
|
public T withClasses(String... classes) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (String s : classes) {
|
||||||
|
sb.append(s != null ? s : "").append(" ");
|
||||||
|
}
|
||||||
|
return attr(Attr.CLASS, sb.toString().trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Tag.java contains all Global Attributes, Attributes which are
|
||||||
|
valid on all HTML Tags. Reference:
|
||||||
|
https://www.w3schools.com/tags/ref_standardattributes.asp
|
||||||
|
Attributes:
|
||||||
|
|
||||||
|
accesskey
|
||||||
|
class
|
||||||
|
contenteditable
|
||||||
|
data-*
|
||||||
|
dir
|
||||||
|
draggable
|
||||||
|
hidden
|
||||||
|
id
|
||||||
|
lang
|
||||||
|
spellcheck
|
||||||
|
style
|
||||||
|
tabindex
|
||||||
|
title
|
||||||
|
translate
|
||||||
|
*/
|
||||||
|
|
||||||
|
public T withAccesskey(String accesskey){ return attr(Attr.ACCESSKEY, accesskey); }
|
||||||
|
|
||||||
|
public T withClass(String className) { return attr(Attr.CLASS, className); }
|
||||||
|
|
||||||
|
public T isContenteditable(){ return attr(Attr.CONTENTEDITABLE, "true"); }
|
||||||
|
|
||||||
|
public T withData(String dataAttr, String value) { return attr(Attr.DATA + "-" + dataAttr, value); }
|
||||||
|
|
||||||
|
public T withDir(String dir) { return attr(Attr.DIR, dir); }
|
||||||
|
|
||||||
|
public T isDraggable(){ return attr(Attr.DRAGGABLE, "true"); }
|
||||||
|
|
||||||
|
public T isHidden() { return attr(Attr.HIDDEN, null); }
|
||||||
|
|
||||||
|
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); }
|
||||||
|
|
||||||
|
public T withTabindex(int index){ return attr(Attr.TABINDEX, index); }
|
||||||
|
|
||||||
|
public T withTitle(String title) { return attr(Attr.TITLE, title); }
|
||||||
|
|
||||||
|
public T isTranslate(){ return attr(Attr.TRANSLATE, "yes"); }
|
||||||
|
|
||||||
|
// ----- start of withCond$ATTR variants -----
|
||||||
|
public T withCondAccessKey(boolean condition, String accesskey){ return condAttr(condition, Attr.ACCESSKEY, accesskey); }
|
||||||
|
|
||||||
|
public T withCondClass(boolean condition, String className) { return condAttr(condition, Attr.CLASS, className); }
|
||||||
|
|
||||||
|
public T withCondContenteditable(boolean condition){ return attr(Attr.CONTENTEDITABLE, (condition)?"true":"false");}
|
||||||
|
|
||||||
|
public T withCondData(boolean condition, String dataAttr, String value) { return condAttr(condition, Attr.DATA + "-" + dataAttr, value); }
|
||||||
|
|
||||||
|
public T withCondDir(boolean condition, String dir) { return condAttr(condition, Attr.DIR, dir); }
|
||||||
|
|
||||||
|
public T withCondDraggable(boolean condition){ return attr(Attr.DRAGGABLE, (condition)?"true":"false"); }
|
||||||
|
|
||||||
|
public T withCondHidden(boolean condition) { return condAttr(condition, Attr.HIDDEN, null); }
|
||||||
|
|
||||||
|
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); }
|
||||||
|
|
||||||
|
public T withCondTabindex(boolean condition, int index){ return condAttr(condition, Attr.TABINDEX, index+""); }
|
||||||
|
|
||||||
|
public T withCondTitle(boolean condition, String title) { return condAttr(condition, Attr.TITLE, title); }
|
||||||
|
|
||||||
|
public T withCondTranslate(boolean condition){ return attr(Attr.TRANSLATE, (condition)?"yes":"no"); }
|
||||||
|
}
|
33
j2html/src/main/java/org/xbib/j2html/tags/Text.java
Normal file
33
j2html/src/main/java/org/xbib/j2html/tags/Text.java
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package org.xbib.j2html.tags;
|
||||||
|
|
||||||
|
import org.xbib.j2html.Config;
|
||||||
|
import org.xbib.j2html.rendering.FlatHtml;
|
||||||
|
import org.xbib.j2html.rendering.HtmlBuilder;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class Text extends DomContent {
|
||||||
|
|
||||||
|
private final String text;
|
||||||
|
|
||||||
|
public Text(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends Appendable> T render(HtmlBuilder<T> builder, Object model) throws IOException {
|
||||||
|
builder.appendEscapedText(String.valueOf(text));
|
||||||
|
return builder.output();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public void renderModel(Appendable writer, Object model) throws IOException {
|
||||||
|
HtmlBuilder<?> builder = (writer instanceof HtmlBuilder)
|
||||||
|
? (HtmlBuilder<?>) writer
|
||||||
|
: FlatHtml.into(writer, Config.global());
|
||||||
|
|
||||||
|
render(builder, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
40
j2html/src/main/java/org/xbib/j2html/tags/UnescapedText.java
Normal file
40
j2html/src/main/java/org/xbib/j2html/tags/UnescapedText.java
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package org.xbib.j2html.tags;
|
||||||
|
|
||||||
|
import org.xbib.j2html.Config;
|
||||||
|
import org.xbib.j2html.rendering.FlatHtml;
|
||||||
|
import org.xbib.j2html.rendering.HtmlBuilder;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class UnescapedText extends DomContent {
|
||||||
|
|
||||||
|
private final String text;
|
||||||
|
|
||||||
|
public UnescapedText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends Appendable> T render(HtmlBuilder<T> builder, Object model) throws IOException {
|
||||||
|
builder.appendUnescapedText(String.valueOf(text));
|
||||||
|
return builder.output();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated
|
||||||
|
public void renderModel(Appendable writer, Object model) throws IOException {
|
||||||
|
HtmlBuilder<?> builder = (writer instanceof HtmlBuilder)
|
||||||
|
? (HtmlBuilder<?>) writer
|
||||||
|
: FlatHtml.into(writer, Config.global());
|
||||||
|
|
||||||
|
render(builder, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == null || !(obj instanceof UnescapedText)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return ((UnescapedText) obj).render().equals(this.render());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IAccept<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withAccept(final String accept_) {
|
||||||
|
return self().attr("accept", accept_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondAccept(final boolean enable, final String accept_) {
|
||||||
|
return enable ? self().attr("accept", accept_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IAction<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withAction(final String action_) {
|
||||||
|
return self().attr("action", action_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondAction(final boolean enable, final String action_) {
|
||||||
|
return enable ? self().attr("action", action_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IAlt<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withAlt(final String alt_) {
|
||||||
|
return self().attr("alt", alt_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondAlt(final boolean enable, final String alt_) {
|
||||||
|
return enable ? self().attr("alt", alt_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IAsync<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T isAsync() {
|
||||||
|
return self().attr("async");
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondAsync(final boolean enable) {
|
||||||
|
return enable ? self().attr("async") : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IAutocomplete<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T isAutocomplete() {
|
||||||
|
return self().attr("autocomplete", "on");
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondAutocomplete(final boolean enable) {
|
||||||
|
return enable ? self().attr("autocomplete", "on") : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IAutofocus<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T isAutofocus() {
|
||||||
|
return self().attr("autofocus");
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondAutofocus(final boolean enable) {
|
||||||
|
return enable ? self().attr("autofocus") : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IAutoplay<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T isAutoplay() {
|
||||||
|
return self().attr("autoplay");
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondAutoplay(final boolean enable) {
|
||||||
|
return enable ? self().attr("autoplay") : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface ICharset<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withCharset(final String charset_) {
|
||||||
|
return self().attr("charset", charset_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondCharset(final boolean enable, final String charset_) {
|
||||||
|
return enable ? self().attr("charset", charset_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IChecked<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T isChecked() {
|
||||||
|
return self().attr("checked");
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondChecked(final boolean enable) {
|
||||||
|
return enable ? self().attr("checked") : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface ICite<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withCite(final String cite_) {
|
||||||
|
return self().attr("cite", cite_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondCite(final boolean enable, final String cite_) {
|
||||||
|
return enable ? self().attr("cite", cite_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface ICols<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withCols(final String cols_) {
|
||||||
|
return self().attr("cols", cols_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondCols(final boolean enable, final String cols_) {
|
||||||
|
return enable ? self().attr("cols", cols_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IColspan<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withColspan(final String colspan_) {
|
||||||
|
return self().attr("colspan", colspan_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondColspan(final boolean enable, final String colspan_) {
|
||||||
|
return enable ? self().attr("colspan", colspan_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IContent<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withContent(final String content_) {
|
||||||
|
return self().attr("content", content_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondContent(final boolean enable, final String content_) {
|
||||||
|
return enable ? self().attr("content", content_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IControls<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T isControls() {
|
||||||
|
return self().attr("controls");
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondControls(final boolean enable) {
|
||||||
|
return enable ? self().attr("controls") : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface ICoords<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withCoords(final String coords_) {
|
||||||
|
return self().attr("coords", coords_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondCoords(final boolean enable, final String coords_) {
|
||||||
|
return enable ? self().attr("coords", coords_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IData<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withData(final String data_) {
|
||||||
|
return self().attr("data", data_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondData(final boolean enable, final String data_) {
|
||||||
|
return enable ? self().attr("data", data_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IDatetime<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withDatetime(final String datetime_) {
|
||||||
|
return self().attr("datetime", datetime_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondDatetime(final boolean enable, final String datetime_) {
|
||||||
|
return enable ? self().attr("datetime", datetime_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IDefault<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T isDefault() {
|
||||||
|
return self().attr("default");
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondDefault(final boolean enable) {
|
||||||
|
return enable ? self().attr("default") : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IDefer<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T isDefer() {
|
||||||
|
return self().attr("defer");
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondDefer(final boolean enable) {
|
||||||
|
return enable ? self().attr("defer") : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IDirname<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withDirname(final String dirname_) {
|
||||||
|
return self().attr("dirname", dirname_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondDirname(final boolean enable, final String dirname_) {
|
||||||
|
return enable ? self().attr("dirname", dirname_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IDisabled<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T isDisabled() {
|
||||||
|
return self().attr("disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondDisabled(final boolean enable) {
|
||||||
|
return enable ? self().attr("disabled") : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IDownload<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T isDownload() {
|
||||||
|
return self().attr("download");
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondDownload(final boolean enable) {
|
||||||
|
return enable ? self().attr("download") : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IEnctype<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withEnctype(final String enctype_) {
|
||||||
|
return self().attr("enctype", enctype_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondEnctype(final boolean enable, final String enctype_) {
|
||||||
|
return enable ? self().attr("enctype", enctype_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IFor<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withFor(final String for_) {
|
||||||
|
return self().attr("for", for_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondFor(final boolean enable, final String for_) {
|
||||||
|
return enable ? self().attr("for", for_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IForm<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withForm(final String form_) {
|
||||||
|
return self().attr("form", form_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondForm(final boolean enable, final String form_) {
|
||||||
|
return enable ? self().attr("form", form_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IFormaction<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withFormaction(final String formaction_) {
|
||||||
|
return self().attr("formaction", formaction_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondFormaction(final boolean enable, final String formaction_) {
|
||||||
|
return enable ? self().attr("formaction", formaction_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IHeaders<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withHeaders(final String headers_) {
|
||||||
|
return self().attr("headers", headers_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondHeaders(final boolean enable, final String headers_) {
|
||||||
|
return enable ? self().attr("headers", headers_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IHeight<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withHeight(final String height_) {
|
||||||
|
return self().attr("height", height_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondHeight(final boolean enable, final String height_) {
|
||||||
|
return enable ? self().attr("height", height_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IHigh<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withHigh(final String high_) {
|
||||||
|
return self().attr("high", high_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondHigh(final boolean enable, final String high_) {
|
||||||
|
return enable ? self().attr("high", high_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IHref<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withHref(final String href_) {
|
||||||
|
return self().attr("href", href_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondHref(final boolean enable, final String href_) {
|
||||||
|
return enable ? self().attr("href", href_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IHreflang<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withHreflang(final String hreflang_) {
|
||||||
|
return self().attr("hreflang", hreflang_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondHreflang(final boolean enable, final String hreflang_) {
|
||||||
|
return enable ? self().attr("hreflang", hreflang_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IIsmap<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T isIsmap() {
|
||||||
|
return self().attr("ismap");
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondIsmap(final boolean enable) {
|
||||||
|
return enable ? self().attr("ismap") : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IKind<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withKind(final String kind_) {
|
||||||
|
return self().attr("kind", kind_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondKind(final boolean enable, final String kind_) {
|
||||||
|
return enable ? self().attr("kind", kind_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface ILabel<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withLabel(final String label_) {
|
||||||
|
return self().attr("label", label_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondLabel(final boolean enable, final String label_) {
|
||||||
|
return enable ? self().attr("label", label_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IList<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withList(final String list_) {
|
||||||
|
return self().attr("list", list_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondList(final boolean enable, final String list_) {
|
||||||
|
return enable ? self().attr("list", list_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface ILoop<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T isLoop() {
|
||||||
|
return self().attr("loop");
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondLoop(final boolean enable) {
|
||||||
|
return enable ? self().attr("loop") : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface ILow<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withLow(final String low_) {
|
||||||
|
return self().attr("low", low_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondLow(final boolean enable, final String low_) {
|
||||||
|
return enable ? self().attr("low", low_) : self();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.j2html.tags.attributes;
|
||||||
|
|
||||||
|
import org.xbib.j2html.tags.IInstance;
|
||||||
|
import org.xbib.j2html.tags.Tag;
|
||||||
|
|
||||||
|
public interface IMax<T extends Tag<T>> extends IInstance<T> {
|
||||||
|
default T withMax(final String max_) {
|
||||||
|
return self().attr("max", max_);
|
||||||
|
}
|
||||||
|
|
||||||
|
default T withCondMax(final boolean enable, final String max_) {
|
||||||
|
return enable ? self().attr("max", max_) : self();
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue