Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: display Beta, Obsolete, and Internal status and descriptions #217

Merged
merged 11 commits into from
Nov 20, 2023
14 changes: 12 additions & 2 deletions third_party/docfx-doclet-143274/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,18 @@
</plugins>
</build>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>26.27.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
Expand Down Expand Up @@ -182,13 +194,11 @@
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-vision</artifactId>
<version>2.0.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-speech</artifactId>
<version>4.23.0</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.sun.source.doctree.LinkTree;
import com.sun.source.doctree.LiteralTree;
import com.sun.source.doctree.SeeTree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -22,6 +23,8 @@
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import jdk.javadoc.doclet.DocletEnvironment;
Expand Down Expand Up @@ -170,19 +173,17 @@ public String extractOverridden(T key) {
return resolve(key).getOverridden();
}

public String extractStatus(T element) {
Optional<DocCommentTree> docCommentTree = getDocCommentTree(element);
if (docCommentTree.isPresent()) {
boolean isDeprecated =
docCommentTree.get().getBlockTags().stream()
.filter(docTree -> docTree.getKind().equals(DocTree.Kind.DEPRECATED))
.findFirst()
.isPresent();
if (isDeprecated) {
return DocTree.Kind.DEPRECATED.name().toLowerCase();
}
}
return null;
protected Optional<DocCommentTree> getDocCommentTree(T element) {
return Optional.ofNullable(environment.getDocTrees().getDocCommentTree(element));
}

private boolean hasDeprecatedJavadocTag(T element) {
List<? extends DocTree> javadocTags =
getDocCommentTree(element)
.map(DocCommentTree::getBlockTags)
.orElse(Collections.emptyList());

return javadocTags.stream().map(DocTree::getKind).anyMatch(DocTree.Kind.DEPRECATED::equals);
}

protected String determineType(T element) {
Expand All @@ -194,16 +195,33 @@ protected String determinePackageName(T element) {
}

protected String determineComment(T element) {
Optional<DocCommentTree> docCommentTree = getDocCommentTree(element);
if (docCommentTree.isPresent()) {
String comment =
docCommentTree
.map(DocCommentTree::getFullBody)
.map(this::replaceLinksAndCodes)
.orElse(null);
return replaceBlockTags(docCommentTree.get(), comment);
String statusComment = getStatusComment(element);
String javadocComment = getJavadocComment(element);
return joinNullable(statusComment, javadocComment);
}

private String getJavadocComment(T element) {
return getDocCommentTree(element)
.map(
tree -> {
String commentWithBlockTags = replaceLinksAndCodes(tree.getFullBody());
return replaceBlockTags(tree, commentWithBlockTags);
})
.orElse(null);
}

/** Safely combine two nullable strings with a newline delimiter */
String joinNullable(@Nullable String top, @Nullable String bottom) {
String a = top == null || top.isEmpty() ? null : top;
String b = bottom == null || bottom.isEmpty() ? null : bottom;

if (a != null && b != null) {
return a + "\n\n" + b;
} else if (a != null) {
return a;
} else {
return b;
}
return null;
}

/** Provides support for deprecated and see tags */
Expand Down Expand Up @@ -277,10 +295,6 @@ String expandLiteralBody(LiteralTree bodyItem) {
return String.valueOf(StringEscapeUtils.unescapeJava(bodyItem.getBody().toString()));
}

protected Optional<DocCommentTree> getDocCommentTree(T element) {
return Optional.ofNullable(environment.getDocTrees().getDocCommentTree(element));
}

/**
* We make type shortening in assumption that package name doesn't contain uppercase characters
*/
Expand Down Expand Up @@ -313,4 +327,103 @@ private String getSeeTagRef(SeeTree seeTree) {
return String.format(
"<xref uid=\"%1$s\" data-throw-if-not-resolved=\"false\">%1$s</xref>", ref);
}

public String extractStatus(T element) {
List<String> annotationNames =
element.getAnnotationMirrors().stream()
.map(mirror -> mirror.getAnnotationType().asElement().getSimpleName().toString())
.collect(Collectors.toList());

if (annotationNames.stream().anyMatch("Deprecated"::equals)
|| hasDeprecatedJavadocTag(element)) {
return "deprecated";
}
if (annotationNames.stream().anyMatch("BetaApi"::equals)) {
return "beta";
}
return null;
}

public String getStatusComment(T element) {
Map<String, Optional<String>> annotationComments = getAnnotationComments(element);

// Deprecated comments are determined by the Javadoc @deprecated block tag.
// See this#replaceBlockTags

List<String> comments = new ArrayList<>();
if (annotationComments.containsKey("InternalApi")) {
comments.add(createInternalOnlyNotice(annotationComments.get("InternalApi")));
}
if (annotationComments.containsKey("InternalExtensionOnly")) {
comments.add(
createInternalExtensionOnlyNotice(annotationComments.get("InternalExtensionOnly")));
}
if (annotationComments.containsKey("ObsoleteApi")) {
comments.add(createObsoleteNotice(annotationComments.get("ObsoleteApi")));
}
if (annotationComments.containsKey("BetaApi")) {
comments.add(createBetaNotice(annotationComments.get("BetaApi")));
}

if (comments.isEmpty()) {
return null;
}
return String.join("\n\n", comments);
}

private String createBetaNotice(Optional<String> customComment) {
return "<aside class=\"beta\">\n"
+ "<p><strong>Beta</strong></p>\n"
+ customComment.map(comment -> "<p><em>" + comment + "</em></p>\n").orElse("")
+ "<p>This feature is covered by the <a href=\"/terms/service-terms#1\">Pre-GA Offerings "
+ "Terms</a> of the Terms of Service. Pre-GA libraries might have limited support, and "
+ "changes to pre-GA libraries might not be compatible with other pre-GA versions. For "
+ "more information, see the launch stage descriptions.</p>\n"
+ "</aside>\n";
}

private String createObsoleteNotice(Optional<String> customComment) {
return "<aside class=\"deprecated\">\n"
+ "<p><strong>Obsolete</strong></p>\n"
+ customComment.map(comment -> "<p><em>" + comment + "</em></p>\n").orElse("")
+ "<p>This feature is stable for usage in this major version, but may be deprecated in a "
+ "future release.</p>\n"
+ "</aside>\n";
}

private String createInternalExtensionOnlyNotice(Optional<String> customComment) {
return "<aside class=\"special\">\n"
+ "<p><strong>Internal Extension Only</strong>: This feature is stable for usage, but is "
alicejli marked this conversation as resolved.
Show resolved Hide resolved
+ "not intended for extension or implementation.</p>\n"
+ customComment.map(comment -> "<p><em>" + comment + "</em></p>\n").orElse("")
lqiu96 marked this conversation as resolved.
Show resolved Hide resolved
+ "</aside>\n";
}

private String createInternalOnlyNotice(Optional<String> customComment) {
alicejli marked this conversation as resolved.
Show resolved Hide resolved
return "<aside class=\"warning\">\n"
+ "<p><strong>Internal Only</strong>: This feature is not stable for application use.</p>\n"
+ customComment.map(comment -> "<p><em>" + comment + "</em></p>\n").orElse("")
+ "</aside>\n";
}

/**
* @return all annotations on the element and their associated comment, if it exists
alicejli marked this conversation as resolved.
Show resolved Hide resolved
*/
public Map<String, Optional<String>> getAnnotationComments(T element) {
Map<String, Optional<String>> annotationComments = new HashMap<>();

for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
String name = annotation.getAnnotationType().asElement().getSimpleName().toString();
Optional<String> value =
annotation.getElementValues().entrySet().stream()
.filter(entry -> entry.getKey().getSimpleName().toString().equals("value"))
.map(Map.Entry::getValue)
.map(annotationValue -> annotationValue.getValue().toString())
.findFirst();

annotationComments.put(name, value);
}

return annotationComments;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,9 @@ private String determineComment(ExecutableElement methodElement) {
String inheritedInlineComment = getInheritedInlineCommentString(methodElement);
Optional<DocCommentTree> docCommentTree = getDocCommentTree(methodElement);
if (docCommentTree.isPresent()) {
return replaceBlockTags(docCommentTree.get(), inheritedInlineComment);
inheritedInlineComment = replaceBlockTags(docCommentTree.get(), inheritedInlineComment);
}
return inheritedInlineComment;
return joinNullable(getStatusComment(methodElement), inheritedInlineComment);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,12 @@ List<String> determineInheritedMembers(List<ExtendedMetadataFileItem> inheritedM
return new ArrayList<>();
}

@Override
public String getStatusComment(TypeElement element) {
// Don't provide status comments for classes.
return null;
alicejli marked this conversation as resolved.
Show resolved Hide resolved
}

public String extractJavaType(TypeElement element) {
String superClass = determineSuperclass(element);
if (superClass != null && superClass.contains("Exception")) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.microsoft.lookup;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.testing.compile.CompilationRule;
Expand All @@ -16,7 +16,6 @@
import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.TextTree;
import com.sun.source.util.DocTrees;
import java.util.Arrays;
import java.util.List;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
Expand All @@ -42,6 +41,7 @@ public class ClassLookupTest {
private DeprecatedTree deprecatedTree;
private TextTree textTree;
private TypeMirror typeMirror;
private ClassItemsLookup classItemsLookup;

@Before
public void setup() {
Expand All @@ -54,6 +54,9 @@ public void setup() {
deprecatedTree = Mockito.mock(DeprecatedTree.class);
textTree = Mockito.mock(TextTree.class);
typeMirror = Mockito.mock(TypeMirror.class);
classItemsLookup = new ClassItemsLookup(environment, Mockito.mock(ElementUtil.class));

when(environment.getDocTrees()).thenReturn(docTrees);
}

@Test
Expand Down Expand Up @@ -189,41 +192,24 @@ public void determineTypeForClass() {
}

@Test
public void extractStatusDeprecated() {
public void extractStatus_deprecated() {
TypeElement element =
elements.getTypeElement(
"com.microsoft.samples.agreements.AgreementDetailsCollectionOperations");

when(environment.getDocTrees()).thenReturn(docTrees);
when(docTrees.getDocCommentTree(element)).thenReturn(docCommentTree);
doReturn(Arrays.asList(deprecatedTree)).when(docCommentTree).getBlockTags();
when(deprecatedTree.getKind()).thenReturn(DocTree.Kind.DEPRECATED);

String result = classLookup.extractStatus(element);

verify(environment).getDocTrees();
verify(docTrees).getDocCommentTree(element);
verify(docCommentTree).getBlockTags();
verify(deprecatedTree).getKind();
assertEquals("Wrong description", result, Status.DEPRECATED.toString());
}

@Test
public void extractStatusNotDeprecated() {
public void extractStatus_notDeprecated() {
TypeElement element =
elements.getTypeElement(
"com.microsoft.samples.agreements.AgreementDetailsCollectionOperations");

when(environment.getDocTrees()).thenReturn(docTrees);
when(docTrees.getDocCommentTree(element)).thenReturn(docCommentTree);
doReturn(Arrays.asList()).when(docCommentTree).getBlockTags();
elements.getTypeElement("com.microsoft.samples.agreements.AgreementMetaData");

String result = classLookup.extractStatus(element);

verify(environment).getDocTrees();
verify(docTrees).getDocCommentTree(element);
verify(docCommentTree).getBlockTags();
assertEquals("Wrong description", result, null);
assertNull("Wrong description", result);
}

@Test
Expand All @@ -233,12 +219,18 @@ public void testExtractJavaType() {
assertEquals("Wrong javaType", classLookup.extractJavaType(typeElement), "exception");

typeElement = elements.getTypeElement("com.microsoft.samples.google.RecognitionAudio");
assertEquals("Wrong javaType", classLookup.extractJavaType(typeElement), null);
assertNull("Wrong javaType", classLookup.extractJavaType(typeElement));

typeElement = elements.getTypeElement("com.microsoft.samples.google.BetaApi");
assertEquals("Wrong javaType", classLookup.extractJavaType(typeElement), "annotationtype");

typeElement = elements.getTypeElement("com.microsoft.samples.IPartner");
assertEquals("Wrong javaType", classLookup.extractJavaType(typeElement), null);
assertNull("Wrong javaType", classLookup.extractJavaType(typeElement));
}

@Test
public void testExtractStatus_class_beta() {
TypeElement betaApi = elements.getTypeElement("com.microsoft.samples.google.BetaApi");
assertThat(classLookup.extractStatus(betaApi)).isEqualTo("beta");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.google.api.core.ApiFunction;
import com.google.api.core.BetaApi;
import com.google.api.core.InternalExtensionOnly;
import com.google.api.gax.core.GoogleCredentialsProvider;
import com.google.api.gax.core.InstantiatingExecutorProvider;
import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;
Expand Down Expand Up @@ -63,6 +64,7 @@
* }</pre>
*/
@BetaApi
@InternalExtensionOnly
@Generated("by gapic-generator-java")
@SuppressWarnings("unchecked")
public class SpeechSettings extends ClientSettings<SpeechSettings> {
Expand Down
Loading