blob: 558dea7af1b76c3dd4ddc0371df3ac4cfbc4b7ac [file] [log] [blame]
package org.codehaus.groovy.tools
import groovy.xml.StreamingMarkupBuilder
import java.io.File
import com.thoughtworks.qdox.JavaDocBuilder
import com.thoughtworks.qdox.model.JavaSource
import com.thoughtworks.qdox.model.JavaClass
import com.thoughtworks.qdox.model.JavaMethod
import com.thoughtworks.qdox.model.JavaParameter
import com.thoughtworks.qdox.model.Type
import java.util.*;
/**
* Generate documentation about the methods provided by the Groovy Development Kit
* enhancing the standard JDK classes.
*
* @author Guillaume Laforge, John Wilson
*/
class DocGenerator
{
def sourceFiles = []
File outputFolder
JavaDocBuilder builder
// categorize all groovy methods per core JDK class to which it applies
def jdkEnhancedClasses = [:]
def packages = [:]
def sortedPackages
DocGenerator(sourceFiles, File outputFolder)
{
this.sourceFiles = sourceFiles
this.outputFolder = outputFolder
parse()
}
/**
* Parse the DefaultGroovyMethods class to build a graph representing the structure of the class,
* with its methods, javadoc comments and tags.
*/
private void parse()
{
builder = new JavaDocBuilder()
sourceFiles.each {
println "adding reader for $it"
builder.addSource(it.newReader())
}
def sources = builder.getSources()
def methods = []
sources.each { source ->
def classes = source.getClasses()
classes.each { aClass ->
methods.addAll (aClass.methods as List)
}
}
def start = System.currentTimeMillis();
for (method in methods) {
if (method.isPublic() && method.isStatic()) {
def parameters = method.getParameters()
def jdkClass = parameters[0].getType().toString()
if (jdkClass.startsWith('groovy')) {
// nothing, skip it
}
else if (jdkEnhancedClasses.containsKey(jdkClass)) {
List l = jdkEnhancedClasses[jdkClass];
l.add(method)
}
else
jdkEnhancedClasses[jdkClass] = [method]
}
}
jdkEnhancedClasses.keySet().each { className ->
def thePackage = className.contains(".") ? className.replaceFirst(/\.[^\.]*$/, "") : ""
if (!packages.containsKey(thePackage)) {
packages[thePackage] = []
}
packages[thePackage] << className
}
sortedPackages = new TreeSet(packages.keySet())
}
/**
* Builds an HTML page from the structure of DefaultGroovyMethods.
*/
def generateNew() {
def engine = new groovy.text.SimpleTemplateEngine()
// the index
def templateIndex = createTemplate(engine, 'index.html')
def out = new File(outputFolder, 'index.html')
def binding = [packages: sortedPackages]
out.withWriter {
it << templateIndex.make(binding)
}
// the overview
def templateOverview = createTemplate(engine, 'overview-summary.html')
out = new File(outputFolder, 'overview-summary.html')
binding = [packages: sortedPackages]
out.withWriter {
it << templateOverview.make(binding)
}
def templateOverviewFrame = createTemplate(engine, 'template.overview-frame.html')
out = new File(outputFolder, 'overview-frame.html')
binding = [packages: sortedPackages]
out.withWriter {
it << templateOverviewFrame.make(binding)
}
// the allclasses-frame.html
def templateAllClasses = createTemplate(engine, 'template.allclasses-frame.html')
out = new File(outputFolder, 'allclasses-frame.html')
binding = [classes: jdkEnhancedClasses.keySet().sort { it.replaceAll('.*\\.', '')}]
out.withWriter {
it << templateAllClasses.make(binding)
}
// the package-frame.html for each package
def templatePackageFrame = createTemplate(engine, 'template.package-frame.html')
packages.each { curPackage, packageClasses ->
def packageName = curPackage ? curPackage : "primitive-types"
generatePackageFrame(templatePackageFrame, packageName, packageClasses)
}
// the class.html for each class
def templateClass = createTemplate(engine, 'template.class.html')
packages.each { curPackage, packageClasses ->
def packageName = curPackage ? curPackage : "primitive-types"
packageClasses.each {
generateClassDetails(templateClass, packageName, it)
}
}
}
private generateClassDetails(template, curPackage, aClass)
{
def packagePath = generatePackagePath(curPackage)
def dir = new File(outputFolder, packagePath)
dir.mkdirs()
def out = new File(dir, aClass.replaceAll('.*\\.', '') + '.html')
def listOfMethods = jdkEnhancedClasses[aClass].sort{ it.name }
def methods = []
listOfMethods.each { method ->
def parameters = method.getTagsByName("param").collect { [name: it.value.replaceAll(' .*', ''), comment: it.value.replaceAll('^\\w*', '')]}
if (parameters)
parameters.remove(0) // method is static, first arg is the "real this"
def returnType = getReturnType(method)
def methodInfo = [name: method.name,
comment: getComment(method),
shortComment: getComment(method).replaceAll('\\..*', ''),
returnComment: method.getTagByName("return")?.getValue() ?: '',
returnTypeDocUrl: getDocUrl(returnType),
parametersSignature: getParametersDecl(method),
parametersDocUrl: getParametersDocUrl(method),
parameters: parameters,
isStatic: method.parentClass.name == 'DefaultGroovyStaticMethods']
methods << methodInfo
}
def binding = [className: aClass.replaceAll(/.*\./, ''),
packageName: curPackage,
methods: methods]
out.withWriter {
it << template.make(binding)
}
}
private String getParametersDocUrl(method)
{
getParameters(method).collect{"${getDocUrl(it.type.toString())} ${it.getName()}" }.join(", ")
}
private String getDocUrl(type)
{
if (!type.contains('.'))
return type
def shortClassName = type.replaceAll(".*\\.", "")
def packageName = type[0..(-shortClassName.size()-2)]
def apiBaseUrl, title
if (type.startsWith("groovy")) {
apiBaseUrl = "http://groovy.codehaus.org/api/"
title = "Groovy class in $packageName"
}
else {
apiBaseUrl = "http://java.sun.com/j2se/1.4.2/docs/api/"
title = "JDK class in $packageName"
}
def url = apiBaseUrl + type.replaceAll("\\.", "/") + '.html'
return "<a href='$url' title='$title'>$shortClassName</a>"
}
private generatePackagePath(curPackage)
{
def fileSep = File.separator
// need to escape separator on windows for regex's sake
if (fileSep == '\\') fileSep *= 2
return curPackage.replaceAll('\\.', fileSep)
}
private generatePackageFrame(templatePackageFrame, curPackage, packageClasses)
{
def packagePath = generatePackagePath(curPackage)
def dir = new File(outputFolder, packagePath)
dir.mkdirs()
def out = new File(dir, 'package-frame.html')
def binding = [classes: packageClasses.sort().collect { it.replaceAll(/.*\./, '')},
packageName: curPackage]
out.withWriter {
it << templatePackageFrame.make(binding)
}
}
def createTemplate(templateEngine, resourceFile)
{
def resourceUrl = getClass().getResource(resourceFile)
return templateEngine.createTemplate(resourceUrl.text)
}
/**
* Retrieves a String representing the return type
*/
private getReturnType(method)
{
def returnType = method.getReturns()
if (returnType != null) {
return returnType.toString()
} else {
return ""
}
}
/**
* Retrieve a String representing the declaration of the parameters of the method passed as parameter.
*
* @param method a method
* @return the declaration of the method (long version)
*/
private getParametersDecl(method)
{
getParameters(method).collect{ "${it.getType()} ${it.getName()}" }.join(", ")
}
/**
* Retrieve the parameters of the method.
*
* @param method a method
* @return a list of parameters without the first one
*/
private getParameters(method)
{
if (method.getParameters().size() > 1)
return method.getParameters().toList()[1..-1]
else
return []
}
/**
* Retrieve the JavaDoc comment associated with the method passed as parameter.
*
* @param method a method
* @return the JavaDoc comment associated with this method
*/
private getComment(method)
{
def ans = method.getComment()
if (ans == null) return ""
return ans
}
/**
* Main entry point.
*/
static void main(args)
{
def defaultGroovyMethodSource = new File("src/main/org/codehaus/groovy/runtime/DefaultGroovyMethods.java")
def defaultGroovyStaticMethodSource = new File("src/main/org/codehaus/groovy/runtime/DefaultGroovyStaticMethods.java")
def outFolder = new File("target/html/groovy-jdk")
outFolder.mkdirs()
def start = System.currentTimeMillis();
def docGen = new DocGenerator([defaultGroovyMethodSource, defaultGroovyStaticMethodSource], outFolder)
docGen.generateNew()
def end = System.currentTimeMillis();
println "Done. in ${end - start} millis"
}
}