From f1575f708a905874fc82f54c8dad705a6c479a1c Mon Sep 17 00:00:00 2001 From: anmeng10101 <33647870+anmeng10101@users.noreply.github.com> Date: Wed, 13 May 2020 17:12:04 +0800 Subject: [PATCH] May Release For 2020 (#34) * not document package private (default modifier) elements (#32) * resolve the uid for context in @link (#33) * add items in specForJava to localLookup context * add fuzzy resolve for @link content --- src/main/java/com/microsoft/build/Lookup.java | 20 ++++- .../com/microsoft/build/YmlFilesBuilder.java | 90 ++++++++++++------- .../com/microsoft/lookup/ClassLookup.java | 15 ++-- .../java/com/microsoft/util/ElementUtil.java | 32 +++++-- .../microsoft/build/YmlFilesBuilderTest.java | 30 ++++--- .../java/com/microsoft/samples/SuperHero.java | 9 +- .../com/microsoft/util/ElementUtilTest.java | 47 +++++++--- 7 files changed, 169 insertions(+), 74 deletions(-) diff --git a/src/main/java/com/microsoft/build/Lookup.java b/src/main/java/com/microsoft/build/Lookup.java index 23183ce..b6fc1c7 100644 --- a/src/main/java/com/microsoft/build/Lookup.java +++ b/src/main/java/com/microsoft/build/Lookup.java @@ -4,6 +4,7 @@ import static org.apache.commons.lang3.RegExUtils.replaceAll; import com.microsoft.model.MetadataFile; + import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -54,6 +55,8 @@ private void consume(List metadataFiles) { * Logic of {@link YmlFilesBuilder#resolveUidByLookup} based on this for case when @link starts from '#' */ Map map = new LinkedHashMap<>(); + Map specForJavaMap = new LinkedHashMap<>(); + file.getItems().forEach(item -> { String uid = item.getUid(); String nameWithType = item.getNameWithType(); @@ -71,13 +74,26 @@ private void consume(List metadataFiles) { map.put(replaceAll(removeAll(uid, UID_PACKAGE_NAME_REGEXP), ",", ", "), uid); map.put(replaceAll(removeAll(nameWithTypeWithoutGenerics, METHOD_PARAMS_REGEXP), ", ", ","), uid); }); + file.getReferences().forEach(item -> { - map.put(item.getNameWithType(), item.getUid()); map.put(item.getUid(), item.getUid()); + + // complex types are recorded in "specForJava" as arrayList of items, thus it has no "NameWithType" + // thus we need to get every reference item from specForJava, and add to localLookup + if (item.getNameWithType() == null || item.getNameWithType().isEmpty()) { + item.getSpecForJava().forEach(spec -> { + specForJavaMap.put(spec.getName(), spec.getUid()); + specForJavaMap.put(spec.getFullName(), spec.getUid()); + }); + } else { + map.put(item.getNameWithType(), item.getUid()); + } }); - localLookupByFileName.put(file.getFileNameWithPath(), map); + // to avoid conflict, the items from specForJava should only add to localLookup globalLookup.putAll(map); + map.putAll(specForJavaMap); + localLookupByFileName.put(file.getFileNameWithPath(), map); }); } } diff --git a/src/main/java/com/microsoft/build/YmlFilesBuilder.java b/src/main/java/com/microsoft/build/YmlFilesBuilder.java index afbdfcb..2a5c50c 100644 --- a/src/main/java/com/microsoft/build/YmlFilesBuilder.java +++ b/src/main/java/com/microsoft/build/YmlFilesBuilder.java @@ -4,39 +4,22 @@ import com.microsoft.lookup.ClassItemsLookup; import com.microsoft.lookup.ClassLookup; import com.microsoft.lookup.PackageLookup; -import com.microsoft.model.MetadataFile; -import com.microsoft.model.MetadataFileItem; -import com.microsoft.model.SpecViewModel; -import com.microsoft.model.TocFile; -import com.microsoft.model.TocItem; +import com.microsoft.model.*; import com.microsoft.util.ElementUtil; import com.microsoft.util.FileUtil; import com.microsoft.util.YamlUtil; +import jdk.javadoc.doclet.DocletEnvironment; +import org.apache.commons.lang3.RegExUtils; +import org.apache.commons.lang3.StringUtils; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; +import javax.lang.model.element.*; +import javax.lang.model.util.ElementFilter; +import java.util.*; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Modifier; -import javax.lang.model.element.PackageElement; -import javax.lang.model.element.TypeElement; -import javax.lang.model.util.ElementFilter; - -import jdk.javadoc.doclet.DocletEnvironment; - -import org.apache.commons.lang3.RegExUtils; -import org.apache.commons.lang3.StringUtils; public class YmlFilesBuilder { @@ -44,6 +27,7 @@ public class YmlFilesBuilder { private final static String[] LANGS = {"java"}; private final Pattern XREF_LINK_PATTERN = Pattern.compile(".*?"); private final Pattern XREF_LINK_CONTENT_PATTERN = Pattern.compile("(?<=.*?)"); + private final Pattern XREF_LINK_RESOLVE_PATTERN = Pattern.compile("(?\\w+)\\#(?\\w+)(\\((?.*)\\))?"); private DocletEnvironment environment; private String outputPath; @@ -182,7 +166,7 @@ void addChildren(TypeElement classElement, List children) { List filterPrivateElements(List elements) { return elements.stream() - .filter(element -> !element.getModifiers().contains(Modifier.PRIVATE)).collect(Collectors.toList()); + .filter(element -> !ElementUtil.isPrivateOrPackagePrivate(element)).collect(Collectors.toList()); } void collect(TypeElement classElement, List children, @@ -209,7 +193,7 @@ void addConstructorsInfo(TypeElement classElement, MetadataFile classMetadataFil void addMethodsInfo(TypeElement classElement, MetadataFile classMetadataFile) { ElementFilter.methodsIn(classElement.getEnclosedElements()).stream() - .filter(methodElement -> !methodElement.getModifiers().contains(Modifier.PRIVATE)) + .filter(methodElement -> !ElementUtil.isPrivateOrPackagePrivate(methodElement)) .forEach(methodElement -> { MetadataFileItem methodItem = buildMetadataFileItem(methodElement); methodItem.setOverload(classItemsLookup.extractOverload(methodElement)); @@ -228,7 +212,7 @@ void addMethodsInfo(TypeElement classElement, MetadataFile classMetadataFile) { void addFieldsInfo(TypeElement classElement, MetadataFile classMetadataFile) { ElementFilter.fieldsIn(classElement.getEnclosedElements()).stream() - .filter(fieldElement -> !fieldElement.getModifiers().contains(Modifier.PRIVATE)) + .filter(fieldElement -> !ElementUtil.isPrivateOrPackagePrivate(fieldElement)) .forEach(fieldElement -> { MetadataFileItem fieldItem = buildMetadataFileItem(fieldElement); fieldItem.setContent(classItemsLookup.extractFieldContent(fieldElement)); @@ -392,25 +376,40 @@ String populateUidValues(String text, LookupContext lookupContext) { } String linkContent = linkContentMatcher.group(); - String uid = resolveUidByLookup(linkContent, lookupContext); + String uid = resolveUidFromLinkContent(linkContent, lookupContext); String updatedLink = linkContentMatcher.replaceAll(uid); text = StringUtils.replace(text, link, updatedLink); } return text; } - String resolveUidByLookup(String linkContent, LookupContext lookupContext) { + /** + * The linkContent could be in following format + * #memeber + * Class#member + * Class#method() + * Class#method(params) + */ + String resolveUidFromLinkContent(String linkContent, LookupContext lookupContext) { if (StringUtils.isBlank(linkContent)) { return ""; } linkContent = linkContent.trim(); + + // complete class name for class internal link if (linkContent.startsWith("#")) { String firstKey = lookupContext.getOwnerUid(); linkContent = firstKey + linkContent; } + + // fuzzy resolve, target for items from project external references + String fuzzyResolvedUid = resolveUidFromReference(linkContent, lookupContext); + + // exact resolve in lookupContext linkContent = linkContent.replace("#", "."); - return lookupContext.containsKey(linkContent) ? lookupContext.resolve(linkContent) : ""; + String exactResolveUid = resolveUidByLookup(linkContent, lookupContext); + return exactResolveUid.isEmpty() ? fuzzyResolvedUid : exactResolveUid; } List splitUidWithGenericsIntoClassNames(String uid) { @@ -464,4 +463,35 @@ else if (uid != "") return specList; } + + /** + * this method is used to do fuzzy resolve + * "*" will be added at the end of uid for method for xerf service resolve purpose + */ + String resolveUidFromReference(String linkContent, LookupContext lookupContext) { + String uid = ""; + Matcher matcher = XREF_LINK_RESOLVE_PATTERN.matcher(linkContent); + + if (matcher.find()) { + String className = matcher.group("class"); + String memberName = matcher.group("member"); + uid = resolveUidByLookup(className, lookupContext); + if (!uid.isEmpty()) { + uid = uid.concat(".").concat(memberName); + + // linkContent targets a method + if (!StringUtils.isBlank(matcher.group(3))) { + uid = uid.concat("*"); + } + } + } + return uid; + } + + String resolveUidByLookup(String signature, LookupContext lookupContext){ + if (StringUtils.isBlank(signature) || lookupContext == null) { + return ""; + } + return lookupContext.containsKey(signature) ? lookupContext.resolve(signature) : ""; + } } diff --git a/src/main/java/com/microsoft/lookup/ClassLookup.java b/src/main/java/com/microsoft/lookup/ClassLookup.java index 7541adf..e875836 100644 --- a/src/main/java/com/microsoft/lookup/ClassLookup.java +++ b/src/main/java/com/microsoft/lookup/ClassLookup.java @@ -3,19 +3,18 @@ import com.microsoft.lookup.model.ExtendedMetadataFileItem; import com.microsoft.model.MetadataFileItem; import com.microsoft.model.TypeParameter; +import com.microsoft.util.ElementUtil; +import jdk.javadoc.doclet.DocletEnvironment; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; -import java.util.*; -import java.util.stream.Collectors; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; -import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; - -import jdk.javadoc.doclet.DocletEnvironment; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; +import java.util.*; +import java.util.stream.Collectors; public class ClassLookup extends BaseLookup { @@ -147,7 +146,7 @@ void appendInheritedMethods(TypeElement element, List .orElse(0); for (Element m : members) { - if (m.getKind() == ElementKind.METHOD && !m.getModifiers().contains(Modifier.PRIVATE)) { + if (m.getKind() == ElementKind.METHOD && !ElementUtil.isPrivateOrPackagePrivate(m)) { String uid = element.getQualifiedName().toString().concat(".") + String.valueOf(m); ExtendedMetadataFileItem item = new ExtendedMetadataFileItem(uid); diff --git a/src/main/java/com/microsoft/util/ElementUtil.java b/src/main/java/com/microsoft/util/ElementUtil.java index 697833e..0f850ea 100644 --- a/src/main/java/com/microsoft/util/ElementUtil.java +++ b/src/main/java/com/microsoft/util/ElementUtil.java @@ -1,16 +1,18 @@ package com.microsoft.util; +import org.apache.commons.lang3.StringUtils; + +import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.ElementFilter; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.lang.model.element.Element; -import javax.lang.model.element.PackageElement; -import javax.lang.model.element.TypeElement; -import javax.lang.model.util.ElementFilter; -import org.apache.commons.lang3.StringUtils; public class ElementUtil { @@ -49,4 +51,24 @@ boolean matchAnyPattern(Set patterns, String stringToCheck) { } return false; } + + public static boolean isPackagePrivate(Element e) { + return !(isPublic(e) || isPrivate(e) || isProtected(e)); + } + + public static boolean isPrivate(Element e) { + return e.getModifiers().contains(Modifier.PRIVATE); + } + + public static boolean isProtected(Element e) { + return e.getModifiers().contains(Modifier.PROTECTED); + } + + public static boolean isPublic(Element e) { + return e.getModifiers().contains(Modifier.PUBLIC); + } + + public static boolean isPrivateOrPackagePrivate(Element e) { + return isPrivate(e) || isPackagePrivate(e); + } } diff --git a/src/test/java/com/microsoft/build/YmlFilesBuilderTest.java b/src/test/java/com/microsoft/build/YmlFilesBuilderTest.java index 56f3daf..bbe5a96 100644 --- a/src/test/java/com/microsoft/build/YmlFilesBuilderTest.java +++ b/src/test/java/com/microsoft/build/YmlFilesBuilderTest.java @@ -11,6 +11,7 @@ import com.microsoft.model.MethodParameter; import com.microsoft.model.Syntax; import com.sun.source.util.DocTrees; + import java.io.File; import java.util.Arrays; import java.util.Collection; @@ -22,6 +23,7 @@ import java.util.stream.Collectors; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; + import jdk.javadoc.doclet.DocletEnvironment; import org.apache.commons.lang3.RegExUtils; import org.junit.Before; @@ -113,29 +115,29 @@ public void populateUidValues() { ymlFilesBuilder.populateUidValues(Collections.emptyList(), Arrays.asList(classMetadataFile)); assertThat("Wrong summary for unknown class", item1.getSummary(), - is("Bla bla UnknownClass bla")); + is("Bla bla UnknownClass bla")); assertThat("Wrong syntax description", item1.getSyntax().getParameters().get(0).getDescription(), - is("One two SomeClass#someMethod(String param) three")); + is("One two SomeClass#someMethod(String param) three")); assertThat("Wrong summary for known class", item2.getSummary(), - is("Bla bla SomeClass#someMethod(String param) bla")); + is("Bla bla SomeClass#someMethod(String param) bla")); assertThat("Wrong summary for method", item3.getSummary(), - is("Bla bla #someMethod2(String p1, String p2) bla")); + is("Bla bla #someMethod2(String p1, String p2) bla")); } private MetadataFileItem buildMetadataFileItem(String uid, String value) { MetadataFileItem item = new MetadataFileItem(uid); item.setSummary( - String.format("Bla bla %s bla", value, value)); + String.format("Bla bla %s bla", value, value)); return item; } private void populateSyntax(MetadataFileItem item, String value) { Syntax syntax = new Syntax(); String methodParamDescription = String - .format("One two %s three", value, value); + .format("One two %s three", value, value); syntax.setParameters( - Arrays.asList(new MethodParameter("method param id", "method param type", methodParamDescription))); + Arrays.asList(new MethodParameter("method param id", "method param type", methodParamDescription))); item.setSyntax(syntax); } @@ -149,15 +151,15 @@ public void determineUidByLinkContent() { LookupContext lookupContext = new LookupContext(lookup, lookup); assertThat("Wrong result for class", ymlFilesBuilder. - resolveUidByLookup("SomeClass", lookupContext), is("a.b.c.SomeClass")); + resolveUidByLookup("SomeClass", lookupContext), is("a.b.c.SomeClass")); assertThat("Wrong result for method", ymlFilesBuilder. - resolveUidByLookup("SomeClass#someMethod()", lookupContext), is("a.b.c.SomeClass.someMethod()")); + resolveUidFromLinkContent("SomeClass#someMethod()", lookupContext), is("a.b.c.SomeClass.someMethod()")); assertThat("Wrong result for method with param", ymlFilesBuilder. - resolveUidByLookup("SomeClass#someMethod(String param)", lookupContext), - is("a.b.c.SomeClass.someMethod(String param)")); + resolveUidFromLinkContent("SomeClass#someMethod(String param)", lookupContext), + is("a.b.c.SomeClass.someMethod(String param)")); assertThat("Wrong result for unknown class", ymlFilesBuilder. - resolveUidByLookup("UnknownClass", lookupContext), is("")); + resolveUidByLookup("UnknownClass", lookupContext), is("")); assertThat("Wrong result for null", ymlFilesBuilder.resolveUidByLookup(null, lookupContext), is("")); assertThat("Wrong result for whitespace", ymlFilesBuilder.resolveUidByLookup(" ", lookupContext), is("")); } @@ -181,7 +183,7 @@ public void expandComplexGenericsInReferences() { assertThat("Wrong references amount", references.size(), is(4)); assertThat("Wrong references content", - references.stream().map(MetadataFileItem::getUid).collect(Collectors.toList()), - hasItems("a.b.c.List", "df.mn.ClassOne", "tr.T", "a.b.c.List>")); + references.stream().map(MetadataFileItem::getUid).collect(Collectors.toList()), + hasItems("a.b.c.List", "df.mn.ClassOne", "tr.T", "a.b.c.List>")); } } diff --git a/src/test/java/com/microsoft/samples/SuperHero.java b/src/test/java/com/microsoft/samples/SuperHero.java index a7c9ea4..25d2da2 100644 --- a/src/test/java/com/microsoft/samples/SuperHero.java +++ b/src/test/java/com/microsoft/samples/SuperHero.java @@ -1,9 +1,10 @@ package com.microsoft.samples; import com.microsoft.samples.subpackage.Person; -import java.io.Serializable; import org.apache.commons.lang3.StringUtils; +import java.io.Serializable; + /** * Hero is the main entity we will be using to something * @@ -18,6 +19,7 @@ public class SuperHero extends Person implements Serializable, Cloneable { private String uniquePower; private int health; private int defense; + String hobby; public final String SOME_PUBLIC_STRING = "So important string value"; @@ -98,6 +100,7 @@ public void setDefense(int defense) { this.defense = defense; } - private void somePrivateMethod() { - } + private void somePrivateMethod() {} + + void somePackagePrivateMethod() {} } diff --git a/src/test/java/com/microsoft/util/ElementUtilTest.java b/src/test/java/com/microsoft/util/ElementUtilTest.java index 2172d09..4d3780c 100644 --- a/src/test/java/com/microsoft/util/ElementUtilTest.java +++ b/src/test/java/com/microsoft/util/ElementUtilTest.java @@ -1,24 +1,23 @@ package com.microsoft.util; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - import com.google.testing.compile.CompilationRule; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import javax.lang.model.element.Element; +import javax.lang.model.util.Elements; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; -import javax.lang.model.element.Element; -import javax.lang.model.util.Elements; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; @RunWith(MockitoJUnitRunner.class) public class ElementUtilTest { @@ -74,4 +73,28 @@ public void matchAnyPattern() { assertFalse(elementUtil.matchAnyPattern(patterns, "EngineFive")); assertFalse(elementUtil.matchAnyPattern(patterns, "com.ms.Awesome")); } + + @Test + public void isPackagePrivate() { + Element element = elements.getTypeElement("com.microsoft.samples.SuperHero"); + + List result = element.getEnclosedElements() + .stream().filter(e -> ElementUtil.isPackagePrivate(e)).collect(Collectors.toList()); + + assertThat("Wrong result list size", result.size(), is(2)); + assertThat("Unexpected package private field", String.valueOf(result.get(0)), is("hobby")); + assertThat("Unexpected package private method", String.valueOf(result.get(1)), is("somePackagePrivateMethod()")); + } + + @Test + public void isPrivateOrPackagePrivate() { + Element element = elements.getTypeElement("com.microsoft.samples.SuperHero"); + + List result = element.getEnclosedElements() + .stream().filter(e -> ElementUtil.isPrivateOrPackagePrivate(e)).collect(Collectors.toList()); + + assertThat("Wrong result list size", result.size(), is(7)); + assertThat("Unexpected private method", String.valueOf(result.get(5)), is("somePrivateMethod()")); + assertThat("Unexpected package private method", String.valueOf(result.get(6)), is("somePackagePrivateMethod()")); + } }