Merge "Handle @sdkExtSince" am: 4775cbbc60

Original change: https://android-review.googlesource.com/c/platform/external/doclava/+/2214834

Change-Id: Iaf609633d417eea06f430ce13916b9cadac3ff0b
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/res/assets/templates/macros.cs b/res/assets/templates/macros.cs
index db502bb..421af97 100644
--- a/res/assets/templates/macros.cs
+++ b/res/assets/templates/macros.cs
@@ -273,9 +273,9 @@
       else
         ?><a href="<?cs var:toroot ?>guide/topics/manifest/uses-sdk-element.html#ApiLevels">API level<?cs
       /if ?> <?cs
-    var:obj.since ?></a><?cs
+    var:obj.since ?></a><?cs if:obj.sdkextsince ?><br/>Also in <a href="<?cs var:toroot ?>sdkExtensions"><?cs var:obj.sdkextsince ?></a><?cs /if ?><?cs
   else ?><a data-version-added="<?cs var:obj.since ?>" href="<?cs var:toroot ?>preview/"><b>Added in Android <?cs
-    var:obj.since ?></b></a><?cs
+    var:obj.since ?></b></a><?cs if:obj.sdkextsince ?><br/>Also in <a href="<?cs var:toroot ?>sdkExtensions"><?cs var:obj.sdkextsince ?></a><?cs /if ?><?cs
   /if?><?cs
 /if ?>
   <?cs if:obj.deprecatedsince ?><?cs
diff --git a/src/com/google/doclava/ClassInfo.java b/src/com/google/doclava/ClassInfo.java
index 2a5db75..e0ee7d0 100644
--- a/src/com/google/doclava/ClassInfo.java
+++ b/src/com/google/doclava/ClassInfo.java
@@ -1255,6 +1255,7 @@
     TagInfo.makeHDF(data, base + ".shortDescr", this.firstSentenceTags());
     TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
     data.setValue(base + ".since", getSince());
+    data.setValue(base + ".sdkextsince", getSdkExtSince());
     if (isDeprecated()) {
       data.setValue(base + ".deprecatedsince", getDeprecatedSince());
     }
@@ -1326,6 +1327,7 @@
       data.setValue("class.kind", kind);
     }
     data.setValue("class.since", getSince());
+    data.setValue("class.sdkextsince", getSdkExtSince());
     if (isDeprecated()) {
       data.setValue("class.deprecatedsince", getDeprecatedSince());
     }
diff --git a/src/com/google/doclava/Comment.java b/src/com/google/doclava/Comment.java
index a7209c2..9c2a392 100644
--- a/src/com/google/doclava/Comment.java
+++ b/src/com/google/doclava/Comment.java
@@ -35,8 +35,7 @@
           "@since",
           //value is an Android API level (set automatically by metalava)
           "@apiSince",
-          // value is <int> <int> (and SDK integer ID and version)
-          // (set automatically by metalava)
+          // value is "<Android SDK extension> <api-level>" (set automatically by metalava)
           "@sdkExtSince",
           "@deprecated",
           //value is an Android API level (set automatically by metalava)
@@ -337,7 +336,10 @@
     } else if (name.equals("@apiSince")) {
       setApiSince(text);
     } else if (name.equals("@sdkExtSince")) {
-      // TODO: handle @sdkExtSince
+      if (getSdkExtSince() != null) {
+        Errors.error(Errors.MULTIPLE_SDK_EXT_INFO, pos, "API symbol has multiple @sdkExtSince javadoc comments");
+      }
+      setSdkExtSince(text);
     } else if (name.equals("@deprecatedSince")) {
       setDeprecatedSince(text);
     } else if (name.equals("@see")) {
@@ -567,6 +569,17 @@
     return mApiSince;
   }
 
