diff --git a/gradle/release.gradle b/gradle/release.gradle index 7a8d82a..706e526 100644 --- a/gradle/release.gradle +++ b/gradle/release.gradle @@ -12,6 +12,62 @@ def hash = { File input -> MessageDigest.getInstance('SHA-256').digest(input.bytes).encodeHex().toString().toUpperCase() } +// Java 8's doclet leaves enum siblings in an arbitrary order. Sort only those +// leaf rows; the rest of the generated hierarchy stays exactly as it was. +def canonicalEnumHierarchy = { String html -> + List lines = html.readLines() + int start = 0 + + while (start < lines.size()) { + int end = start + while (end < lines.size() && lines[end] ==~ /\s*
  • .*title="enum in [^"]+".*<\/li>\s*/) { + end++ + } + if (end > start) { + Collections.sort(lines.subList(start, end)) + start = end + } else { + start++ + } + } + + lines.join('\n') + '\n' +} + +tasks.named('javadoc', Javadoc) { + doLast { + project.fileTree(destinationDir).matching { include '**/*-tree.html' }.each { File tree -> + tree.setText(canonicalEnumHierarchy(tree.getText('UTF-8')), 'UTF-8') + } + } +} + +tasks.register('verifyJavadocOrdering') { + group = 'verification' + description = 'Checks that Java 8 enum documentation has a stable order.' + dependsOn tasks.named('javadoc') + doLast { + String first = '
  • A A
  • ' + String second = '
  • B B
  • ' + String ordered = "\n" + String reversed = "\n" + if (canonicalEnumHierarchy(reversed) != ordered + || canonicalEnumHierarchy(ordered) != ordered) { + throw new GradleException('Enum hierarchy normalization changed content or was not stable') + } + + File output = tasks.named('javadoc', Javadoc).get().destinationDir + def trees = project.fileTree(output).matching { include '**/*-tree.html' } + if (trees.isEmpty()) throw new GradleException('Javadoc did not generate any hierarchy pages') + trees.each { File tree -> + String html = tree.getText('UTF-8') + if (html != canonicalEnumHierarchy(html)) { + throw new GradleException("Unstable enum ordering in ${tree.name}") + } + } + } +} + tasks.register('verifyReleaseConfiguration') { group = 'verification' doLast { @@ -193,4 +249,5 @@ tasks.withType(PublishToMavenRepository).configureEach { tasks.named('check') { dependsOn tasks.named('auditPublicArchives') dependsOn tasks.named('verifyMavenCoordinates') + dependsOn tasks.named('verifyJavadocOrdering') }