Initial import of Doclava project

Change-Id: Ia5ae56f1700fce98e0ae6954fa2df617ec0537bb
diff --git a/src/com/google/doclava/Comment.java b/src/com/google/doclava/Comment.java
new file mode 100644
index 0000000..178c17a
--- /dev/null
+++ b/src/com/google/doclava/Comment.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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 com.google.doclava;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.ArrayList;
+
+public class Comment {
+  static final Pattern LEADING_WHITESPACE = Pattern.compile("^[ \t\n\r]*(.*)$", Pattern.DOTALL);
+
+  static final Pattern TAG_BEGIN = Pattern.compile("[\r\n][\r\n \t]*@", Pattern.DOTALL);
+
+  static final Pattern TAG = Pattern.compile("(@[^ \t\r\n]+)[ \t\r\n]+(.*)", Pattern.DOTALL);
+
+  static final Pattern INLINE_TAG =
+      Pattern.compile("(.*?)\\{(@[^ \t\r\n\\}]+)[ \t\r\n]*(.*?)\\}", Pattern.DOTALL);
+
+  static final Pattern FIRST_SENTENCE =
+      Pattern.compile("((.*?)\\.)[ \t\r\n\\<](.*)", Pattern.DOTALL);
+
+  private static final String[] KNOWN_TAGS =
+      new String[] {"@author", "@since", "@version", "@deprecated", "@undeprecate", "@docRoot",
+          "@sdkCurrent", "@inheritDoc", "@more", "@samplecode", "@sample", "@include",
+          "@serial", "@com.intel.drl.spec_ref", "@ar.org.fitc.spec_ref",};
+
+  public Comment(String text, ContainerInfo base, SourcePositionInfo sp) {
+    mText = text;
+    mBase = base;
+    // sp now points to the end of the text, not the beginning!
+    mPosition = SourcePositionInfo.findBeginning(sp, text);
+  }
+
+  private void parseRegex(String text) {
+    Matcher m;
+
+    m = LEADING_WHITESPACE.matcher(text);
+    m.matches();
+    text = m.group(1);
+
+    m = TAG_BEGIN.matcher(text);
+
+    int start = 0;
+    int end = 0;
+    while (m.find()) {
+      end = m.start();
+
+      tag(text, start, end);
+
+      start = m.end() - 1; // -1 is the @
+    }
+    end = text.length();
+    tag(text, start, end);
+  }
+
+  private void tag(String text, int start, int end) {
+    SourcePositionInfo pos = SourcePositionInfo.add(mPosition, mText, start);
+
+    if (start >= 0 && end > 0 && (end - start) > 0) {
+      text = text.substring(start, end);
+
+      Matcher m = TAG.matcher(text);
+      if (m.matches()) {
+        // out of line tag
+        tag(m.group(1), m.group(2), false, pos);
+      } else {
+        // look for inline tags
+        m = INLINE_TAG.matcher(text);
+        start = 0;
+        while (m.find()) {
+          String str = m.group(1);
+          String tagname = m.group(2);
+          String tagvalue = m.group(3);
+          tag(null, m.group(1), true, pos);
+          tag(tagname, tagvalue, true, pos);
+          start = m.end();
+        }
+        int len = text.length();
+        if (start != len) {
+          tag(null, text.substring(start), true, pos);
+        }
+      }
+    }
+  }
+
+  private void tag(String name, String text, boolean isInline, SourcePositionInfo pos) {
+    /*
+     * String s = isInline ? "inline" : "outofline"; System.out.println("---> " + s + " name=[" +
+     * name + "] text=[" + text + "]");
+     */
+    if (name == null) {
+      mInlineTagsList.add(new TextTagInfo("Text", "Text", text, pos));
+    } else if (name.equals("@param")) {
+      mParamTagsList.add(new ParamTagInfo("@param", "@param", text, mBase, pos));
+    } else if (name.equals("@see")) {
+      mSeeTagsList.add(new SeeTagInfo("@see", "@see", text, mBase, pos));
+    } else if (name.equals("@link") || name.equals("@linkplain")) {
+      mInlineTagsList.add(new SeeTagInfo(name, "@see", text, mBase, pos));
+    } else if (name.equals("@throws") || name.equals("@exception")) {
+      mThrowsTagsList.add(new ThrowsTagInfo("@throws", "@throws", text, mBase, pos));
+    } else if (name.equals("@return")) {
+      mReturnTagsList.add(new ParsedTagInfo("@return", "@return", text, mBase, pos));
+    } else if (name.equals("@deprecated")) {
+      if (text.length() == 0) {
+        Errors.error(Errors.MISSING_COMMENT, pos, "@deprecated tag with no explanatory comment");
+        text = "No replacement.";
+      }
+      mDeprecatedTagsList.add(new ParsedTagInfo("@deprecated", "@deprecated", text, mBase, pos));
+    } else if (name.equals("@literal")) {
+      mInlineTagsList.add(new LiteralTagInfo(text, pos));
+    } else if (name.equals("@code")) {
+      mInlineTagsList.add(new CodeTagInfo(text, pos));
+    } else if (name.equals("@hide") || name.equals("@pending") || name.equals("@doconly")) {
+      // nothing
+    } else if (name.equals("@attr")) {
+      AttrTagInfo tag = new AttrTagInfo("@attr", "@attr", text, mBase, pos);
+      mAttrTagsList.add(tag);
+      Comment c = tag.description();
+      if (c != null) {
+        for (TagInfo t : c.tags()) {
+          mInlineTagsList.add(t);
+        }
+      }
+    } else if (name.equals("@undeprecate")) {
+      mUndeprecateTagsList.add(new TextTagInfo("@undeprecate", "@undeprecate", text, pos));
+    } else if (name.equals("@include") || name.equals("@sample")) {
+      mInlineTagsList.add(new SampleTagInfo(name, "@include", text, mBase, pos));
+    } else {
+      boolean known = false;
+      for (String s : KNOWN_TAGS) {
+        if (s.equals(name)) {
+          known = true;
+          break;
+        }
+      }
+      if (!known) {
+        Errors.error(Errors.UNKNOWN_TAG, pos == null ? null : new SourcePositionInfo(pos),
+            "Unknown tag: " + name);
+      }
+      TagInfo t = new TextTagInfo(name, name, text, pos);
+      if (isInline) {
+        mInlineTagsList.add(t);
+      } else {
+        mTagsList.add(t);
+      }
+    }
+  }
+
+  private void parseBriefTags() {
+    int N = mInlineTagsList.size();
+
+    // look for "@more" tag, which means that we might go past the first sentence.
+    int more = -1;
+    for (int i = 0; i < N; i++) {
+      if (mInlineTagsList.get(i).name().equals("@more")) {
+        more = i;
+      }
+    }
+    if (more >= 0) {
+      for (int i = 0; i < more; i++) {
+        mBriefTagsList.add(mInlineTagsList.get(i));
+      }
+    } else {
+      for (int i = 0; i < N; i++) {
+        TagInfo t = mInlineTagsList.get(i);
+        if (t.name().equals("Text")) {
+          Matcher m = FIRST_SENTENCE.matcher(t.text());
+          if (m.matches()) {
+            String text = m.group(1);
+            TagInfo firstSentenceTag = new TagInfo(t.name(), t.kind(), text, t.position());
+            mBriefTagsList.add(firstSentenceTag);
+            break;
+          }
+        }
+        mBriefTagsList.add(t);
+
+      }
+    }
+  }
+
+  public TagInfo[] tags() {
+    init();
+    return mInlineTags;
+  }
+
+  public TagInfo[] tags(String name) {
+    init();
+    ArrayList<TagInfo> results = new ArrayList<TagInfo>();
+    int N = mInlineTagsList.size();
+    for (int i = 0; i < N; i++) {
+      TagInfo t = mInlineTagsList.get(i);
+      if (t.name().equals(name)) {
+        results.add(t);
+      }
+    }
+    return results.toArray(new TagInfo[results.size()]);
+  }
+
+  public ParamTagInfo[] paramTags() {
+    init();
+    return mParamTags;
+  }
+
+  public SeeTagInfo[] seeTags() {
+    init();
+    return mSeeTags;
+  }
+
+  public ThrowsTagInfo[] throwsTags() {
+    init();
+    return mThrowsTags;
+  }
+
+  public TagInfo[] returnTags() {
+    init();
+    return mReturnTags;
+  }
+
+  public TagInfo[] deprecatedTags() {
+    init();
+    return mDeprecatedTags;
+  }
+
+  public TagInfo[] undeprecateTags() {
+    init();
+    return mUndeprecateTags;
+  }
+
+  public AttrTagInfo[] attrTags() {
+    init();
+    return mAttrTags;
+  }
+
+  public TagInfo[] briefTags() {
+    init();
+    return mBriefTags;
+  }
+
+  public boolean isHidden() {
+    if (mHidden != -1) {
+      return mHidden != 0;
+    } else {
+      if (Doclava.checkLevel(Doclava.SHOW_HIDDEN)) {
+        mHidden = 0;
+        return false;
+      }
+      boolean b = mText.indexOf("@hide") >= 0 || mText.indexOf("@pending") >= 0;
+      mHidden = b ? 1 : 0;
+      return b;
+    }
+  }
+
+  public boolean isDocOnly() {
+    if (mDocOnly != -1) {
+      return mDocOnly != 0;
+    } else {
+      boolean b = (mText != null) && (mText.indexOf("@doconly") >= 0);
+      mDocOnly = b ? 1 : 0;
+      return b;
+    }
+  }
+  
+  public boolean isDeprecated() {
+    if (mDeprecated != -1) {
+      return mDeprecated != 0;
+    } else {
+      boolean b = (mText != null) && (mText.indexOf("@deprecated") >= 0);
+      mDeprecated = b ? 1 : 0;
+      return b;
+    }
+  }
+
+  private void init() {
+    if (!mInitialized) {
+      initImpl();
+    }
+  }
+
+  private void initImpl() {
+    isHidden();
+    isDocOnly();
+    isDeprecated();
+
+    // Don't bother parsing text if we aren't generating documentation.
+    if (Doclava.generatingDocs()) {
+      parseRegex(mText);
+      parseBriefTags();
+    } else {
+      // Forces methods to be recognized by findOverriddenMethods in MethodInfo.
+      mInlineTagsList.add(new TextTagInfo("Text", "Text", mText,
+          SourcePositionInfo.add(mPosition, mText, 0)));
+    }
+
+    mText = null;
+    mInitialized = true;
+
+    mInlineTags = mInlineTagsList.toArray(new TagInfo[mInlineTagsList.size()]);
+    mParamTags = mParamTagsList.toArray(new ParamTagInfo[mParamTagsList.size()]);
+    mSeeTags = mSeeTagsList.toArray(new SeeTagInfo[mSeeTagsList.size()]);
+    mThrowsTags = mThrowsTagsList.toArray(new ThrowsTagInfo[mThrowsTagsList.size()]);
+    mReturnTags =
+        ParsedTagInfo.joinTags(mReturnTagsList.toArray(new ParsedTagInfo[mReturnTagsList.size()]));
+    mDeprecatedTags =
+        ParsedTagInfo.joinTags(mDeprecatedTagsList.toArray(new ParsedTagInfo[mDeprecatedTagsList
+            .size()]));
+    mUndeprecateTags = mUndeprecateTagsList.toArray(new TagInfo[mUndeprecateTagsList.size()]);
+    mAttrTags = mAttrTagsList.toArray(new AttrTagInfo[mAttrTagsList.size()]);
+    mBriefTags = mBriefTagsList.toArray(new TagInfo[mBriefTagsList.size()]);
+
+    mParamTagsList = null;
+    mSeeTagsList = null;
+    mThrowsTagsList = null;
+    mReturnTagsList = null;
+    mDeprecatedTagsList = null;
+    mUndeprecateTagsList = null;
+    mAttrTagsList = null;
+    mBriefTagsList = null;
+  }
+
+  boolean mInitialized;
+  int mHidden = -1;
+  int mDocOnly = -1;
+  int mDeprecated = -1;
+  String mText;
+  ContainerInfo mBase;
+  SourcePositionInfo mPosition;
+  int mLine = 1;
+
+  TagInfo[] mInlineTags;
+  TagInfo[] mTags;
+  ParamTagInfo[] mParamTags;
+  SeeTagInfo[] mSeeTags;
+  ThrowsTagInfo[] mThrowsTags;
+  TagInfo[] mBriefTags;
+  TagInfo[] mReturnTags;
+  TagInfo[] mDeprecatedTags;
+  TagInfo[] mUndeprecateTags;
+  AttrTagInfo[] mAttrTags;
+
+  ArrayList<TagInfo> mInlineTagsList = new ArrayList<TagInfo>();
+  ArrayList<TagInfo> mTagsList = new ArrayList<TagInfo>();
+  ArrayList<ParamTagInfo> mParamTagsList = new ArrayList<ParamTagInfo>();
+  ArrayList<SeeTagInfo> mSeeTagsList = new ArrayList<SeeTagInfo>();
+  ArrayList<ThrowsTagInfo> mThrowsTagsList = new ArrayList<ThrowsTagInfo>();
+  ArrayList<TagInfo> mBriefTagsList = new ArrayList<TagInfo>();
+  ArrayList<ParsedTagInfo> mReturnTagsList = new ArrayList<ParsedTagInfo>();
+  ArrayList<ParsedTagInfo> mDeprecatedTagsList = new ArrayList<ParsedTagInfo>();
+  ArrayList<TagInfo> mUndeprecateTagsList = new ArrayList<TagInfo>();
+  ArrayList<AttrTagInfo> mAttrTagsList = new ArrayList<AttrTagInfo>();
+
+
+}