+  public void setSdkExtSince(String sdkextsince) {
+    if (sdkextsince != null) {
+      sdkextsince = sdkextsince.trim();
+    }
+    mSdkExtSince = sdkextsince;
+  }
+
+  public String getSdkExtSince() {
+    return mSdkExtSince;
+  }
+
   public boolean isDocOnly() {
     if (mDocOnly == null) {
       mDocOnly = (mText != null) && (mText.indexOf("@doconly") >= 0);
@@ -644,6 +657,7 @@
   Boolean mDeprecated = null;
   String mDeprecatedSince;
   String mApiSince;
+  String mSdkExtSince;
   String mText;
   ContainerInfo mBase;
   SourcePositionInfo mPosition;
diff --git a/src/com/google/doclava/DocInfo.java b/src/com/google/doclava/DocInfo.java
index fae225f..8fa577f 100644
--- a/src/com/google/doclava/DocInfo.java
+++ b/src/com/google/doclava/DocInfo.java
@@ -107,6 +107,17 @@
     return mSince;
   }
 
+  public void setSdkExtSince(String sdkextsince) {
+    mSdkExtSince = sdkextsince;
+  }
+
+  public String getSdkExtSince() {
+    if (Doclava.METALAVA_API_SINCE) {
+      mSdkExtSince = comment().getSdkExtSince();
+    }
+    return mSdkExtSince;
+  }
+
   /**
    * Sets the artifact in which the class resides.
    * <p>
@@ -168,6 +179,7 @@
   Comment mComment;
   SourcePositionInfo mPosition;
   private String mSince;
+  private String mSdkExtSince;
   private String mArtifact;
   private String mDeprecatedSince;
   private Set<FederatedSite> mFederatedReferences = new LinkedHashSet<FederatedSite>();
diff --git a/src/com/google/doclava/Errors.java b/src/com/google/doclava/Errors.java
index a7a3bdf..05ac006 100644
--- a/src/com/google/doclava/Errors.java
+++ b/src/com/google/doclava/Errors.java
@@ -315,6 +315,7 @@
   public static final Error NO_ARTIFACT_DATA = new Error(129, HIDDEN);
   public static final Error BROKEN_ARTIFACT_FILE = new Error(130, ERROR);
   public static final Error JAVA_TAG_IN_COMMENT = new Error(131, LINT);
+  public static final Error MULTIPLE_SDK_EXT_INFO = new Error(132, ERROR);
 
   public static boolean setErrorLevel(int code, int level) {
     for (Error e : sErrors) {
diff --git a/src/com/google/doclava/FieldInfo.java b/src/com/google/doclava/FieldInfo.java
index 40a3c81..99d208c 100644
--- a/src/com/google/doclava/FieldInfo.java
+++ b/src/com/google/doclava/FieldInfo.java
@@ -376,6 +376,7 @@
     TagInfo.makeHDF(data, base + ".deprecated", comment().deprecatedTags());
     TagInfo.makeHDF(data, base + ".seeAlso", comment().seeTags());
     data.setValue(base + ".since", getSince());
+    data.setValue(base + ".sdkextsince", getSdkExtSince());
     if (isDeprecated()) {
       data.setValue(base + ".deprecatedsince", getDeprecatedSince());
     }
diff --git a/src/com/google/doclava/MethodInfo.java b/src/com/google/doclava/MethodInfo.java
index ac6d2c5..f3ac64f 100644
--- a/src/com/google/doclava/MethodInfo.java
+++ b/src/com/google/doclava/MethodInfo.java
@@ -630,6 +630,7 @@
     TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
     TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
     data.setValue(base + ".since", getSince());
+    data.setValue(base + ".sdkextsince", getSdkExtSince());
     if (isDeprecated()) {
       data.setValue(base + ".deprecatedsince", getDeprecatedSince());
     }
diff --git a/src/com/google/doclava/PackageInfo.java b/src/com/google/doclava/PackageInfo.java
index 25f229e..edef00d 100644
--- a/src/com/google/doclava/PackageInfo.java
+++ b/src/com/google/doclava/PackageInfo.java
@@ -208,6 +208,7 @@
     }
     data.setValue(base + ".name", name());
     data.setValue(base + ".since", getSince());
+    data.setValue(base + ".sdkextsince", getSdkExtSince());
   }
 
   public void makeClassLinkListHDF(Data data, String base) {
@@ -219,6 +220,7 @@
     ClassInfo.makeLinkListHDF(data, base + ".exceptions", exceptions());
     ClassInfo.makeLinkListHDF(data, base + ".errors", errors());
     data.setValue(base + ".since", getSince());
+    data.setValue(base + ".sdkextsince", getSdkExtSince());
   }
 
   public ClassInfo[] annotations() {
diff --git a/src/com/google/doclava/TypeInfo.java b/src/com/google/doclava/TypeInfo.java
index 4369036..da5bc1f 100644
--- a/src/com/google/doclava/TypeInfo.java
+++ b/src/com/google/doclava/TypeInfo.java
@@ -317,6 +317,7 @@
       if (mClass.isIncluded()) {
         data.setValue(base + ".link", mClass.htmlPage());
         data.setValue(base + ".since", mClass.getSince());
+        data.setValue(base + ".sdkextsince", mClass.getSdkExtSince());
       } else {
         Doclava.federationTagger.tag(mClass);
         if (!mClass.getFederatedReferences().isEmpty()) {