JavaDoc
doc-files in JavaDoc
You can provide additional files for the Javadoc documentation of your project (and only to the documentation) by creating a folder with the name doc-files in the respective package folder and copying the files there.
From the Javadoc in your source file, your refer to these files like this: <a href="doc-files/docFile.xyz">Link to File</a>.
Although the javadoc tool itself still supports this, it does not work properly when used with Maven or Gradle.
Maven is using a special copy operation that takes care of the additional files, but for Gradle I had to create my own solution1.
The first approach looked like this:
tasks.named( 'javadoc' ) {
mustRunAfter "jar"
//---* Configure Javadoc *-------------------------------------------------
…
doLast {
…
copy {
from "$projectDir/src/main/java"
into "$destinationDir"
include "**/doc-files/*"
}
}
}
It worked for projects that do not use JPMS2. The reason is that for a module the HTML files for the documentation are kept in a folder that is named after the module.
To illustrate that, the directory trees for a project with just the class foo.bar.Main, first without a module, then with the module my.module:
| Without module | With module |
|---|---|
└ javadoc ├ index-files ├ legal ├ foo │ └ bar │ ├ class-use │ └ Main.html ⋮ ├ allclasses-index.html ⋮ └ type-search-index.js |
└ javadoc ├ index-files ├ legal ├ my.module │ ├ module-summary.html │ └ foo │ └ bar │ ├ class-use │ └ Main.html ⋮ ├ allclasses-index.html ⋮ └ type-search-index.js |
Unfortunately, there is no method in Gradle to get the Java module name; ok, the convention is to name the module after the project’s main package, and I have the convention to name the project after the module … but at the same time, I have multi-module (this time Gradle modules) projects where the projects have modules or not (so for example, annotation processors do not work well when implemented as a module …).
So I used this method to obtain the module name3:
/**
* Determines the module name of a Java 9 (Jigsaw) module
* (don't mix this up with a module as it is defined by Gradle
* itself!)}.
* The implementation will search the source path for a file with the
* name "module-info.java" and gets the name of the module from there.
* Call this function from your build.gradle file like this:
*
* var moduleName = Tools.obtainModuleName( project.sourceSets.main )
*
* @param sourceSet The source set for the project.
* @return An instance of
* {@link Optional}
* that holds the name of the module.
*/
public static final Optional<String> obtainModuleName( final SourceSet sourceSet )
{
Optional<String> retValue = Optional.empty();
final var directorySet = sourceSet.getAllJava();
final var moduleDefinitionFile = directorySet.getFiles()
.stream()
.filter( File::exists )
.filter( File::isFile )
.filter( f -> f.getName().equals( MODULE_DEFINITION ) )
.findFirst()
.map( File::toPath );
if( moduleDefinitionFile.isPresent() )
{
try( final var lines = lines( moduleDefinitionFile.get() ) )
{
final var contents = lines.collect( joining() );
final var pattern = compile( ".*module\\s+(\\S+)\\s*\\{.*" );
final var matcher = pattern.matcher( contents );
if( matcher.matches() )
{
retValue = Optional.ofNullable( matcher.group( 1 ) );
}
}
catch( final IOException e )
{
throw new Error( e );
}
}
//---* Done *----------------------------------------------------------
return retValue;
} // obtainModuleName()
It lives in a class named org.tquadrat.build.Tools in my case; that allows me to write the copy command like this now:
tasks.named( 'javadoc' ) {
mustRunAfter "jar"
//---* Configure Javadoc *-------------------------------------------------
…
doLast {
…
var moduleName = org.tquadrat.build.Tools.obtainModuleName( project.sourceSets.main )
var targetDir = moduleName.map( "/%s"::formatted ).orElse( "" )
copy {
from "$projectDir/src/main/java"
into "$destinationDir/javadoc$targetDir"
include "**/doc-files/*"
}
}
}
Integration of a Logo into the Documentation
I want to have a logo on each page of the Javadoc documentation for my projects, so that it looks like this:

To achieve this, my Javadoc task in Gradle looks like this:
tasks.named( 'javadoc' ) {
String topValue = """
<div style="overflow:auto;">
<img src="{@docRoot}/resources/logo.jpg"
alt="logo"
style="float:right;">
<p style="font-family:sans-serif;font-size:40px;font-weight:bold;padding-left:30px;">My Library</p>
</div>
"""
options {
…
addStringOption( 'top', topValue )
…
}
doLast {
copy {
from "$projectDir/../resources/javadoc/logo.jpg"
into "$destinationDir/resources"
}
}
}
The HTML code in topValue can be adjusted according to individual needs. Also the source location for the logo can be different.
-
Meanwhile I got a tip that Gradle’s JavaDoc task should work on
project.sourceSets.main.getAllJava()instead of onprojects.sourceSets.main.getAllSource(). If true, it should help if I add all thedoc-filesfolders and their contents to the source set. I need to test this … ↩ -
The Java Platform Module System, introduced through the Jigsaw project in Java 9. ↩
-
Sorry, but my Groovy skills are still … poor, at best. But of course, the method can be implemented in Groovy, too. ↩