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: Left-hand navigation API-versioned package organization #203

Merged
merged 19 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import com.google.gson.annotations.SerializedName;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;

public class RepoMetadata {
Expand Down Expand Up @@ -140,10 +142,12 @@ public String getMavenLink() {

public RepoMetadata parseRepoMetadata(String fileName) {
Gson gson = new Gson();
try (FileReader reader = new FileReader(fileName)) {
Path path = Paths.get(fileName);
try (FileReader reader = new FileReader(path.toFile())) {
return gson.fromJson(reader, RepoMetadata.class);
} catch (IOException e) {
throw new RuntimeException(".repo-metadata.json is not found", e);
throw new RuntimeException(
".repo-metadata.json is not found @ " + path.toAbsolutePath().normalize(), e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@

import com.microsoft.lookup.ClassItemsLookup;
import com.microsoft.lookup.ClassLookup;
import com.microsoft.lookup.PackageLookup;
import com.microsoft.model.ApiVersionPackageToc;
import com.microsoft.model.MetadataFile;
import com.microsoft.model.MetadataFileItem;
import com.microsoft.model.StubPackageToc;
import com.microsoft.model.TocItem;
import com.microsoft.model.TocTypeMap;
import com.microsoft.util.ElementUtil;
Expand All @@ -32,31 +35,141 @@
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementFilter;

class ClassBuilder {
private ElementUtil elementUtil;
private ClassLookup classLookup;
private ClassItemsLookup classItemsLookup;
private String outputPath;
private ReferenceBuilder referenceBuilder;

private final ElementUtil elementUtil;
private final ClassLookup classLookup;
private final ClassItemsLookup classItemsLookup;
private final String outputPath;
private final PackageLookup packageLookup;
private final ReferenceBuilder referenceBuilder;

ClassBuilder(
ElementUtil elementUtil,
ClassLookup classLookup,
ClassItemsLookup classItemsLookup,
String outputPath,
PackageLookup packageLookup,
ReferenceBuilder referenceBuilder) {
this.elementUtil = elementUtil;
this.classLookup = classLookup;
this.classItemsLookup = classItemsLookup;
this.outputPath = outputPath;
this.packageLookup = packageLookup;
this.referenceBuilder = referenceBuilder;
}

void buildFilesForInnerClasses(
Element element, TocTypeMap tocTypeMap, List<MetadataFile> container) {
List<TocItem> buildFilesForPackage(PackageElement pkg, List<MetadataFile> classMetadataFiles) {
if (packageLookup.isApiVersionPackage(pkg) && containsAtLeastOneClient(pkg)) {
alicejli marked this conversation as resolved.
Show resolved Hide resolved
// API Version package organization is a nested list organized by GAPIC concepts
ApiVersionPackageToc apiVersionPackageToc = new ApiVersionPackageToc();
buildFilesForApiVersionPackage(pkg, apiVersionPackageToc, classMetadataFiles);
return apiVersionPackageToc.toList();

} else if (packageLookup.isApiVersionStubPackage(pkg)) {
StubPackageToc stubPackageToc = new StubPackageToc();
buildFilesForStubPackage(pkg, stubPackageToc, classMetadataFiles);
return stubPackageToc.toList();

} else {
// Standard package organization is a flat list organized by Java type
TocTypeMap typeMap = new TocTypeMap();
buildFilesForStandardPackage(pkg, typeMap, classMetadataFiles);
return typeMap.toList();
}
}

private void buildFilesForApiVersionPackage(
Element element,
ApiVersionPackageToc apiVersionPackageToc,
List<MetadataFile> classMetadataFiles) {
for (TypeElement classElement : elementUtil.extractSortedElements(element)) {
String uid = classLookup.extractUid(classElement);
String name = classLookup.extractTocName(classElement);
String status = classLookup.extractStatus(classElement);
TocItem tocItem = new TocItem(uid, name, status);

// The order of these checks matter!
// Ex: a paging response class would change its category if "isPagingClass" is checked first.
if (classElement.getKind() == ElementKind.INTERFACE) {
apiVersionPackageToc.addInterface(tocItem);
} else if (isClient(classElement)) {
apiVersionPackageToc.addClient(tocItem);
} else if (name.endsWith("Response") || name.endsWith("Request")) {
apiVersionPackageToc.addRequestOrResponse(tocItem);
} else if (name.endsWith("Settings")) {
apiVersionPackageToc.addSettings(tocItem);
} else if (name.endsWith("Builder")) {
apiVersionPackageToc.addBuilder(tocItem);
} else if (classElement.getKind() == ElementKind.ENUM) {
apiVersionPackageToc.addEnum(tocItem);
} else if (name.endsWith("Exception")) {
apiVersionPackageToc.addException(tocItem);
} else if (isGeneratedMessage(classElement)) {
apiVersionPackageToc.addMessage(tocItem);
} else if (isPagingClass(classElement)) {
apiVersionPackageToc.addPaging(tocItem);
} else if (isResourceName(classElement)) {
apiVersionPackageToc.addResourceName(tocItem);
} else {
apiVersionPackageToc.addUncategorized(tocItem);
}

classMetadataFiles.add(buildClassYmlFile(classElement));
buildFilesForApiVersionPackage(classElement, apiVersionPackageToc, classMetadataFiles);
}
}

private void buildFilesForStubPackage(
Element element, StubPackageToc packageToc, List<MetadataFile> classMetadataFiles) {
for (TypeElement classElement : elementUtil.extractSortedElements(element)) {
String uid = classLookup.extractUid(classElement);
String name = classLookup.extractTocName(classElement);
String status = classLookup.extractStatus(classElement);
TocItem tocItem = new TocItem(uid, name, status);

if (name.endsWith("Stub")) {
packageToc.addStub(tocItem);
} else if (name.contains("Settings")) {
packageToc.addSettings(tocItem);
} else if (name.endsWith("CallableFactory")) {
packageToc.addCallableFactory(tocItem);
} else {
packageToc.addUncategorized(tocItem);
}

classMetadataFiles.add(buildClassYmlFile(classElement));
buildFilesForStubPackage(classElement, packageToc, classMetadataFiles);
}
}

boolean containsAtLeastOneClient(PackageElement pkg) {
return elementUtil.extractSortedElements(pkg).stream().anyMatch(this::isClient);
}

boolean isClient(TypeElement classElement) {
return classLookup.extractTocName(classElement).endsWith("Client");
}

boolean isResourceName(TypeElement classElement) {
return classElement.getInterfaces().stream()
.anyMatch(i -> String.valueOf(i).contains("ResourceName"));
}

boolean isGeneratedMessage(TypeElement classElement) {
return String.valueOf(classElement.getSuperclass()).contains("GeneratedMessage");
}

boolean isPagingClass(TypeElement classElement) {
return String.valueOf(classElement.getSuperclass()).contains(".paging.");
}

void buildFilesForStandardPackage(
Element element, TocTypeMap tocTypeMap, List<MetadataFile> classMetadataFiles) {
for (TypeElement classElement : elementUtil.extractSortedElements(element)) {
String uid = classLookup.extractUid(classElement);
String name = classLookup.extractTocName(classElement);
Expand All @@ -73,8 +186,8 @@ void buildFilesForInnerClasses(
tocTypeMap.get(ElementKind.CLASS.name()).add(new TocItem(uid, name, status));
}

container.add(buildClassYmlFile(classElement));
buildFilesForInnerClasses(classElement, tocTypeMap, container);
classMetadataFiles.add(buildClassYmlFile(classElement));
buildFilesForStandardPackage(classElement, tocTypeMap, classMetadataFiles);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import com.microsoft.model.MetadataFileItem;
import com.microsoft.model.TocFile;
import com.microsoft.model.TocItem;
import com.microsoft.model.TocTypeMap;
import com.microsoft.util.ElementUtil;
import com.microsoft.util.FileUtil;
import java.util.ArrayList;
Expand Down Expand Up @@ -78,6 +77,7 @@ public YmlFilesBuilder(
classLookup,
new ClassItemsLookup(environment, elementUtil),
outputPath,
packageLookup,
referenceBuilder);
}

Expand Down Expand Up @@ -192,27 +192,20 @@ private TocItem buildPackage(PackageElement element) {
packageMetadataFiles.add(packageBuilder.buildPackageMetadataFile(element));

// build classes/interfaces/enums/exceptions/annotations
TocTypeMap typeMap = new TocTypeMap();
classBuilder.buildFilesForInnerClasses(element, typeMap, classMetadataFiles);
packageTocItem.getItems().addAll(joinTocTypeItems(typeMap));
packageTocItem
.getItems()
.addAll(classBuilder.buildFilesForPackage(element, classMetadataFiles));

// build stubs
TocItem stubPackagesItem = new TocItem("Stub packages", "Stub packages", "");
packageLookup
.findStubPackages(element, allPackages)
.forEach((PackageElement stub) -> packageTocItem.getItems().add(buildPackage(stub)));
.forEach((PackageElement stub) -> stubPackagesItem.getItems().add(buildPackage(stub)));
if (!stubPackagesItem.getItems().isEmpty()) {
packageTocItem.getItems().add(stubPackagesItem);
}

return packageTocItem;
}
}

List<TocItem> joinTocTypeItems(TocTypeMap tocTypeMap) {
return tocTypeMap.getTitleList().stream()
.filter(kindTitle -> tocTypeMap.get(kindTitle.getElementKind()).size() > 0)
.flatMap(
kindTitle -> {
tocTypeMap.get(kindTitle.getElementKind()).add(0, new TocItem(kindTitle.getTitle()));
return tocTypeMap.get(kindTitle.getElementKind()).stream();
})
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public boolean run(DocletEnvironment environment) {
String artifactVersion = System.getenv("artifactVersion");
String librariesBomVersion = System.getenv("librariesBomVersion");
String repoMetadataFilePath = System.getenv("repoMetadataFilePath");
Objects.requireNonNull(
repoMetadataFilePath, "Environment variable 'repoMetadataFilePath' must not be null.");
reporter.print(Kind.NOTE, "Environment variable artifactVersion: " + artifactVersion);
reporter.print(Kind.NOTE, "Environment variable librariesBomVersion: " + librariesBomVersion);
reporter.print(Kind.NOTE, "Environment variable repoMetadataFilePath: " + repoMetadataFilePath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ public Optional<ApiVersion> extractApiVersion(String name) {
return ApiVersion.parse(getLeafPackage(name));
}

public boolean isApiVersionPackage(PackageElement pkg) {
return extractApiVersion(pkg).isPresent();
}
burkedavison marked this conversation as resolved.
Show resolved Hide resolved

public enum PackageGroup {
VISIBLE,
OLDER_AND_PRERELEASE
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.microsoft.model;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;

public class ApiVersionPackageToc {
static final String CLIENTS = "Clients";
static final String REQUESTS_AND_RESPONSES = "Requests and responses";
static final String SETTINGS = "Settings";
static final String ALL_OTHERS = "All other classes and interfaces";
static final String BUILDERS = "Builders";
static final String ENUMS = "Enums";
static final String INTERFACES = "Interfaces";
static final String MESSAGES = "Messages";
static final String EXCEPTIONS = "Exceptions";
static final String PAGING = "Paging";
alicejli marked this conversation as resolved.
Show resolved Hide resolved
static final String RESOURCE_NAMES = "Resource names";
static final String UNCATEGORIZED = "Other";

private final LinkedHashMap<String, List<TocItem>> visibleCategories = new LinkedHashMap<>();
private final LinkedHashMap<String, List<TocItem>> hiddenCategories = new LinkedHashMap<>();

public ApiVersionPackageToc() {
// Order here determines final organization order.
visibleCategories.put(CLIENTS, new ArrayList<>());
visibleCategories.put(SETTINGS, new ArrayList<>());
visibleCategories.put(REQUESTS_AND_RESPONSES, new ArrayList<>());

hiddenCategories.put(BUILDERS, new ArrayList<>());
hiddenCategories.put(ENUMS, new ArrayList<>());
hiddenCategories.put(EXCEPTIONS, new ArrayList<>());
hiddenCategories.put(MESSAGES, new ArrayList<>());
hiddenCategories.put(PAGING, new ArrayList<>());
hiddenCategories.put(RESOURCE_NAMES, new ArrayList<>());
hiddenCategories.put(INTERFACES, new ArrayList<>());
hiddenCategories.put(UNCATEGORIZED, new ArrayList<>());
}

public void addClient(TocItem tocItem) {
visibleCategories.get(CLIENTS).add(tocItem);
}

public void addRequestOrResponse(TocItem tocItem) {
visibleCategories.get(REQUESTS_AND_RESPONSES).add(tocItem);
}

public void addSettings(TocItem tocItem) {
visibleCategories.get(SETTINGS).add(tocItem);
}

public void addBuilder(TocItem tocItem) {
hiddenCategories.get(BUILDERS).add(tocItem);
}

public void addEnum(TocItem tocItem) {
hiddenCategories.get(ENUMS).add(tocItem);
}

public void addException(TocItem tocItem) {
hiddenCategories.get(EXCEPTIONS).add(tocItem);
}

public void addInterface(TocItem tocItem) {
hiddenCategories.get(INTERFACES).add(tocItem);
}

public void addMessage(TocItem tocItem) {
hiddenCategories.get(MESSAGES).add(tocItem);
}

public void addUncategorized(TocItem tocItem) {
hiddenCategories.get(UNCATEGORIZED).add(tocItem);
}

public void addPaging(TocItem tocItem) {
hiddenCategories.get(PAGING).add(tocItem);
}

public void addResourceName(TocItem tocItem) {
hiddenCategories.get(RESOURCE_NAMES).add(tocItem);
}

/** Build a list of TocItems for inclusion in the library's table of contents */
public List<TocItem> toList() {
List<TocItem> toc = new ArrayList<>();

visibleCategories.forEach(
(name, category) -> {
if (!category.isEmpty()) {
toc.add(createCategory(name, category));
}
});

TocItem allOthers = new TocItem(ALL_OTHERS, ALL_OTHERS, null);
hiddenCategories.forEach(
(name, category) -> {
if (!category.isEmpty()) {
allOthers.getItems().add(createCategory(name, category));
}
});
if (allOthers.getItems().size() > 0) {
toc.add(allOthers);
}

return toc;
}

private TocItem createCategory(String name, List<TocItem> items) {
TocItem category = new TocItem(name, name, null);
items.sort(Comparator.comparing(TocItem::getName));
category.getItems().addAll(items);
return category;
}
}
Loading