Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.grails.doc.gradle

import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.TaskAction
import java.nio.file.*
import java.util.regex.Matcher
import java.util.regex.Pattern

/**
* A task that repairs malformed navigation links in generated Groovydocs.
* Specifically targets 'phantom' links and relative path issues in navigation files
* like overview-summary.html, deprecated-list.html, and help-doc.html.
*/
abstract class FixGroovydocLinksTask extends DefaultTask {

@InputDirectory
abstract DirectoryProperty getApiDocsDir()

@TaskAction
void fixLinks() {
File apiDir = apiDocsDir.get().asFile
if (!apiDir.exists()) {
logger.warn "API documentation directory does not exist: ${apiDir.absolutePath}"
return
}

// Patterns common to Groovydoc navigation failures
Map<Pattern, String> replacements = [
(Pattern.compile(/href='([^']+?)\/deprecated-list\.html'/)): "href='deprecated-list.html'",
(Pattern.compile(/href='([^']+?)\/help-doc\.html'/)): "href='help-doc.html'",
(Pattern.compile(/href='([^']+?)\/index-all\.html'/)): "href='index-all.html'",
(Pattern.compile(/href='([^']+?)\/overview-summary\.html'/)): "href='overview-summary.html'"
]

apiDir.eachFileRecurse { File file ->
if (file.name.endsWith(".html")) {
String content = file.text
boolean changed = false

replacements.each { pattern, replacement ->
Matcher matcher = pattern.matcher(content)
if (matcher.find()) {
content = matcher.replaceAll(replacement)
changed = true
}
}

// Fix specific inner class path issues like Query/Order.Direction.html -> Query.Order.Direction.html
// We only do this if the file actually exists
Pattern innerClassPattern = Pattern.compile(/href='([^']+?)\/([A-Z][A-Za-z0-9_]*?)\/([A-Z][A-Za-z0-9_.]*?\.html)'/)
Matcher innerMatcher = innerClassPattern.matcher(content)
while (innerMatcher.find()) {
String relPath = innerMatcher.group(1)
String outer = innerMatcher.group(2)
String inner = innerMatcher.group(3)

Path currentPath = file.toPath().parent
Path targetPath = currentPath.resolve(relPath).resolve("${outer}.${inner}").normalize()

if (Files.exists(targetPath)) {
String newHref = "href='${relPath}/${outer}.${inner}'"
content = content.replace(innerMatcher.group(0), newHref)
changed = true
}
}

if (changed) {
file.text = content
}
}
}

logger.lifecycle "Groovydoc link repair completed for ${apiDir.absolutePath}"
}
}
37 changes: 27 additions & 10 deletions gradle/docs-dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,10 @@ dependencies {
}

String resolveProjectVersion(String artifact) {
String version = configurations.runtimeClasspath
.resolvedConfiguration
.resolvedArtifacts
.find {
it.moduleVersion.id.name == artifact
}?.moduleVersion?.id?.version
if (!version) {
return null
def component = configurations.runtimeClasspath.incoming.resolutionResult.allComponents.find {
it.moduleVersion?.name == artifact
}
version
return component?.moduleVersion?.version
}

def configureGroovyDoc = tasks.register('configureGroovyDoc') {
Expand All @@ -56,12 +50,35 @@ def configureGroovyDoc = tasks.register('configureGroovyDoc') {
}
def springVersion = resolveProjectVersion('spring-core')
if (springVersion) {
links << [packages: 'org.springframework.core.', href: "https://docs.spring.io/spring-framework/docs/${springVersion}/javadoc-api/"]
links << [packages: 'org.springframework.', href: "https://docs.spring.io/spring-framework/docs/${springVersion}/javadoc-api/"]
}
def springBootVersion = resolveProjectVersion('spring-boot')
if (springBootVersion) {
links << [packages: 'org.springframework.boot.', href: "https://docs.spring.io/spring-boot/docs/${springBootVersion}/api/"]
}
def hibernateVersion = resolveProjectVersion('hibernate-core')
if (hibernateVersion) {
def shortVersion = hibernateVersion.split('\\.').take(2).join('.')
links << [packages: 'org.hibernate.', href: "https://docs.jboss.org/hibernate/orm/${shortVersion}/javadocs/"]
}
def jakartaValidationVersion = resolveProjectVersion('jakarta.validation-api')
if (jakartaValidationVersion) {
links << [packages: 'jakarta.validation.', href: "https://jakarta.ee/specifications/bean-validation/3.0/apidocs/"]
}
def jakartaPersistenceVersion = resolveProjectVersion('jakarta.persistence-api')
if (jakartaPersistenceVersion) {
links << [packages: 'jakarta.persistence.', href: "https://jakarta.ee/specifications/persistence/3.1/apidocs/"]
}
def jakartaServletVersion = resolveProjectVersion('jakarta.servlet-api')
if (jakartaServletVersion) {
links << [packages: 'jakarta.servlet.', href: "https://jakarta.ee/specifications/platform/10/apidocs/"]
}
links << [packages: 'org.grails.datastore.', href: "https://gorm.grails.org/latest/api/"]
links << [packages: 'grails.gorm.', href: "https://gorm.grails.org/latest/api/"]
links << [packages: 'org.grails.gorm.', href: "https://gorm.grails.org/latest/api/"]
links << [packages: 'groovy.', href: "https://docs.groovy-lang.org/latest/html/gapi/"]
links << [packages: 'org.apache.groovy.', href: "https://docs.groovy-lang.org/latest/html/gapi/"]
links << [packages: 'org.codehaus.groovy.', href: "https://docs.groovy-lang.org/latest/html/gapi/"]
if (it.ext.has('groovydocLinks')) {
links.addAll(it.ext.groovydocLinks as List<Map<String, String>>)
}
Expand Down
Loading