| /* |
| * Copyright 2003-2007 the original author or authors. |
| * |
| * Licensed 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 |
| * |
| * http://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.codehaus.groovy.tools.groovydoc; |
| |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Stack; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.codehaus.groovy.antlr.GroovySourceAST; |
| import org.codehaus.groovy.antlr.LineColumn; |
| import org.codehaus.groovy.antlr.SourceBuffer; |
| import org.codehaus.groovy.antlr.parser.GroovyTokenTypes; |
| import org.codehaus.groovy.antlr.treewalker.VisitorAdapter; |
| import org.codehaus.groovy.groovydoc.GroovyConstructorDoc; |
| |
| public class SimpleGroovyClassDocAssembler extends VisitorAdapter { |
| private Stack stack; |
| private Map classDocs; |
| private SimpleGroovyClassDoc currentClassDoc; // todo - stack? |
| private SimpleGroovyConstructorDoc currentConstructorDoc; // todo - stack? |
| private SimpleGroovyMethodDoc currentMethodDoc; // todo - stack? |
| private SourceBuffer sourceBuffer; |
| private String packagePath; |
| private Pattern previousJavaDocCommentPattern; |
| private static final String FS = "/"; |
| |
| public SimpleGroovyClassDocAssembler(String packagePath, String file, SourceBuffer sourceBuffer) { |
| this.sourceBuffer = sourceBuffer; |
| this.packagePath = packagePath; |
| |
| stack = new Stack(); |
| classDocs = new HashMap(); |
| String className = file; |
| if (file != null) { |
| // todo: replace this simple idea of default class name |
| int idx = file.lastIndexOf("."); |
| className = file.substring(0,idx); |
| } |
| currentClassDoc = new SimpleGroovyClassDoc(className); |
| currentClassDoc.setFullPathName(packagePath + FS + className); |
| classDocs.put(currentClassDoc.getFullPathName(),currentClassDoc); |
| |
| previousJavaDocCommentPattern = Pattern.compile("(?s)/\\*\\*(.*?)\\*/"); |
| } |
| |
| public Map getGroovyClassDocs() { |
| postProcessClassDocs(); |
| return classDocs; |
| } |
| |
| // Step through ClassDocs and tie up loose ends |
| private void postProcessClassDocs() { |
| Iterator classDocIterator = classDocs.values().iterator(); |
| while (classDocIterator.hasNext()) { |
| SimpleGroovyClassDoc classDoc = (SimpleGroovyClassDoc) classDocIterator.next(); |
| |
| GroovyConstructorDoc[] constructors = classDoc.constructors(); |
| if (constructors != null && constructors.length == 0) { // add default constructor to doc |
| // name of class for the constructor |
| GroovyConstructorDoc constructorDoc = new SimpleGroovyConstructorDoc(classDoc.name()); |
| // don't forget to tell the class about this default constructor. |
| classDoc.add(constructorDoc); |
| } |
| } |
| } |
| |
| public void visitExtendsClause(GroovySourceAST t,int visit) { |
| if (visit == OPENING_VISIT) { |
| GroovySourceAST superClassNode = t.childOfType(GroovyTokenTypes.IDENT); |
| if (superClassNode != null) { |
| String superClassName = superClassNode.getText(); |
| currentClassDoc.setSuperClassName(superClassName); // un 'packaged' class name |
| } |
| } |
| } |
| |
| public void visitClassDef(GroovySourceAST t,int visit) { |
| if (visit == OPENING_VISIT) { |
| // todo is this correct for java + groovy src? |
| String className = t.childOfType(GroovyTokenTypes.IDENT).getText(); |
| currentClassDoc = (SimpleGroovyClassDoc) classDocs.get(packagePath + FS + className); |
| if (currentClassDoc == null) { |
| currentClassDoc = new SimpleGroovyClassDoc(className); |
| } |
| // comments |
| String commentText = getJavaDocCommentsBeforeNode(t); |
| currentClassDoc.setRawCommentText(commentText); |
| |
| currentClassDoc.setFullPathName(packagePath + FS + currentClassDoc.name()); |
| classDocs.put(currentClassDoc.getFullPathName(), currentClassDoc); |
| } |
| } |
| |
| public void visitCtorIdent(GroovySourceAST t,int visit) { |
| if (visit == OPENING_VISIT) { |
| // now... get relevant values from the AST |
| |
| // name of class for the constructor |
| currentConstructorDoc = new SimpleGroovyConstructorDoc(currentClassDoc.name()); |
| |
| // comments |
| String commentText = getJavaDocCommentsBeforeNode(t); |
| currentConstructorDoc.setRawCommentText(commentText); |
| |
| addParametersTo(currentConstructorDoc, t, visit); |
| |
| // don't forget to tell the class about this constructor. |
| currentClassDoc.add(currentConstructorDoc); |
| } |
| } |
| |
| |
| public void visitMethodDef(GroovySourceAST t, int visit) { |
| if (visit == OPENING_VISIT) { |
| // init |
| |
| // now... get relevant values from the AST |
| |
| // method name |
| String methodName = t.childOfType(GroovyTokenTypes.IDENT).getText(); |
| currentMethodDoc = new SimpleGroovyMethodDoc(methodName); |
| |
| // comments |
| String commentText = getJavaDocCommentsBeforeNode(t); |
| currentMethodDoc.setRawCommentText(commentText); |
| |
| // return type |
| String returnTypeName = getTypeNodeAsText(t.childOfType(GroovyTokenTypes.TYPE),"def"); |
| SimpleGroovyType returnType = new SimpleGroovyType(returnTypeName); // todo !!! |
| currentMethodDoc.setReturnType(returnType); |
| |
| addParametersTo(currentMethodDoc, t, visit); |
| |
| // don't forget to tell the class about this method so carefully constructed. |
| currentClassDoc.add(currentMethodDoc); |
| } |
| } |
| |
| // todo - If no comment before node, then get comment from same node on parent class - ouch! |
| |
| private String getJavaDocCommentsBeforeNode(GroovySourceAST t) { |
| String returnValue = ""; |
| |
| String text = sourceBuffer.getSnippet(new LineColumn(1,1), new LineColumn(t.getLine(), t.getColumn())); |
| if (text != null) { |
| int openBlockIndex = text.lastIndexOf("{"); |
| int closingBlockIndex = text.lastIndexOf("}"); |
| int lastBlockIndex = Math.max(openBlockIndex, closingBlockIndex); |
| if (lastBlockIndex > 0) { |
| text = text.substring(lastBlockIndex); |
| } |
| |
| Matcher m = previousJavaDocCommentPattern.matcher(text); |
| if (m.find()) { |
| returnValue = m.group(1); |
| } |
| } |
| |
| return returnValue; |
| } |
| |
| private String getText(GroovySourceAST node) { |
| String returnValue = null; |
| if (node != null) { |
| returnValue = node.getText(); |
| } |
| return returnValue; |
| } |
| |
| private String getTypeNodeAsText(GroovySourceAST typeNode, String defaultText) { |
| String returnValue = defaultText; |
| if (typeNode != null && |
| typeNode.getType() == GroovyTokenTypes.TYPE && |
| typeNode.getNumberOfChildren() > 0) { |
| GroovySourceAST child = (GroovySourceAST) typeNode.getFirstChild(); // assume type has only one child // todo type of "foo.bar.Wibble" |
| switch (child.getType()) { |
| // literals |
| case GroovyTokenTypes.LITERAL_boolean: returnValue = "boolean"; break; |
| case GroovyTokenTypes.LITERAL_byte: returnValue = "byte"; break; |
| case GroovyTokenTypes.LITERAL_char: returnValue = "char"; break; |
| // note: LITERAL_def never created |
| case GroovyTokenTypes.LITERAL_double: returnValue = "double"; break; |
| case GroovyTokenTypes.LITERAL_float: returnValue = "float"; break; |
| case GroovyTokenTypes.LITERAL_int: returnValue = "int"; break; |
| case GroovyTokenTypes.LITERAL_long: returnValue = "long"; break; |
| case GroovyTokenTypes.LITERAL_short: returnValue = "short"; break; |
| case GroovyTokenTypes.LITERAL_void: returnValue = "void"; break; |
| |
| // identifiers |
| case GroovyTokenTypes.IDENT: returnValue = child.getText(); break; |
| } |
| } |
| return returnValue; |
| } |
| |
| |
| private void addParametersTo(SimpleGroovyExecutableMemberDoc executableMemberDoc, GroovySourceAST t,int visit) { |
| // parameters |
| GroovySourceAST parametersNode = t.childOfType(GroovyTokenTypes.PARAMETERS); |
| if (parametersNode != null && parametersNode.getNumberOfChildren() > 0) { |
| GroovySourceAST currentNode = (GroovySourceAST) parametersNode.getFirstChild(); |
| while (currentNode != null) { |
| String parameterTypeName = getTypeNodeAsText(currentNode.childOfType(GroovyTokenTypes.TYPE),"def"); |
| String parameterName = getText(currentNode.childOfType(GroovyTokenTypes.IDENT)); |
| SimpleGroovyParameter parameter = new SimpleGroovyParameter(parameterName); |
| parameter.setTypeName(parameterTypeName); |
| |
| executableMemberDoc.add(parameter); |
| |
| currentNode = (GroovySourceAST)currentNode.getNextSibling(); |
| } |
| } |
| } |
| |
| |
| |
| public void push(GroovySourceAST t) { |
| stack.push(t); |
| } |
| public GroovySourceAST pop() { |
| if (!stack.empty()) { |
| return (GroovySourceAST) stack.pop(); |
| } |
| return null; |
| } |
| |
| private GroovySourceAST getParentNode() { |
| Object parentNode = null; |
| Object currentNode = stack.pop(); |
| if (!stack.empty()) { |
| parentNode = stack.peek(); |
| } |
| stack.push(currentNode); |
| return (GroovySourceAST) parentNode; |
| } |
| } |