View Javadoc

1   package DTDDoc;
2   
3   import java.util.HashMap;
4   import java.util.Map;
5   
6   import com.wutka.dtd.DTDComment;
7   
8   /** <p>This class contains several tools used for parsing javadoc-like
9    *  comments  (tags) appearing in XML comments.</p>
10   *
11   *  <p>Such a comment
12   *  is made of text and "tags". A tag is a \@ immediately followed by
13   *  a word and preceeded by at least a space.
14   *  This function will return a map that associate
15   *  tags to their text. The text associated to a tag is the one
16   *  that follows it and that precedes the next tag or the end
17   *  of the comment. The text that appears before the first
18   *  tag will automatically associated to the "comment" tag.</p>
19   *  <p>Right now, only "attr" tag can appear multiple times :
20   *  other tags can appear once and only once!</p>
21   *  <p>It is guaranteed that there are no spaces at the beginning or
22   *  at the end of the tag's value.</p>
23   *
24   *  @see #getUniqueTagValue(String)
25   *	@see #getMultipleTagValue(String)
26   *  @author Stefan Champailler */
27  
28  public class CommentParser {
29  
30      private final Logger log;
31  
32      /** The character preceding each of the tag recognized in our comment
33       *  system. */
34  
35      private static final char BEGIN_TAG = DTDCommenter.BEGIN_TAG;
36  
37      /** The tag starting a comment. This one is put in front on the
38       *  XML comments by default. */
39  
40      private static final String COMMENT_TAG = DTDCommenter.COMMENT_TAG;
41  
42      /** A mapping between each "@" tag and its value.
43       * @see #putTagValue(String, String)
44       */
45      private final Map values = new HashMap();
46  
47      /** This function parses a javaDoc-like comment:the resulting
48       *  mapping between each tag and its value is added in {@link #values} Map attribute.
49       *
50       *  @param comment the comment
51       *  @param getAroundNetBeanComments If true, we replace &lt;!--- with
52       *  &lt;!-- (and thus interpreting NetBeans comments as they should, as
53       *    explained <a
54       *    href="http://sourceforge.net/mailarchive/forum.php?thread_id=23713463&forum_id=33177">here</a>.)
55       */
56  
57      private void parseComment( String comment, boolean
58          getAroundNetBeanComments) {
59  
60          int start = 0;
61          // it was reported that Netbeans generates DTD documentation as
62          // "<!--- ..." instead of "<!-- ..."
63          // see http://sourceforge.net/mailarchive/forum.php?thread_id=23713463&forum_id=33177
64          // => simple workaround to remove this extra leading '-', since no real
65          // user documentation starts in such a way
66          if( getAroundNetBeanComments && comment.startsWith("-")) {
67              comment = comment.substring(1);
68          }
69  
70          // We prefix the comment with the comment tag so that we can generalize
71          // our code better later on.
72  
73          comment = COMMENT_TAG+" "+comment;
74  
75      //log.debug("comment = "+comment);
76  
77          while( start < comment.length()) {
78  
79              int tagNameStart = comment.indexOf( BEGIN_TAG, start);
80  
81              // To work with "substring", the index of the last char
82              // of the tagname is one char further than truth.
83              int tagNameEnd = comment.indexOf( ' ', tagNameStart);
84  
85              if( tagNameStart >= start && tagNameEnd > tagNameStart) {
86                  // We do have a tag, and its name is ...
87  
88                  String tagName = comment.substring( tagNameStart, tagNameEnd);
89  
90                  // The value of a tag goes from the end of that tag
91                  // to the beginning of the next tag.
92  
93                  int tagValueStart = tagNameEnd + 1;
94                  int tagValueEnd = comment.indexOf( BEGIN_TAG, tagValueStart);
95  
96                  // If a tag is not followed by another tag, then the value goes
97                  // from the end of the tag to the end of the comment.
98  
99                  if( tagValueEnd == -1)
100                     tagValueEnd = comment.length();
101 
102                 // Now we check if there's a value for the tag. There must be
103                 // one and it must not be spaces.
104 
105                 if( tagValueEnd > tagValueStart &&
106                     comment.substring( tagValueStart, tagValueEnd)
107                            .trim().length() > 0) {
108                     // There's a value and it's no spaces.
109                     String tagValue = comment.substring( tagValueStart, tagValueEnd).trim();
110                     putTagValue( tagName, tagValue);
111                 } else if (! COMMENT_TAG.equals(tagName)) {
112                     // only the "main" tag can be empty (for attributes,
113                     // for example)
114                     log.warn("The tag "+tagName+" can not be empty.");
115                 }
116 
117                 start = tagNameEnd + 1;
118 
119             } else
120                 start = comment.length();
121         }
122     }
123 
124     public String getUniqueTagValue( String tagName) {
125 
126         Object v = values.get( tagName);
127 
128         if( v == null)
129             return null;
130         else if( v instanceof String)
131             return (String) v;
132         else if( v instanceof Map) {
133             log.warn("Expected a unique value for tag " + BEGIN_TAG + tagName);
134             return null;
135         } else {
136             log.error("Internal error, unexpected type for" +
137                 " tag " + BEGIN_TAG + tagName + "'s value.");
138             return null;
139         }
140     }
141 
142     public Map getMultipleTagValue( String tagName) {
143         Object v = values.get( tagName);
144 
145         if( v == null)
146             return null;
147         else if( v instanceof Map)
148             return (Map) v;
149         else if( v instanceof String) {
150             log.warn("Expected a multiple value for tag " + BEGIN_TAG + tagName);
151             return null;
152         } else {
153             log.error("Unexpected type for" +
154                 " tag " + BEGIN_TAG + tagName + "'s value.");
155             return null;
156         }
157     }
158 
159     private void putTagValue( String name, String content) {
160 
161         if( name == null)
162             throw new IllegalArgumentException("putTagValue(): null tag name !");
163 
164         if( "@attr".equalsIgnoreCase( name)) {
165             // @attr is the only tag supporting multiple occurences: one time per attribute documented
166             if( content == null)
167                 throw new IllegalArgumentException("putTagValue(): content null for "+name+" !");
168 
169             String key = null;
170             String value = null;
171             int firstSpaceIndex = content.indexOf(' ');
172 
173             if( firstSpaceIndex != -1 && firstSpaceIndex+1 < content.length()) {
174                 key = content.substring( 0, firstSpaceIndex);
175                 value = content.substring( firstSpaceIndex+1, content.length());
176             } else {
177                 log.warn("The tag " + name + " requires a key and a value !");
178                 return;
179             }
180 
181             Map current = (Map) values.get( name);
182             if( current == null) {
183                 current = new HashMap();
184                 values.put( name, current);
185             }
186             current.put( key, value);
187         } else {
188             //log.debug("putTagValue(): name="+name+" value="+content);
189             values.put( name, content);
190         }
191     }
192 
193     public CommentParser( DTDComment comment, Logger log, boolean
194         getAroundNetBeanComments) {
195         this.log = log;
196         parseComment( comment.getText(), getAroundNetBeanComments);
197     }
198 }