Home | History | Annotate | Download | only in src
      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 
     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 
     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 
     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 
    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 
    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 
    233     public TagInfo[] tags()
    234     {
    235         init();
    236         return mInlineTags;
    237     }
    238 
    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 
    253     public ParamTagInfo[] paramTags()
    254     {
    255         init();
    256         return mParamTags;
    257     }
    258 
    259     public SeeTagInfo[] seeTags()
    260     {
    261         init();
    262         return mSeeTags;
    263     }
    264 
    265     public ThrowsTagInfo[] throwsTags()
    266     {
    267         init();
    268         return mThrowsTags;
    269     }
    270 
    271     public TagInfo[] returnTags()
    272     {
    273         init();
    274         return mReturnTags;
    275     }
    276 
    277     public TagInfo[] deprecatedTags()
    278     {
    279         init();
    280         return mDeprecatedTags;
    281     }
    282 
    283     public TagInfo[] undeprecateTags()
    284     {
    285         init();
    286         return mUndeprecateTags;
    287     }
    288 
    289     public AttrTagInfo[] attrTags()
    290     {
    291         init();
    292         return mAttrTags;
    293     }
    294 
    295     public TagInfo[] briefTags()
    296     {
    297         init();
    298         return mBriefTags;
    299     }
    300 
    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 
    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 
    326     private void init()
    327     {
    328         if (!mInitialized) {
    329             initImpl();
    330         }
    331     }
    332 
    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