| /* gnu.classpath.tools.gjdoc.DocImpl |
| Copyright (C) 2001 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307 USA. */ |
| |
| package gnu.classpath.tools.gjdoc; |
| |
| import com.sun.javadoc.*; |
| import java.util.*; |
| import java.text.*; |
| import java.io.File; |
| import javax.swing.text.Segment; |
| |
| /** |
| * Represents the least common denominator of all Javadoc |
| * comment classes. |
| */ |
| public abstract class DocImpl implements Doc, TagContainer { |
| |
| protected static Tag[] seeTagEmptyArr = new SeeTagImpl[0]; |
| protected static Tag[] linkTagEmptyArr = new LinkTagImpl[0]; |
| protected static Tag[] paramTagEmptyArr = new ParamTagImpl[0]; |
| protected static Tag[] throwsTagEmptyArr = new ThrowsTagImpl[0]; |
| protected SourcePosition position; |
| private String boilerplateComment; |
| |
| // Return the text of the comment for this doc item. |
| public String commentText() { |
| |
| StringBuffer rc=new StringBuffer(); |
| |
| Tag[] textTags=(Tag[])tagMap.get("text"); |
| if (textTags!=null) { |
| for (int i=0; i<textTags.length; ++i) { |
| rc.append(textTags[i].text()); |
| } |
| } |
| return rc.toString(); |
| } |
| |
| // Compares this Object with the specified Object for order. |
| public int compareTo(java.lang.Object o) { |
| return Main.getInstance().getCollator().compare(name(), ((Doc)o).name()); |
| } |
| |
| // Return the first sentence of the comment as tags. |
| public Tag[] firstSentenceTags() { |
| |
| Tag[] rc=(Tag[])tagMap.get("first"); |
| if (rc==null) rc=new Tag[0]; |
| return rc; |
| } |
| |
| // Return the full unprocessed text of the comment. |
| public String getRawCommentText() { |
| if (rawDocumentation!=null) |
| return rawDocumentation; |
| else if (rawDocOffset>=0) |
| return Main.getRootDoc().readRawComment(rawDocOffset); |
| else |
| return null; |
| } |
| |
| // Return comment as tags. |
| public Tag[] inlineTags() { |
| |
| Tag[] rc=(Tag[])tagMap.get("inline"); |
| if (rc==null) rc=new Tag[0]; |
| return rc; |
| } |
| |
| // Is this Doc item a class. |
| public boolean isClass() { |
| return false; |
| } |
| |
| // Is this Doc item a constructor? False until overridden. |
| public boolean isConstructor() { |
| return false; |
| } |
| |
| // Is this Doc item a error class? False until overridden. |
| public boolean isError() { |
| return false; |
| } |
| |
| // Is this Doc item a exception class? False until overridden. |
| public boolean isException() { |
| return false; |
| } |
| |
| // Is this Doc item a field? False until overridden. |
| public boolean isField() { |
| return false; |
| } |
| |
| // return true if this Doc is include in the active set. |
| public boolean isIncluded() { |
| return false; |
| } |
| |
| // Is this Doc item a interface? False until overridden. |
| public boolean isInterface() { |
| return false; |
| } |
| |
| // Is this Doc item a simple method (i.e. |
| public boolean isMethod() { |
| return false; |
| } |
| |
| public boolean isPackage() { |
| return false; |
| } |
| |
| // Is this Doc item a ordinary class (i.e. |
| public boolean isOrdinaryClass() { |
| return false; |
| } |
| |
| // Return the see also tags in this Doc item. |
| public SeeTag[] seeTags() { |
| return (SeeTag[])getTagArr("see", seeTagEmptyArr); |
| } |
| |
| protected Tag[] getTagArr(String kindOfTag, Tag[] defaultRc) { |
| Tag[] rc=(Tag[])tagMap.get(kindOfTag); |
| if (rc==null) rc=defaultRc; |
| return rc; |
| } |
| |
| // Set the full unprocessed text of the comment. |
| public void setRawCommentText(String rawDocumentation) { |
| this.rawDocumentation=rawDocumentation; |
| } |
| |
| public void resolveComments() { |
| |
| if (rawDocumentation!=null && tagMap.isEmpty()) { |
| char[] charArray = rawDocumentation.toCharArray(); |
| int length = rawDocumentation.length(); |
| int startOffset = 0; |
| int endOffset = 0; |
| if (charArray[0] == '/' |
| && charArray[1] == '*' |
| && charArray[2] == '*' |
| && charArray[length - 2] == '*' |
| && charArray[length - 1] == '/') { |
| |
| startOffset = 3; |
| endOffset = 2; |
| } |
| |
| this.tagMap=parseCommentTags(charArray, |
| startOffset, |
| length - endOffset, |
| getContextClass(), |
| getContextMember(), |
| null, |
| boilerplateComment); |
| |
| if (Main.getInstance().isCacheRawComments()) { |
| rawDocOffset=Main.getRootDoc().writeRawComment(rawDocumentation); |
| rawDocumentation=null; |
| } |
| |
| resolveTags(); |
| } |
| else if (tagMap.isEmpty() && null != boilerplateComment) { |
| tagMap.put("all", new Tag[] { new TagImpl("@boilerplate", boilerplateComment,getContextClass(),null) }); |
| tagMap.put("@boilerplate", new Tag[] { new TagImpl("@boilerplate", boilerplateComment,getContextClass(),null) }); |
| } |
| } |
| |
| public static int skipHtmlWhitespace(char[] buffer, int startIndex) { |
| while (startIndex < buffer.length) { |
| char c=buffer[startIndex]; |
| if (!Parser.isWhitespace(c)) { |
| break; |
| } |
| else { |
| ++ startIndex; |
| } |
| } |
| return startIndex; |
| } |
| |
| /** |
| * Looks for an end-of-sentence marker in <code>text</code>, |
| * starting at <code>startIndex</code> and stopping at |
| * <code>endIndex</code>. |
| * |
| * @param text the text to be searched |
| * @param startIndex index in <code>text</code> at which to start |
| * @param endIndex index in <code>text</code> at which to stop |
| * |
| * @return the index of the character following the end-of-sentence |
| * marker, <code>endIndex</code> if no end-of-sentence |
| * marker could be found, or -1 if not implemented. |
| */ |
| private static int findEndOfSentence(char[] text, int startIndex, |
| int endIndex) |
| { |
| if (Main.getInstance().isUseBreakIterator()) { |
| Segment segment = new Segment(text, startIndex, endIndex - startIndex); |
| BreakIterator breakIterator = BreakIterator.getSentenceInstance(Main.getInstance().getLocale()); |
| breakIterator.setText(segment); |
| int result = breakIterator.next(); |
| if (BreakIterator.DONE == result) { |
| return endIndex; |
| } |
| else { |
| return result; |
| } |
| } |
| else { |
| while (startIndex < endIndex) { |
| if (text[startIndex] == '.' |
| && (startIndex+1 == endIndex |
| || Character.isWhitespace(text[startIndex+1]) |
| || isHTMLBreakTag(text, startIndex+1, endIndex) |
| )) { |
| return startIndex; |
| } |
| |
| startIndex++; |
| } |
| return endIndex; |
| } |
| } |
| |
| /** |
| * Returns true is the text from start to end begins with a 'p' or 'br' tag. |
| */ |
| private static boolean isHTMLBreakTag(char[] text, int start, int end) |
| { |
| String[] breakTags = { |
| "p>", "/p>", "h1>", "h2>", "h3>", "h4>", "h5>", "h6>", "hr>", |
| "pre>", "/pre>" |
| }; |
| |
| if (text[start] == '<') { |
| |
| outer: |
| for (int i=0; i<breakTags.length; ++i) { |
| String tag = breakTags[i]; |
| int len = tag.length(); |
| if (start + len < end) { |
| for (int j=0; j<len; ++j) { |
| char c = tag.charAt(j); |
| if (Character.toLowerCase(text[start + 1 + j]) != c) { |
| continue outer; |
| } |
| } |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| //private static final StringBuffer buf=new StringBuffer(32768); |
| private static final StringBuffer whitespaceBuf=new StringBuffer(); |
| private static char[] charBuf = new char[60000]; |
| private static int bufPos = 0; |
| |
| private static void appendToBuf(char c) |
| { |
| if (bufPos < charBuf.length) { |
| charBuf[bufPos++] = c; |
| } |
| else { |
| // |
| } |
| } |
| |
| private static void appendToBuf(StringBuffer s) |
| { |
| if (bufPos + s.length() <= charBuf.length) { |
| s.getChars(0, s.length(), charBuf, bufPos); |
| bufPos += s.length(); |
| } |
| else { |
| // |
| } |
| } |
| |
| private static void setBufLength(int length) |
| { |
| bufPos = 0; |
| } |
| |
| private static String bufToString() |
| { |
| return new String(charBuf, 0, bufPos); |
| } |
| |
| private static int bufLength() |
| { |
| return bufPos; |
| } |
| |
| public static Map parseCommentTags(char[] comment, int startIndex, int endIndex, |
| ClassDocImpl contextClass, MemberDocImpl contextMember, |
| AbstractTagImpl contextTag, String boilerplateComment) { |
| |
| int rawDocStart=skipHtmlWhitespace(comment, startIndex); |
| |
| int firstSentenceEnd = 0; |
| |
| if (comment.length>rawDocStart) { |
| |
| firstSentenceEnd = findEndOfSentence(comment, rawDocStart, comment.length); |
| |
| if (firstSentenceEnd < 0) { |
| BreakIterator boundary = BreakIterator.getSentenceInstance(Locale.ENGLISH); |
| boundary.setText(new ArrayCharacterIterator(comment, rawDocStart)); |
| boundary.first(); |
| boundary.next(); |
| firstSentenceEnd = boundary.current(); |
| } |
| |
| // Always include period at end of sentence if there is one. |
| if (firstSentenceEnd < comment.length |
| && '.' == comment[firstSentenceEnd]) { |
| ++ firstSentenceEnd; |
| } |
| } |
| |
| final int STATE_BEGOFLINE = 1; |
| final int STATE_TEXT = 2; |
| final int STATE_PARAM = 3; |
| final int STATE_PARAMVALUE = 4; |
| final int STATE_PARAMWRAP = 5; |
| final int STATE_INLINEPARAM = 6; |
| final int STATE_INLINEPARAMVALUE = 7; |
| final int STATE_WHITESPACE = 8; |
| final int STATE_INLINEPARAMVALUE_BOL = 9; |
| final int STATE_IPV_WHITESPACE = 10; |
| |
| int state=STATE_BEGOFLINE; |
| int prevState=STATE_TEXT; |
| |
| setBufLength(0); |
| whitespaceBuf.setLength(0); |
| |
| String paramName="", paramValue=""; |
| |
| Map tags=new HashMap(); |
| tags.put("inline", new LinkedList()); |
| tags.put("first", new LinkedList()); |
| tags.put("all", new LinkedList()); |
| |
| final char EOL=(char)-1; |
| |
| for (int i=rawDocStart; i<=endIndex; ++i) { |
| char c=(i<endIndex)?comment[i]:EOL; |
| char peek=(i<endIndex-1)?comment[i+1]:EOL; |
| |
| switch (state){ |
| |
| case STATE_BEGOFLINE: |
| if (i==firstSentenceEnd) { |
| AbstractTagImpl newTag = addTag(tags, "text", bufToString(), true, contextClass, contextMember, contextTag, false); |
| if (null != newTag) { |
| contextTag = newTag; |
| } |
| setBufLength(0); |
| } |
| |
| if (Parser.isWhitespace(c)) { |
| // ignore |
| } |
| else if (c=='*') { |
| // ignore, but go to STATE_TEXT |
| if (peek!='*' && peek!='@' && peek!=EOL) { |
| state=STATE_WHITESPACE; |
| } |
| } |
| else if (c=='@' || (c=='{' && peek=='@') || c==EOL) { |
| if (bufLength()>0) { |
| addTag(tags, "text", bufToString(), i<firstSentenceEnd, contextClass, contextMember, contextTag, false); |
| setBufLength(0); |
| } |
| if (c=='{') { |
| ++i; |
| state=STATE_INLINEPARAM; |
| } |
| else { |
| state=STATE_PARAM; |
| } |
| } |
| else { |
| state=STATE_TEXT; |
| appendToBuf(whitespaceBuf); |
| whitespaceBuf.setLength(0); |
| appendToBuf(c); |
| } |
| break; |
| |
| case STATE_WHITESPACE: |
| if (i==firstSentenceEnd) { |
| AbstractTagImpl newTag = addTag(tags, "text", bufToString(), true, contextClass, contextMember, contextTag, false); |
| if (null != newTag) { |
| contextTag = newTag; |
| } |
| setBufLength(0); |
| } |
| |
| if (c=='\n') { |
| whitespaceBuf.append(c); |
| state=STATE_BEGOFLINE; |
| } |
| else if (Parser.isWhitespace(c)) { |
| whitespaceBuf.append(c); |
| } |
| else if (c=='@' || (c=='{' && peek=='@') || c==EOL) { |
| if (bufLength()>0) { |
| AbstractTagImpl newTag = addTag(tags, "text", bufToString(), i<firstSentenceEnd, contextClass, contextMember, contextTag, false); |
| if (null != newTag) { |
| contextTag = newTag; |
| } |
| setBufLength(0); |
| } |
| if (c=='{') { |
| ++i; |
| state=STATE_INLINEPARAM; |
| } |
| else { |
| state=STATE_PARAM; |
| } |
| } |
| else { |
| appendToBuf(whitespaceBuf); |
| whitespaceBuf.setLength(0); |
| appendToBuf(c); |
| state=STATE_TEXT; |
| } |
| break; |
| |
| case STATE_PARAMWRAP: |
| if (c=='\n') { |
| appendToBuf(c); |
| } |
| else if (Parser.isWhitespace(c)) { |
| // ignore |
| } |
| else if (c=='*') { |
| // ignore, but go to STATE_TEXT |
| /* |
| if (i<endIndex && comment[i+1]!='*' && comment[i+1]!='@') { |
| state=STATE_PARAMVALUE; |
| } |
| */ |
| } |
| else if (c=='@' || c==EOL) { |
| paramValue=bufToString(); |
| AbstractTagImpl newTag = addTag(tags, paramName, paramValue, i<firstSentenceEnd, contextClass, contextMember, contextTag, false); |
| if (null != newTag) { |
| contextTag = newTag; |
| } |
| setBufLength(0); |
| if (c=='{') { |
| ++i; |
| state=STATE_INLINEPARAM; |
| } |
| else { |
| state=STATE_PARAM; |
| } |
| } |
| else { |
| state=STATE_PARAMVALUE; |
| appendToBuf(c); |
| } |
| break; |
| |
| case STATE_PARAM: |
| if (!(c==EOL || Parser.isWhitespace(c))) { |
| appendToBuf(c); |
| } |
| else if (c=='\n') { |
| paramName=bufToString(); |
| setBufLength(0); |
| state=STATE_PARAMWRAP; |
| } |
| else { |
| paramName=bufToString(); |
| setBufLength(0); |
| state=STATE_PARAMVALUE; |
| } |
| break; |
| |
| case STATE_INLINEPARAM: |
| if (c=='}') { |
| // tag without value |
| paramName=bufToString(); |
| AbstractTagImpl newTag = addTag(tags, paramName, "", i<firstSentenceEnd, contextClass, contextMember, contextTag, true); |
| if (null != newTag) { |
| contextTag = newTag; |
| } |
| state=prevState; |
| setBufLength(0); |
| } |
| else if (!(c==EOL || Parser.isWhitespace(c))) { |
| appendToBuf(c); |
| } |
| else if (c=='\n') { |
| paramName=bufToString(); |
| setBufLength(0); |
| state=STATE_INLINEPARAMVALUE_BOL; |
| } |
| else { |
| paramName=bufToString(); |
| setBufLength(0); |
| state=STATE_INLINEPARAMVALUE; |
| } |
| break; |
| |
| case STATE_PARAMVALUE: |
| if (c==EOL) { |
| paramValue=bufToString(); |
| AbstractTagImpl newTag = addTag(tags, paramName, paramValue, i<firstSentenceEnd, contextClass, contextMember, contextTag, false); |
| if (null != newTag) { |
| contextTag = newTag; |
| } |
| } |
| else if (c=='\n') { |
| appendToBuf(c); |
| state=STATE_PARAMWRAP; |
| } |
| else { |
| appendToBuf(c); |
| } |
| break; |
| |
| case STATE_INLINEPARAMVALUE: |
| if (c=='\n') { |
| appendToBuf(c); |
| state=STATE_INLINEPARAMVALUE_BOL; |
| } |
| else if (c==EOL || c=='}') { |
| paramValue=bufToString(); |
| AbstractTagImpl newTag = addTag(tags, paramName, paramValue, i<firstSentenceEnd, contextClass, contextMember, contextTag, true); |
| if (null != newTag) { |
| contextTag = newTag; |
| } |
| state=prevState; |
| setBufLength(0); |
| } |
| else { |
| appendToBuf(c); |
| } |
| break; |
| |
| case STATE_INLINEPARAMVALUE_BOL: |
| if (Parser.isWhitespace(c)) { |
| // ignore |
| } |
| else if (c=='*') { |
| // ignore, but go to STATE_TEXT |
| if (i<endIndex && peek!='*') { |
| state=STATE_IPV_WHITESPACE; |
| } |
| } |
| else if (c==EOL) { |
| if (bufLength()>0) { |
| AbstractTagImpl newTag = addTag(tags, "text", bufToString(), i<firstSentenceEnd, contextClass, contextMember, contextTag, false); |
| if (null != newTag) { |
| contextTag = newTag; |
| } |
| } |
| } |
| else { |
| state=STATE_INLINEPARAMVALUE; |
| appendToBuf(whitespaceBuf); |
| whitespaceBuf.setLength(0); |
| appendToBuf(c); |
| } |
| break; |
| |
| case STATE_IPV_WHITESPACE: |
| if (c=='\n') { |
| whitespaceBuf.append(c); |
| state=STATE_INLINEPARAMVALUE_BOL; |
| } |
| else if (Parser.isWhitespace(c)) { |
| whitespaceBuf.append(c); |
| } |
| else if (c==EOL) { |
| if (bufLength()>0) { |
| AbstractTagImpl newTag = addTag(tags, "text", bufToString(), i<firstSentenceEnd, contextClass, contextMember, contextTag, false); |
| if (null != newTag) { |
| contextTag = newTag; |
| } |
| setBufLength(0); |
| } |
| } |
| else { |
| appendToBuf(whitespaceBuf); |
| whitespaceBuf.setLength(0); |
| appendToBuf(c); |
| state=STATE_INLINEPARAMVALUE; |
| } |
| break; |
| |
| case STATE_TEXT: |
| if (i==firstSentenceEnd) { |
| AbstractTagImpl newTag = addTag(tags, "text", bufToString(), true, contextClass, contextMember, contextTag, false); |
| if (null != newTag) { |
| contextTag = newTag; |
| } |
| setBufLength(0); |
| } |
| |
| if (c==EOL) { |
| paramValue=bufToString(); |
| AbstractTagImpl newTag = addTag(tags, "text", paramValue, i<firstSentenceEnd, contextClass, contextMember, contextTag, false); |
| if (null != newTag) { |
| contextTag = newTag; |
| } |
| } |
| else if (c=='\n') { |
| appendToBuf(c); |
| state=STATE_BEGOFLINE; |
| } |
| else if (c=='{' && peek=='@') { |
| paramValue=bufToString(); |
| AbstractTagImpl newTag = addTag(tags, "text", paramValue, i<firstSentenceEnd, contextClass, contextMember, contextTag, false); |
| if (null != newTag) { |
| contextTag = newTag; |
| } |
| ++i; |
| setBufLength(0); |
| state=STATE_INLINEPARAM; |
| } |
| else { |
| appendToBuf(c); |
| } |
| break; |
| |
| default: |
| throw new Error("illegal state "+state); |
| } |
| } |
| |
| |
| if (null == contextMember && null != boilerplateComment && Main.getInstance().isCopyLicenseText()) { |
| addTag(tags, "@boilerplate", boilerplateComment, false, contextClass, null, null, false); |
| } |
| |
| Map rc=new HashMap(); |
| |
| for (Iterator it=tags.keySet().iterator(); it.hasNext(); ) { |
| String key=(String)it.next(); |
| Tag[] templateArr; |
| List list=(List)tags.get(key); |
| |
| if ("see".equals(key)) |
| templateArr=new SeeTag[list.size()]; |
| else if ("param".equals(key)) |
| templateArr=new ParamTag[list.size()]; |
| else if ("serialField".equals(key)) |
| templateArr=new SerialFieldTag[list.size()]; |
| else if ("throws".equals(key) || "exception".equals(key)) |
| templateArr=new ThrowsTag[list.size()]; |
| else { |
| templateArr=new Tag[list.size()]; |
| } |
| |
| rc.put(key, list.toArray(templateArr)); |
| } |
| |
| return rc; |
| } |
| |
| private ClassDocImpl getContextClass() { |
| if (isClass() || isInterface()) { |
| return (ClassDocImpl)this; |
| } |
| else if (isField() || isMethod() || isConstructor()) { |
| return (ClassDocImpl)((MemberDocImpl)this).containingClass(); |
| } |
| else { |
| return null; |
| } |
| } |
| |
| private MemberDocImpl getContextMember() { |
| if (isField() || isMethod() || isConstructor()) { |
| return (MemberDocImpl)this; |
| } |
| else { |
| return null; |
| } |
| } |
| |
| protected static AbstractTagImpl addTag(Map tags, String name, |
| String value, boolean isFirstSentence, |
| ClassDocImpl contextClass, |
| MemberDocImpl contextMember, |
| AbstractTagImpl contextTag, |
| boolean isInline) { |
| |
| AbstractTagImpl tag = null; |
| |
| boolean haveValue = (0 != value.trim().length()); |
| |
| String emptyWarning = "Empty @" + name + " tag."; |
| |
| if (name.equals("param")) { |
| if (haveValue) { |
| tag=new ParamTagImpl(value, contextClass, contextMember); |
| } |
| else { |
| //printWarning(emptyWarning); |
| } |
| } |
| else if (name.equals("see")) { |
| if (haveValue) { |
| tag=new SeeTagImpl(value, contextClass); |
| } |
| else { |
| //printWarning(emptyWarning); |
| } |
| } |
| else if (name.equals("link") || name.equals("linkplain")) { |
| if (haveValue) { |
| tag=new LinkTagImpl("@" + name, value, contextClass); |
| isInline = true; |
| } |
| else { |
| //printWarning(emptyWarning); |
| } |
| } |
| else if (name.equals("value")) { |
| if (haveValue) { |
| tag=new ValueTagImpl(value, contextClass); |
| isInline = true; |
| } |
| else { |
| //printWarning(emptyWarning); |
| } |
| } |
| else if (name.equals("inheritDoc")) { |
| if (haveValue) { |
| //printWarning("@inheritDoc tags are not supposed to have any content."); |
| } |
| tag=new InheritDocTagImpl(contextClass, contextMember, contextTag); |
| isInline = true; |
| } |
| else if (name.equals("serialField")) { |
| if (haveValue) { |
| tag=new SerialFieldTagImpl(value, contextClass, contextMember); |
| } |
| else { |
| //printWarning(emptyWarning); |
| } |
| } |
| else if (name.equals("throws") || name.equals("exception")) { |
| if (haveValue) { |
| tag=new ThrowsTagImpl(value, contextClass, contextMember); |
| } |
| else { |
| //printWarning(emptyWarning); |
| } |
| name="throws"; |
| } |
| else if (name.equals("text")) { |
| tag=new TextTagImpl(value); |
| isInline = true; |
| } |
| else { |
| tag=new TagImpl("@"+name, value.trim(), contextClass, contextMember); |
| // FIXME: consider taglets |
| } |
| |
| if (tag != null) { |
| if (isInline) { |
| ((List)tags.get("inline")).add(tag); |
| if (isFirstSentence) { |
| if (name.equals("text")) { |
| String txt = ((TextTagImpl)tag).getText(); |
| Tag newTag; |
| if (txt.startsWith("<p>")) { |
| newTag = new TextTagImpl(txt.substring(3)); |
| } |
| else if (txt.endsWith("</p>")) { |
| newTag = new TextTagImpl(txt.substring(0, txt.length() - 4)); |
| } |
| else { |
| newTag = tag; |
| } |
| ((List)tags.get("first")).add(newTag); |
| |
| } |
| else { |
| ((List)tags.get("first")).add(tag); |
| } |
| } |
| } |
| else { |
| ((List)tags.get("all")).add(tag); |
| } |
| |
| List l=((List)tags.get(name)); |
| if (l==null) { |
| l=new LinkedList(); |
| tags.put(name,l); |
| } |
| l.add(tag); |
| |
| return isInline ? tag : contextTag; |
| } |
| else { |
| return null; |
| } |
| } |
| |
| // Return all tags in this Doc item. |
| public Tag[] tags() { |
| Tag[] rc=(Tag[])tagMap.get("all"); |
| if (rc==null) rc=new Tag[0]; |
| return rc; |
| } |
| |
| // Return tags of the specified kind in this Doc item. |
| public Tag[] tags(java.lang.String tagname) { |
| Tag[] rc=(Tag[])tagMap.get(tagname); |
| if (rc==null) rc=new Tag[0]; |
| return rc; |
| } |
| |
| protected String rawDocumentation; |
| protected long rawDocOffset=-1; |
| |
| protected Map tagMap = new HashMap(); |
| |
| public Map getTagMap() { return tagMap; } |
| |
| protected void resolveTags() { |
| |
| Tag[] tags=tags(); |
| for (int i=0; i<tags.length; ++i) { |
| ((AbstractTagImpl)tags[i]).resolve(); |
| } |
| |
| Tag[] inlineTags=inlineTags(); |
| for (int i=0; i<inlineTags.length; ++i) { |
| ((AbstractTagImpl)inlineTags[i]).resolve(); |
| } |
| } |
| |
| private static Map classDocToFileMap = new HashMap(); |
| |
| private static File getFile(ClassDoc classDoc) { |
| File result = (File)classDocToFileMap.get(classDoc); |
| if (null == result) { |
| result = new File(((GjdocPackageDoc)classDoc.containingPackage()).packageDirectory(), |
| classDoc.name() + ".java"); |
| classDocToFileMap.put(classDoc, result); |
| } |
| return result; |
| } |
| |
| public static SourcePosition getPosition(ClassDoc classDoc) |
| { |
| return new SourcePositionImpl(getFile(classDoc), 0, 0); |
| } |
| |
| public static SourcePosition getPosition(ClassDoc classDoc, char[] source, int startIndex) |
| { |
| int column = 0; |
| int line = 0; |
| for (int i=0; i<startIndex; ++i) { |
| if (10 == source[i]) { |
| ++ line; |
| column = 0; |
| } |
| else if (13 != source[i]) { |
| ++ column; |
| } |
| } |
| while (true) { |
| ClassDoc containingClassDoc = classDoc.containingClass(); |
| if (null != containingClassDoc) { |
| classDoc = containingClassDoc; |
| } |
| else { |
| break; |
| } |
| } |
| |
| File file = getFile(classDoc); |
| |
| return new SourcePositionImpl(file, line + 1, column + 1); |
| } |
| |
| public SourcePosition position() |
| { |
| return this.position; |
| } |
| |
| public DocImpl(SourcePosition position) |
| { |
| this.position = position; |
| } |
| |
| public void setPosition(SourcePosition position) |
| { |
| this.position = position; |
| } |
| |
| private static TagContainer checkForInheritedDoc(ClassDoc classDoc, |
| MemberDocImpl memberDoc, |
| AbstractTagImpl tag) |
| { |
| DocImpl result; |
| |
| if (!(classDoc instanceof ClassDocImpl)) { |
| result = null; |
| } |
| else if (null == memberDoc) { |
| result = (DocImpl)classDoc; |
| } |
| else if (memberDoc.isField()) { |
| result = (DocImpl)((ClassDocImpl)classDoc).getFieldDoc(memberDoc.name()); |
| } |
| else if (memberDoc.isMethod()) { |
| result = (DocImpl)((ClassDocImpl)classDoc).getMethodDoc(memberDoc.name(), |
| ((MethodDoc)memberDoc).signature()); |
| } |
| else if (memberDoc.isConstructor()) { |
| result = (DocImpl)((ClassDocImpl)classDoc).getConstructorDoc(((ConstructorDoc)memberDoc).signature()); |
| } |
| else { |
| //assert(false); |
| throw new RuntimeException("memberDoc is supposed to be field, method or constructor"); |
| } |
| |
| if (null != result |
| && null != memberDoc |
| && null != tag) { |
| |
| TagContainer tagDoc = null; |
| |
| Tag[] tags = result.tags(); |
| for (int i=0; i<tags.length; ++i) { |
| if (tags[i].kind().equals(tag.kind())) { |
| if ("@param".equals(tag.kind())) { |
| if (((ParamTagImpl)tags[i]).parameterName().equals(((ParamTagImpl)tag).parameterName())) { |
| tagDoc = (TagContainer)tags[i]; |
| break; |
| } |
| } |
| else if ("@throws".equals(tag.kind())) { |
| if (((ThrowsTagImpl)tags[i]).exceptionName().equals(((ThrowsTagImpl)tag).exceptionName())) { |
| tagDoc = (TagContainer)tags[i]; |
| break; |
| } |
| } |
| else if ("@return".equals(tag.kind())) { |
| tagDoc = (TagContainer)tags[i]; |
| } |
| } |
| } |
| |
| return tagDoc; |
| } |
| |
| if (null == result || result.isEmptyDoc()) { |
| return null; |
| } |
| else { |
| return result; |
| } |
| } |
| |
| public static TagContainer findInheritedDoc(ClassDoc classDoc, |
| MemberDocImpl memberDoc, |
| AbstractTagImpl tag) |
| { |
| TagContainer result; |
| |
| // (Taken from Javadoc Solaris Tool documentation 1.5, |
| // section "Automatic Copying of Method Comments") |
| |
| // Algorithm for Inheriting Method Comments - If a method does |
| // not have a doc comment, or has an {@inheritDoc} tag, the |
| // Javadoc tool searches for an applicable comment using the |
| // following algorithm, which is designed to find the most |
| // specific applicable doc comment, giving preference to |
| // interfaces over superclasses: |
| |
| // 1. Look in each directly implemented (or extended) interface |
| // in the order they appear following the word implements (or |
| // extends) in the method declaration. Use the first doc comment |
| // found for this method. |
| |
| ClassDoc[] interfaces = classDoc.interfaces(); |
| if (null != interfaces) { |
| for (int i=0; i<interfaces.length; ++i) { |
| result = checkForInheritedDoc(interfaces[i], memberDoc, tag); |
| if (null != result) { |
| return result; |
| } |
| } |
| } |
| |
| // 2. If step 1 failed to find a doc comment, recursively apply |
| // this entire algorithm to each directly implemented (or |
| // extended) interface, in the same order they were examined |
| // in step 1. |
| |
| if (null != interfaces) { |
| for (int i=0; i<interfaces.length; ++i) { |
| result = findInheritedDoc(interfaces[i], memberDoc, tag); |
| if (null != result) { |
| return result; |
| } |
| } |
| } |
| |
| ClassDoc superclassDoc = classDoc.superclass(); |
| |
| // 3. If step 2 failed to find a doc comment and this is a class |
| // other than Object (not an interface): |
| if (!classDoc.isInterface() |
| && null != superclassDoc |
| && !"java.lang.Object".equals(classDoc.qualifiedTypeName())) { |
| |
| // 3a. If the superclass has a doc comment for this method, use it. |
| |
| result = checkForInheritedDoc(superclassDoc, memberDoc, tag); |
| if (null != result) { |
| return result; |
| } |
| |
| // 3b. If step 3a failed to find a doc comment, recursively |
| // apply this entire algorithm to the superclass. |
| |
| return findInheritedDoc(superclassDoc, |
| memberDoc, tag); |
| } |
| else { |
| return null; |
| } |
| } |
| |
| public boolean isEmptyDoc() |
| { |
| return tagMap.isEmpty(); |
| } |
| |
| void setBoilerplateComment(String boilerplateComment) |
| { |
| this.boilerplateComment = boilerplateComment; |
| } |
| } |
| |