diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index bf1bc10f3..cfd23f546 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -33,6 +33,20 @@ jobs:
- name: Test and Build with Gradle Wrapper
run: ./gradlew build
+ update-pages:
+ needs: test
+ # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
+ permissions:
+ contents: write
+ pages: write
+ id-token: write
+ # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
+ # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
+ concurrency:
+ group: "pages"
+ cancel-in-progress: false
+ uses: ./.github/workflows/pages.yml
+
jacoco:
needs: test
runs-on: ubuntu-latest
diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
new file mode 100644
index 000000000..5142a863a
--- /dev/null
+++ b/.github/workflows/pages.yml
@@ -0,0 +1,49 @@
+# Simple workflow for deploying static content to GitHub Pages
+name: Deploy static content to Pages
+
+on:
+ workflow_call:
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+jobs:
+ # Single deploy job since we're just deploying
+ deploy:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup Pages
+ uses: actions/configure-pages@v5
+
+ - name: Set up JDK
+ uses: actions/setup-java@v4
+ with:
+ java-version: '8'
+ distribution: 'temurin'
+
+ - name: Setup Gradle
+ uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
+
+ - name: Generate Javadoc
+ run: ./gradlew aggregateJavaDoc
+
+ - name: Generate Coverage
+ run: |-
+ ./gradlew testCodeCoverageReport
+ mv build/reports/jacoco/testCodeCoverageReport/html html/coverage
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ # Upload from html
+ path: './html/'
+
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/build.gradle b/build.gradle
index 82ca2f5fe..eb335ef90 100644
--- a/build.gradle
+++ b/build.gradle
@@ -143,7 +143,7 @@ allprojects {
legacy_version = minecraftVersion
obsolete_version = minecraftVersion
- subprojects.findAll { it.path.contains("bukkit") }.each {dependsOn "${it.path}:test" }
+ subprojects.findAll { it.path.contains("bukkit") }.each { dependsOn "${it.path}:test" }
}
tasks.register('sourcesJar', Jar) {
@@ -184,6 +184,13 @@ allprojects {
}
}
+tasks.register("aggregateJavaDoc") {
+ allprojects.forEach { dependsOn "${it.path}:javadoc" }
+ doLast {
+ JavaDocUtils.aggregateJavaDoc("html/docs", rootProject.name, rootProject.version, TEST_MODULE)
+ }
+}
+
testCodeCoverageReport {
dependsOn test
reports {
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
new file mode 100644
index 000000000..c93826763
--- /dev/null
+++ b/buildSrc/build.gradle
@@ -0,0 +1,23 @@
+plugins {
+ id 'java'
+}
+
+repositories {
+ mavenCentral()
+ maven {
+ name = "Fulminazzo repository"
+ url = "https://repo.fulminazzo.it/releases"
+ }
+}
+
+dependencies {
+ testImplementation platform(libs.junit.platform)
+ testImplementation 'org.junit.jupiter:junit-jupiter'
+ testImplementation 'org.junit.jupiter:junit-jupiter-params'
+
+ testImplementation libs.yamlparser
+}
+
+test {
+ useJUnitPlatform()
+}
\ No newline at end of file
diff --git a/buildSrc/settings.gradle b/buildSrc/settings.gradle
new file mode 100644
index 000000000..7018d7b43
--- /dev/null
+++ b/buildSrc/settings.gradle
@@ -0,0 +1,7 @@
+dependencyResolutionManagement {
+ versionCatalogs {
+ libs {
+ from(files("../gradle/libs.versions.toml"))
+ }
+ }
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/groovy/JavaDocUtils.groovy b/buildSrc/src/main/groovy/JavaDocUtils.groovy
new file mode 100644
index 000000000..331cbdaf9
--- /dev/null
+++ b/buildSrc/src/main/groovy/JavaDocUtils.groovy
@@ -0,0 +1,158 @@
+import groovy.transform.CompileDynamic
+
+import java.nio.file.Files
+import java.nio.file.StandardCopyOption
+
+/**
+ * A collection of utilities to aggregate Javadocs
+ */
+@CompileDynamic
+class JavaDocUtils {
+ private static final String DOCS_DIR = 'javadoc'
+ private static final String[] IGNORE_DIRS = [ 'main', 'java', 'groovy', 'html', 'src', 'buildSrc', 'resources' ]
+ private static final String[] RESOURCES = [ 'index.html', 'javadoc-stylesheet.css' ]
+ private static final String MODULE_PLACEHOLDER = '%modules%'
+ private static final String MODULE_FORMAT_FILE = 'module-format.html'
+
+ /**
+ * Aggregates the javadoc of all the root and subprojects into a single directory
+ *
+ * @param output the output directory
+ * @param ignoreDirs the directories (modules) to be ignore
+ */
+ static aggregateJavaDoc(String output, String name, String version, String... ignoreDirs) {
+ def current = new File(System.getProperty('user.dir'))
+ if (current.name == 'buildSrc') current = current.parentFile
+ def outputDir = new File(current, output)
+
+ if (outputDir.exists() && !outputDir.deleteDir())
+ throw new IllegalStateException("Could not delete previous directory ${output}")
+ if (!outputDir.mkdirs()) throw new IllegalStateException("Could not create directory ${output}")
+
+ aggregateJavaDocRec(current, outputDir, ignoreDirs)
+ createModulesPage(name, version, outputDir)
+ }
+
+ /**
+ * Copies the given file.
+ *
+ * @param src the source (copied) file
+ * @param dst the destination (copy) file
+ */
+ static copyDirectory(File src, File dst) {
+ if (src.directory) {
+ def files = src.listFiles()
+ dst.mkdirs()
+ if (files != null)
+ files*.name.each { copyDirectory(new File(src, it), new File(dst, it)) }
+ } else Files.copy(src.toPath(), dst.toPath(), StandardCopyOption.REPLACE_EXISTING)
+ }
+
+ /**
+ * Finds the most common path between the two given files
+ *
+ * @param file1 the first file
+ * @param file2 the second file
+ * @return the common path
+ */
+ static commonPath(File file1, File file2) {
+ def path1 = file1.absolutePath
+ def path2 = file2.absolutePath
+ def result = ''
+
+ for (i in 0..Math.min(path1.length(), path2.length())) {
+ def c1 = path1[i]
+ def c2 = path2[i]
+ if (c1 != c2) break
+ else result += c1
+ }
+
+ return result
+ }
+
+ /**
+ * Gets the given resource.
+ *
+ * @param name the name
+ * @return the resource
+ */
+ static getResource(String name) {
+ while (name.startsWith('/')) name = name.substring(1)
+ def resource = JavaDocUtils.getResourceAsStream(name)
+ if (resource == null) resource = JavaDocUtils.getResourceAsStream("/${name}")
+ if (resource == null) throw new IllegalArgumentException("Could not find resource '${name}'")
+ return resource
+ }
+
+ private static aggregateJavaDocRec(File current, File output, String... ignoreDirs) {
+ if (!current.directory) return
+
+ def files = current.listFiles()
+ if (files == null)
+ throw new IllegalArgumentException("Could not list files of ${current.path}")
+
+ files.findAll { f -> f.directory }
+ .findAll { f -> !IGNORE_DIRS.any { d -> d == f.name } }
+ .findAll { f -> !ignoreDirs.any { d -> d == f.name } }
+ .each { f ->
+ if (f.name == DOCS_DIR) {
+ def dest = getDestinationFromModule(output, f)
+ def out = new File(output, dest)
+ copyDirectory(f, out)
+ } else aggregateJavaDocRec(f, output)
+ }
+ }
+
+ private static createModulesPage(String name, String version, File file) {
+ if (!file.directory) return
+ def files = file.listFiles()
+ if (files == null) return
+ if (files.any { it.name.contains('.html') }) return
+
+ RESOURCES.each { parseResource(file, it, name, version, files) }
+
+ files.each { createModulesPage(it.name, version, it) }
+ }
+
+ private static parseResource(File parentFile, String resource, String name, String version, File[] files) {
+ getResource("${resource}").withReader { reader ->
+ new File(parentFile, resource).withWriter { writer ->
+ String line
+ while ((line = reader.readLine()) != null) {
+ line = line.replace('%module_name%', name)
+ .replace('%module_version%', version)
+ if (line.contains(MODULE_PLACEHOLDER))
+ line = parseModulesPlaceholder(line, files)
+ writer.write(line)
+ writer.write('\n')
+ }
+ }
+ }
+ }
+
+ private static parseModulesPlaceholder(String line, File[] files) {
+ String output = ''
+ files*.name.each { n ->
+ getResource("/${MODULE_FORMAT_FILE}").withReader { r ->
+ String l
+ while ((l = r.readLine()) != null)
+ output += l.replace('%submodule_name%', n)
+ .replace('%submodule_path%', "${n}${File.separator}index.html")
+ }
+ }
+ line.replace(MODULE_PLACEHOLDER, output)
+ }
+
+ private static getDestinationFromModule(File output, File file) {
+ def parent = new File(commonPath(output, file))
+ def module = new File(file, '')
+ while (module.parentFile.parentFile != parent)
+ module = module.parentFile
+ def moduleName = module.name
+
+ def dst = "${module.parentFile.name}"
+ if (moduleName != 'build') dst += "${File.separator}${module.name}"
+ return dst
+ }
+
+}
diff --git a/buildSrc/src/main/resources/index.html b/buildSrc/src/main/resources/index.html
new file mode 100644
index 000000000..7a55e1a92
--- /dev/null
+++ b/buildSrc/src/main/resources/index.html
@@ -0,0 +1,79 @@
+
+
+
+ Overview (%module_name% %module_version% API)
+
+
+
+
+
+
+ - Overview
+ - Package
+ - Class
+ - Tree
+ - Deprecated
+ - Index
+ - Help
+
+
+
+
+
+
+
+
+
+ Submodules
+
+ Submodule |
+ Description |
+
+
+ %modules%
+
+
+
+
+
+
+
+ - Overview
+ - Package
+ - Class
+ - Tree
+ - Deprecated
+ - Index
+ - Help
+
+
+
+
+
+
diff --git a/buildSrc/src/main/resources/javadoc-stylesheet.css b/buildSrc/src/main/resources/javadoc-stylesheet.css
new file mode 100644
index 000000000..854f2694f
--- /dev/null
+++ b/buildSrc/src/main/resources/javadoc-stylesheet.css
@@ -0,0 +1,668 @@
+/* Javadoc style sheet */
+@import url('resources/fonts/dejavu.css');
+
+body {
+ background-color: #ffffff;
+ color: #353833;
+ font-family: 'DejaVu Sans', Arial, Helvetica, sans-serif;
+ font-size: 14px;
+ margin: 0;
+}
+
+a:link, a:visited {
+ text-decoration: none;
+ color: #4A6782;
+}
+
+a:hover, a:focus {
+ text-decoration: none;
+ color: #bb7a2a;
+}
+
+a:active {
+ text-decoration: none;
+ color: #4A6782;
+}
+
+a[name] {
+ color: #353833;
+}
+
+a[name]:hover {
+ text-decoration: none;
+ color: #353833;
+}
+
+pre {
+ font-family: 'DejaVu Sans Mono', monospace;
+ font-size: 14px;
+}
+
+h1 {
+ font-size: 20px;
+}
+
+h2 {
+ font-size: 18px;
+}
+
+h3 {
+ font-size: 16px;
+ font-style: italic;
+}
+
+h4 {
+ font-size: 13px;
+}
+
+h5 {
+ font-size: 12px;
+}
+
+h6 {
+ font-size: 11px;
+}
+
+ul {
+ list-style-type: disc;
+}
+
+code, tt {
+ font-family: 'DejaVu Sans Mono', monospace;
+ font-size: 14px;
+ padding-top: 4px;
+ margin-top: 8px;
+ line-height: 1.4em;
+}
+
+dt code {
+ font-family: 'DejaVu Sans Mono', monospace;
+ font-size: 14px;
+ padding-top: 4px;
+}
+
+table tr td dt code {
+ font-family: 'DejaVu Sans Mono', monospace;
+ font-size: 14px;
+ vertical-align: top;
+ padding-top: 4px;
+}
+
+sup {
+ font-size: 8px;
+}
+
+/*
+Document title and Copyright styles
+*/
+.clear {
+ clear: both;
+ height: 0;
+ overflow: hidden;
+}
+
+.about-language {
+ float: right;
+ padding: 0 21px;
+ font-size: 11px;
+ z-index: 200;
+ margin-top: -9px;
+}
+
+.legal-copy {
+ margin-left: .5em;
+}
+
+.bar a, .bar a:link, .bar a:visited, .bar a:active {
+ color: #FFFFFF;
+ text-decoration: none;
+}
+
+.bar a:hover, .bar a:focus {
+ color: #bb7a2a;
+}
+
+.tab {
+ background-color: #0066FF;
+ color: #ffffff;
+ padding: 8px;
+ width: 5em;
+ font-weight: bold;
+}
+
+/*
+Navigation bar styles
+*/
+.bar {
+ background-color: #4D7A97;
+ color: #FFFFFF;
+ padding: .8em .5em .4em .8em;
+ height: auto;
+ font-size: 11px;
+ margin: 0;
+}
+
+.top-nav {
+ background-color: #4D7A97;
+ color: #FFFFFF;
+ float: left;
+ padding: 0;
+ width: 100%;
+ clear: right;
+ height: 2.8em;
+ padding-top: 10px;
+ overflow: hidden;
+ font-size: 12px;
+}
+
+.bottom-nav {
+ margin-top: 10px;
+ background-color: #4D7A97;
+ color: #FFFFFF;
+ float: left;
+ padding: 0;
+ width: 100%;
+ clear: right;
+ height: 2.8em;
+ padding-top: 10px;
+ overflow: hidden;
+ font-size: 12px;
+}
+
+.sub-nav {
+ background-color: #dee3e9;
+ float: left;
+ width: 100%;
+ overflow: hidden;
+ font-size: 12px;
+}
+
+.sub-nav div {
+ clear: left;
+ float: left;
+ padding: 0 0 5px 6px;
+ text-transform: uppercase;
+}
+
+ul.nav-list, ul.sub-nav-list {
+ float: left;
+ margin: 0 25px 0 0;
+ padding: 0;
+}
+
+ul.nav-list li{
+ list-style: none;
+ float: left;
+ padding: 5px 6px;
+ text-transform: uppercase;
+}
+
+ul.sub-nav-list li{
+ list-style: none;
+ float: left;
+}
+
+.top-nav a:link, .top-nav a:active, .top-nav a:visited, .bottom-nav a:link, .bottom-nav a:active, .bottom-nav a:visited {
+ color: #FFFFFF;
+ text-decoration: none;
+ text-transform: uppercase;
+}
+
+.top-nav a:hover, .bottom-nav a:hover {
+ text-decoration: none;
+ color: #bb7a2a;
+ text-transform: uppercase;
+}
+
+.nav-bar-cell-1-rev {
+ background-color: #F8981D;
+ color: #253441;
+ margin: auto 5px;
+}
+
+.skip-nav {
+ position: absolute;
+ top: auto;
+ left: -9999px;
+ overflow: hidden;
+}
+
+/*
+Page header and footer styles
+*/
+.header, .footer {
+ clear: both;
+ margin: 0 20px;
+ padding: 5px 0 0;
+}
+
+.index-header {
+ margin: 10px;
+ position: relative;
+}
+
+.index-header span{
+ margin-right: 15px;
+}
+
+.index-header h1 {
+ font-size: 13px;
+}
+
+.title {
+ color: #2c4557;
+ margin: 10px 0;
+}
+
+.sub-title {
+ margin: 5px 0 0 0;
+}
+
+.header ul {
+ margin: 0 0 15px 0;
+ padding: 0;
+}
+
+.footer ul {
+ margin: 20px 0 5px 0;
+}
+
+.header ul li, .footer ul li {
+ list-style: none;
+ font-size: 13px;
+}
+
+/*
+Heading styles
+*/
+div.details ul.block-list ul.block-list ul.block-list li.block-list h4, div.details ul.block-list ul.block-list ul.block-list-last li.block-list h4 {
+ background-color: #dee3e9;
+ border: 1px solid #d0d9e0;
+ margin: 0 0 6px -8px;
+ padding: 7px 5px;
+}
+
+ul.block-list ul.block-list ul.block-list li.block-list h3 {
+ background-color: #dee3e9;
+ border: 1px solid #d0d9e0;
+ margin: 0 0 6px -8px;
+ padding: 7px 5px;
+}
+
+ul.block-list ul.block-list li.block-list h3 {
+ padding: 0;
+ margin: 15px 0;
+}
+
+ul.block-list li.block-list h2 {
+ padding: 0 0 20px 0;
+}
+
+/*
+Page layout container styles
+*/
+.content-container, .source-container, .class-use-container, .serialized-form-container, .constant-values-container {
+ clear: both;
+ padding: 10px 20px;
+ position: relative;
+}
+
+.index-container {
+ margin: 10px;
+ position: relative;
+ font-size: 12px;
+}
+
+.index-container h2 {
+ font-size: 13px;
+ padding: 0 0 3px 0;
+}
+
+.index-container ul {
+ margin: 0;
+ padding: 0;
+}
+
+.index-container ul li {
+ list-style: none;
+ padding-top: 2px;
+}
+
+.content-container .description dl dt, .content-container .details dl dt, .serialized-form-container dl dt {
+ font-size: 12px;
+ font-weight: bold;
+ margin: 10px 0 0 0;
+ color: #4E4E4E;
+}
+
+.content-container .description dl dd, .content-container .details dl dd, .serialized-form-container dl dd {
+ margin: 5px 0 10px 0;
+ font-size: 14px;
+ font-family: 'DejaVu Sans Mono',monospace;
+}
+
+.serialized-form-container dl.name-value dt {
+ margin-left: 1px;
+ font-size: 1.1em;
+ display: inline;
+ font-weight: bold;
+}
+
+.serialized-form-container dl.name-value dd {
+ margin: 0 0 0 1px;
+ font-size: 1.1em;
+ display: inline;
+}
+
+/*
+List styles
+*/
+ul.horizontal li {
+ display: inline;
+ font-size: 0.9em;
+}
+
+ul.inheritance {
+ margin: 0;
+ padding: 0;
+}
+
+ul.inheritance li {
+ display: inline;
+ list-style: none;
+}
+
+ul.inheritance li ul.inheritance {
+ margin-left: 15px;
+ padding-left: 15px;
+ padding-top: 1px;
+}
+
+ul.block-list, ul.block-list-last {
+ margin: 10px 0 10px 0;
+ padding: 0;
+}
+
+ul.block-list li.block-list, ul.block-list-last li.block-list {
+ list-style: none;
+ margin-bottom: 15px;
+ line-height: 1.4;
+}
+
+ul.block-list ul.block-list li.block-list, ul.block-list ul.block-list-last li.block-list {
+ padding: 0 20px 5px 10px;
+ border: 1px solid #ededed;
+ background-color: #f8f8f8;
+}
+
+ul.block-list ul.block-list ul.block-list li.block-list, ul.block-list ul.block-list ul.block-list-last li.block-list {
+ padding: 0 0 5px 8px;
+ background-color: #ffffff;
+ border: none;
+}
+
+ul.block-list ul.block-list ul.block-list ul.block-list li.block-list {
+ margin-left: 0;
+ padding-left: 0;
+ padding-bottom: 15px;
+ border: none;
+}
+
+ul.block-list ul.block-list ul.block-list ul.block-list li.block-list-last {
+ list-style: none;
+ border-bottom: none;
+ padding-bottom: 0;
+}
+
+table tr td dl, table tr td dl dt, table tr td dl dd {
+ margin-top: 0;
+ margin-bottom: 1px;
+}
+
+/*
+Table styles
+*/
+.overview-summary, .member-summary, .type-summary, .use-summary, .constants-summary, .deprecated-summary {
+ width: 100%;
+ border-left: 1px solid #EEE;
+ border-right: 1px solid #EEE;
+ border-bottom: 1px solid #EEE;
+}
+
+.overview-summary, .member-summary {
+ padding: 0;
+}
+
+.overview-summary caption, .member-summary caption, .type-summary caption,
+.use-summary caption, .constants-summary caption, .deprecated-summary caption {
+ position: relative;
+ text-align: left;
+ background-repeat: no-repeat;
+ color: #253441;
+ font-weight: bold;
+ clear: none;
+ overflow: hidden;
+ padding: 0;
+ padding-top: 10px;
+ padding-left: 1px;
+ margin: 0;
+ white-space: pre;
+}
+
+.overview-summary caption a:link, .member-summary caption a:link, .type-summary caption a:link,
+.use-summary caption a:link, .constants-summary caption a:link, .deprecated-summary caption a:link,
+.overview-summary caption a:hover, .member-summary caption a:hover, .type-summary caption a:hover,
+.use-summary caption a:hover, .constants-summary caption a:hover, .deprecated-summary caption a:hover,
+.overview-summary caption a:active, .member-summary caption a:active, .type-summary caption a:active,
+.use-summary caption a:active, .constants-summary caption a:active, .deprecated-summary caption a:active,
+.overview-summary caption a:visited, .member-summary caption a:visited, .type-summary caption a:visited,
+.use-summary caption a:visited, .constants-summary caption a:visited, .deprecated-summary caption a:visited {
+ color: #FFFFFF;
+}
+
+.overview-summary caption span, .member-summary caption span, .type-summary caption span,
+.use-summary caption span, .constants-summary caption span, .deprecated-summary caption span {
+ white-space: nowrap;
+ padding-top: 5px;
+ padding-left: 12px;
+ padding-right: 12px;
+ padding-bottom: 7px;
+ display: inline-block;
+ float: left;
+ background-color: #F8981D;
+ border: none;
+ height: 16px;
+}
+
+.member-summary caption span.active-table-tab span {
+ white-space: nowrap;
+ padding-top: 5px;
+ padding-left: 12px;
+ padding-right: 12px;
+ margin-right: 3px;
+ display: inline-block;
+ float: left;
+ background-color: #F8981D;
+ height: 16px;
+}
+
+.member-summary caption span.table-tab span {
+ white-space: nowrap;
+ padding-top: 5px;
+ padding-left: 12px;
+ padding-right: 12px;
+ margin-right: 3px;
+ display: inline-block;
+ float: left;
+ background-color: #4D7A97;
+ height: 16px;
+}
+
+.member-summary caption span.table-tab, .member-summary caption span.active-table-tab {
+ padding-top: 0;
+ padding-left: 0;
+ padding-right: 0;
+ background-image: none;
+ float: none;
+ display: inline;
+}
+
+.overview-summary .tab-end, .member-summary .tab-end, .type-summary .tab-end,
+.use-summary .tab-end, .constants-summary .tab-end, .deprecated-summary .tab-end {
+ display: none;
+ width: 5px;
+ position: relative;
+ float: left;
+ background-color: #F8981D;
+}
+
+.member-summary .active-table-tab .tab-end {
+ display: none;
+ width: 5px;
+ margin-right: 3px;
+ position: relative;
+ float: left;
+ background-color: #F8981D;
+}
+
+.member-summary .table-tab .tab-end {
+ display: none;
+ width: 5px;
+ margin-right: 3px;
+ position: relative;
+ background-color: #4D7A97;
+ float: left;
+
+}
+
+.overview-summary td, .member-summary td, .type-summary td,
+.use-summary td, .constants-summary td, .deprecated-summary td {
+ text-align: left;
+ padding: 0 0 12px 10px;
+}
+
+th.col-one, th.col-first, th.col-last, .use-summary th, .constants-summary th,
+td.col-one, td.col-first, td.col-last, .use-summary td, .constants-summary td{
+ vertical-align: top;
+ padding-right: 0;
+ padding-top: 8px;
+ padding-bottom: 3px;
+}
+
+th.col-first, th.col-last, th.col-one, .constants-summary th {
+ background: #dee3e9;
+ text-align: left;
+ padding: 8px 3px 3px 7px;
+}
+
+td.col-first, th.col-first {
+ white-space: nowrap;
+ font-size: 13px;
+}
+
+td.col-last, th.col-last {
+ font-size: 13px;
+}
+
+td.col-one, th.col-one {
+ font-size: 13px;
+}
+
+.overview-summary td.col-first, .overview-summary th.col-first,
+.use-summary td.col-first, .use-summary th.col-first,
+.overview-summary td.col-one, .overview-summary th.col-one,
+.member-summary td.col-first, .member-summary th.col-first,
+.member-summary td.col-one, .member-summary th.col-one,
+.type-summary td.col-first{
+ width: 25%;
+ vertical-align: top;
+}
+
+td.col-one a:link, td.col-one a:active, td.col-one a:visited, td.col-one a:hover, td.col-first a:link, td.col-first a:active, td.col-first a:visited, td.col-first a:hover, td.col-last a:link, td.col-last a:active, td.col-last a:visited, td.col-last a:hover, .constant-values-container td a:link, .constant-values-container td a:active, .constant-values-container td a:visited, .constant-values-container td a:hover {
+ font-weight: bold;
+}
+
+.table-sub-heading-color {
+ background-color: #EEEEFF;
+}
+
+.alt-color {
+ background-color: #FFFFFF;
+}
+
+.row-color {
+ background-color: #EEEEEF;
+}
+
+/*
+Content styles
+*/
+.description pre {
+ margin-top: 0;
+}
+
+.deprecated-content {
+ margin: 0;
+ padding: 10px 0;
+}
+
+.doc-summary {
+ padding: 0;
+}
+
+ul.block-list ul.block-list ul.block-list li.block-list h3 {
+ font-style: normal;
+}
+
+div.block {
+ font-size: 14px;
+ font-family: 'DejaVu Serif', Georgia, "Times New Roman", Times, serif;
+}
+
+td.col-last div {
+ padding-top: 0;
+}
+
+
+td.col-last a {
+ padding-bottom: 3px;
+}
+
+/*
+Formatting effect styles
+*/
+.source-line-no {
+ color: green;
+ padding: 0 30px 0 0;
+}
+
+h1.hidden {
+ visibility: hidden;
+ overflow: hidden;
+ font-size: 10px;
+}
+
+.block {
+ display: block;
+ margin: 3px 10px 2px 0;
+ color: #474747;
+}
+
+.deprecated-label, .descframe-type-label, .member-name-label, .member-name-link,
+.override-specify-label, .package-hierarchy-label, .param-label, .return-label,
+.see-label, .simple-tag-label, .throws-label, .type-name-label, .type-name-link {
+ font-weight: bold;
+}
+
+.deprecation-comment, .emphasized-phrase, .interface-name {
+ font-style: italic;
+}
+
+div.block div.block span.deprecation-comment, div.block div.block span.emphasized-phrase,
+div.block div.block span.interface-name {
+ font-style: normal;
+}
+
+div.content-container ul.block-list li.block-list h2{
+ padding-bottom: 0;
+}
diff --git a/buildSrc/src/main/resources/module-format.html b/buildSrc/src/main/resources/module-format.html
new file mode 100644
index 000000000..ad433420c
--- /dev/null
+++ b/buildSrc/src/main/resources/module-format.html
@@ -0,0 +1,4 @@
+
+ %submodule_name% |
+ |
+
\ No newline at end of file
diff --git a/buildSrc/src/test/java/JavaDocUtilsTest.java b/buildSrc/src/test/java/JavaDocUtilsTest.java
new file mode 100644
index 000000000..bdedd090c
--- /dev/null
+++ b/buildSrc/src/test/java/JavaDocUtilsTest.java
@@ -0,0 +1,67 @@
+import it.fulminazzo.yamlparser.utils.FileUtils;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class JavaDocUtilsTest {
+
+ @Test
+ void testCopy() throws IOException {
+ File parent = new File("build/resources/test");
+ if (parent.exists()) FileUtils.deleteFolder(parent);
+ FileUtils.createFolder(parent);
+ // Setup
+ File dir1 = new File(parent, "dir1");
+ dir1.mkdir();
+ File file1 = new File(dir1, "file1");
+ file1.createNewFile();
+ FileUtils.writeToFile(file1, "Hello");
+ File file2 = new File(dir1, "file2");
+ file2.createNewFile();
+ FileUtils.writeToFile(file2, "World");
+
+ // Copy
+ File dir2 = new File(parent, "dir2");
+ JavaDocUtils.copyDirectory(dir1, dir2);
+
+ // Verify
+ assertTrue(dir2.isDirectory(), String.format("No directory %s", dir2.getAbsolutePath()));
+
+ file1 = new File(dir2, "file1");
+ assertTrue(file1.exists(), String.format("No file %s", file1.getAbsolutePath()));
+ assertEquals("Hello", FileUtils.readFileToString(file1));
+
+ file2 = new File(dir2, "file2");
+ assertTrue(file2.exists(), String.format("No file %s", file2.getAbsolutePath()));
+ assertEquals("World", FileUtils.readFileToString(file2));
+ }
+
+ @Test
+ void testCommonPaths() {
+ String expected = System.getProperty("user.dir") + "/this/path/is/expected/";
+ File path1 = new File(expected, "this/is/not");
+ File path2 = new File(expected, "me/neither");
+
+ Object actual = JavaDocUtils.commonPath(path1, path2);
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ void testGetResource() throws IOException {
+ InputStream stream = (InputStream) JavaDocUtils.getResource("mock.txt");
+ assertNotNull(stream);
+ StringBuilder builder = new StringBuilder();
+ while (stream.available() > 0) builder.append((char) stream.read());
+ assertEquals("Hello world", builder.toString());
+ }
+
+ @Test
+ void testInvalidResource() {
+ assertThrowsExactly(IllegalArgumentException.class, () -> JavaDocUtils.getResource("invalid"));
+ }
+
+}
\ No newline at end of file
diff --git a/buildSrc/src/test/resources/mock.txt b/buildSrc/src/test/resources/mock.txt
new file mode 100644
index 000000000..70c379b63
--- /dev/null
+++ b/buildSrc/src/test/resources/mock.txt
@@ -0,0 +1 @@
+Hello world
\ No newline at end of file