| 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" |
| |
| } |
| } |