1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 import java.util.regex.Pattern; 18 import java.util.regex.Matcher; 19 import java.util.ArrayList; 20 21 public class Comment 22 { 23 static final Pattern LEADING_WHITESPACE = Pattern.compile( 24 "^[ \t\n\r]*(.*)$", 25 Pattern.DOTALL); 26 27 static final Pattern TAG_BEGIN = Pattern.compile( 28 "[\r\n][\r\n \t]*@", 29 Pattern.DOTALL); 30 31 static final Pattern TAG = Pattern.compile( 32 "(@[^ \t\r\n]+)[ \t\r\n]+(.*)", 33 Pattern.DOTALL); 34 35 static final Pattern INLINE_TAG = Pattern.compile( 36 "(.*?)\\{(@[^ \t\r\n\\}]+)[ \t\r\n]*(.*?)\\}", 37 Pattern.DOTALL); 38 39 static final Pattern FIRST_SENTENCE = Pattern.compile( 40 "((.*?)\\.)[ \t\r\n\\<](.*)", 41 Pattern.DOTALL); 42 43 private static final String[] KNOWN_TAGS = new String[] { 44 "@author", 45 "@since", 46 "@version", 47 "@deprecated", 48 "@undeprecate", 49 "@docRoot", 50 "@sdkCurrent", 51 "@inheritDoc", 52 "@more", 53 "@code", 54 "@samplecode", 55 "@sample", 56 "@include", 57 "@serial", 58 "@com.intel.drl.spec_ref", 59 "@ar.org.fitc.spec_ref", 60 }; 61 Comment(String text, ContainerInfo base, SourcePositionInfo sp)62 public Comment(String text, ContainerInfo base, SourcePositionInfo sp) 63 { 64 mText = text; 65 mBase = base; 66 // sp now points to the end of the text, not the beginning! 67 mPosition = SourcePositionInfo.findBeginning(sp, text); 68 } 69 parseRegex(String text)70 private void parseRegex(String text) 71 { 72 Matcher m; 73 74 m = LEADING_WHITESPACE.matcher(text); 75 m.matches(); 76 text = m.group(1); 77 78 m = TAG_BEGIN.matcher(text); 79 80 int start = 0; 81 int end = 0; 82 while (m.find()) { 83 end = m.start(); 84 85 tag(text, start, end); 86 87 start = m.end()-1; // -1 is the @ 88 } 89 end = text.length(); 90 tag(text, start, end); 91 } 92 tag(String text, int start, int end)93 private void tag(String text, int start, int end) 94 { 95 SourcePositionInfo pos = SourcePositionInfo.add(mPosition, mText, start); 96 97 if (start >= 0 && end > 0 && (end-start) > 0) { 98 text = text.substring(start, end); 99 100 Matcher m = TAG.matcher(text); 101 if (m.matches()) { 102 // out of line tag 103 tag(m.group(1), m.group(2), false, pos); 104 } else { 105 // look for inline tags 106 m = INLINE_TAG.matcher(text); 107 start = 0; 108 while (m.find()) { 109 String str = m.group(1); 110 String tagname = m.group(2); 111 String tagvalue = m.group(3); 112 tag(null, m.group(1), true, pos); 113 tag(tagname, tagvalue, true, pos); 114 start = m.end(); 115 } 116 int len = text.length(); 117 if (start != len) { 118 tag(null, text.substring(start), true, pos); 119 } 120 } 121 } 122 } 123 tag(String name, String text, boolean isInline, SourcePositionInfo pos)124 private void tag(String name, String text, boolean isInline, SourcePositionInfo pos) 125 { 126 /* 127 String s = isInline ? "inline" : "outofline"; 128 System.out.println("---> " + s 129 + " name=[" + name + "] text=[" + text + "]"); 130 */ 131 if (name == null) { 132 mInlineTagsList.add(new TextTagInfo("Text", "Text", text, pos)); 133 } 134 else if (name.equals("@param")) { 135 mParamTagsList.add(new ParamTagInfo("@param", "@param", text, mBase, pos)); 136 } 137 else if (name.equals("@see")) { 138 mSeeTagsList.add(new SeeTagInfo("@see", "@see", text, mBase, pos)); 139 } 140 else if (name.equals("@link") || name.equals("@linkplain")) { 141 mInlineTagsList.add(new SeeTagInfo(name, "@see", text, mBase, pos)); 142 } 143 else if (name.equals("@throws") || name.equals("@exception")) { 144 mThrowsTagsList.add(new ThrowsTagInfo("@throws", "@throws", text, mBase, pos)); 145 } 146 else if (name.equals("@return")) { 147 mReturnTagsList.add(new ParsedTagInfo("@return", "@return", text, mBase, pos)); 148 } 149 else if (name.equals("@deprecated")) { 150 if (text.length() == 0) { 151 Errors.error(Errors.MISSING_COMMENT, pos, 152 "@deprecated tag with no explanatory comment"); 153 text = "No replacement."; 154 } 155 mDeprecatedTagsList.add(new ParsedTagInfo("@deprecated", "@deprecated", text, mBase, pos)); 156 } 157 else if (name.equals("@literal")) { 158 mInlineTagsList.add(new LiteralTagInfo(name, name, text, pos)); 159 } 160 else if (name.equals("@hide") || name.equals("@pending") || name.equals("@doconly")) { 161 // nothing 162 } 163 else if (name.equals("@attr")) { 164 AttrTagInfo tag = new AttrTagInfo("@attr", "@attr", text, mBase, pos); 165 mAttrTagsList.add(tag); 166 Comment c = tag.description(); 167 if (c != null) { 168 for (TagInfo t: c.tags()) { 169 mInlineTagsList.add(t); 170 } 171 } 172 } 173 else if (name.equals("@undeprecate")) { 174 mUndeprecateTagsList.add(new TextTagInfo("@undeprecate", "@undeprecate", text, pos)); 175 } 176 else if (name.equals("@include") || name.equals("@sample")) { 177 mInlineTagsList.add(new SampleTagInfo(name, "@include", text, mBase, pos)); 178 } 179 else { 180 boolean known = false; 181 for (String s: KNOWN_TAGS) { 182 if (s.equals(name)) { 183 known = true; 184 break; 185 } 186 } 187 if (!known) { 188 Errors.error(Errors.UNKNOWN_TAG, pos == null ? null : new SourcePositionInfo(pos), 189 "Unknown tag: " + name); 190 } 191 TagInfo t = new TextTagInfo(name, name, text, pos); 192 if (isInline) { 193 mInlineTagsList.add(t); 194 } else { 195 mTagsList.add(t); 196 } 197 } 198 } 199 parseBriefTags()200 private void parseBriefTags() 201 { 202 int N = mInlineTagsList.size(); 203 204 // look for "@more" tag, which means that we might go past the first sentence. 205 int more = -1; 206 for (int i=0; i<N; i++) { 207 if (mInlineTagsList.get(i).name().equals("@more")) { 208 more = i; 209 } 210 } 211 if (more >= 0) { 212 for (int i=0; i<more; i++) { 213 mBriefTagsList.add(mInlineTagsList.get(i)); 214 } 215 } else { 216 for (int i=0; i<N; i++) { 217 TagInfo t = mInlineTagsList.get(i); 218 if (t.name().equals("Text")) { 219 Matcher m = FIRST_SENTENCE.matcher(t.text()); 220 if (m.matches()) { 221 String text = m.group(1); 222 TagInfo firstSentenceTag = new TagInfo(t.name(), t.kind(), text, t.position()); 223 mBriefTagsList.add(firstSentenceTag); 224 break; 225 } 226 } 227 mBriefTagsList.add(t); 228 229 } 230 } 231 } 232 tags()233 public TagInfo[] tags() 234 { 235 init(); 236 return mInlineTags; 237 } 238 tags(String name)239 public TagInfo[] tags(String name) 240 { 241 init(); 242 ArrayList<TagInfo> results = new ArrayList<TagInfo>(); 243 int N = mInlineTagsList.size(); 244 for (int i=0; i<N; i++) { 245 TagInfo t = mInlineTagsList.get(i); 246 if (t.name().equals(name)) { 247 results.add(t); 248 } 249 } 250 return results.toArray(new TagInfo[results.size()]); 251 } 252 paramTags()253 public ParamTagInfo[] paramTags() 254 { 255 init(); 256 return mParamTags; 257 } 258 seeTags()259 public SeeTagInfo[] seeTags() 260 { 261 init(); 262 return mSeeTags; 263 } 264 throwsTags()265 public ThrowsTagInfo[] throwsTags() 266 { 267 init(); 268 return mThrowsTags; 269 } 270 returnTags()271 public TagInfo[] returnTags() 272 { 273 init(); 274 return mReturnTags; 275 } 276 deprecatedTags()277 public TagInfo[] deprecatedTags() 278 { 279 init(); 280 return mDeprecatedTags; 281 } 282 undeprecateTags()283 public TagInfo[] undeprecateTags() 284 { 285 init(); 286 return mUndeprecateTags; 287 } 288 attrTags()289 public AttrTagInfo[] attrTags() 290 { 291 init(); 292 return mAttrTags; 293 } 294 briefTags()295 public TagInfo[] briefTags() 296 { 297 init(); 298 return mBriefTags; 299 } 300 isHidden()301 public boolean isHidden() 302 { 303 if (mHidden >= 0) { 304 return mHidden != 0; 305 } else { 306 if (DroidDoc.checkLevel(DroidDoc.SHOW_HIDDEN)) { 307 mHidden = 0; 308 return false; 309 } 310 boolean b = mText.indexOf("@hide") >= 0 || mText.indexOf("@pending") >= 0; 311 mHidden = b ? 1 : 0; 312 return b; 313 } 314 } 315 isDocOnly()316 public boolean isDocOnly() { 317 if (mDocOnly >= 0) { 318 return mDocOnly != 0; 319 } else { 320 boolean b = (mText != null) && (mText.indexOf("@doconly") >= 0); 321 mDocOnly = b ? 1 : 0; 322 return b; 323 } 324 } 325 init()326 private void init() 327 { 328 if (!mInitialized) { 329 initImpl(); 330 } 331 } 332 initImpl()333 private void initImpl() 334 { 335 isHidden(); 336 isDocOnly(); 337 parseRegex(mText); 338 parseBriefTags(); 339 mText = null; 340 mInitialized = true; 341 342 mInlineTags = mInlineTagsList.toArray(new TagInfo[mInlineTagsList.size()]); 343 mParamTags = mParamTagsList.toArray(new ParamTagInfo[mParamTagsList.size()]); 344 mSeeTags = mSeeTagsList.toArray(new SeeTagInfo[mSeeTagsList.size()]); 345 mThrowsTags = mThrowsTagsList.toArray(new ThrowsTagInfo[mThrowsTagsList.size()]); 346 mReturnTags = ParsedTagInfo.joinTags(mReturnTagsList.toArray( 347 new ParsedTagInfo[mReturnTagsList.size()])); 348 mDeprecatedTags = ParsedTagInfo.joinTags(mDeprecatedTagsList.toArray( 349 new ParsedTagInfo[mDeprecatedTagsList.size()])); 350 mUndeprecateTags = mUndeprecateTagsList.toArray(new TagInfo[mUndeprecateTagsList.size()]); 351 mAttrTags = mAttrTagsList.toArray(new AttrTagInfo[mAttrTagsList.size()]); 352 mBriefTags = mBriefTagsList.toArray(new TagInfo[mBriefTagsList.size()]); 353 354 mParamTagsList = null; 355 mSeeTagsList = null; 356 mThrowsTagsList = null; 357 mReturnTagsList = null; 358 mDeprecatedTagsList = null; 359 mUndeprecateTagsList = null; 360 mAttrTagsList = null; 361 mBriefTagsList = null; 362 } 363 364 boolean mInitialized; 365 int mHidden = -1; 366 int mDocOnly = -1; 367 String mText; 368 ContainerInfo mBase; 369 SourcePositionInfo mPosition; 370 int mLine = 1; 371 372 TagInfo[] mInlineTags; 373 TagInfo[] mTags; 374 ParamTagInfo[] mParamTags; 375 SeeTagInfo[] mSeeTags; 376 ThrowsTagInfo[] mThrowsTags; 377 TagInfo[] mBriefTags; 378 TagInfo[] mReturnTags; 379 TagInfo[] mDeprecatedTags; 380 TagInfo[] mUndeprecateTags; 381 AttrTagInfo[] mAttrTags; 382 383 ArrayList<TagInfo> mInlineTagsList = new ArrayList<TagInfo>(); 384 ArrayList<TagInfo> mTagsList = new ArrayList<TagInfo>(); 385 ArrayList<ParamTagInfo> mParamTagsList = new ArrayList<ParamTagInfo>(); 386 ArrayList<SeeTagInfo> mSeeTagsList = new ArrayList<SeeTagInfo>(); 387 ArrayList<ThrowsTagInfo> mThrowsTagsList = new ArrayList<ThrowsTagInfo>(); 388 ArrayList<TagInfo> mBriefTagsList = new ArrayList<TagInfo>(); 389 ArrayList<ParsedTagInfo> mReturnTagsList = new ArrayList<ParsedTagInfo>(); 390 ArrayList<ParsedTagInfo> mDeprecatedTagsList = new ArrayList<ParsedTagInfo>(); 391 ArrayList<TagInfo> mUndeprecateTagsList = new ArrayList<TagInfo>(); 392 ArrayList<AttrTagInfo> mAttrTagsList = new ArrayList<AttrTagInfo>(); 393 394 395 } 396