Skip to content
This repository has been archived by the owner on Aug 9, 2022. It is now read-only.

Commit

Permalink
implement comment inheritance for methods (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
anmeng10101 authored Jun 4, 2020
1 parent d09486e commit 78fc76a
Show file tree
Hide file tree
Showing 22 changed files with 1,568 additions and 4 deletions.
50 changes: 50 additions & 0 deletions src/main/java/com/microsoft/lookup/ClassItemsLookup.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
package com.microsoft.lookup;

import com.microsoft.lookup.model.ExtendedMetadataFileItem;

import com.microsoft.model.ExceptionItem;
import com.microsoft.model.MethodParameter;
import com.microsoft.model.Return;

import com.microsoft.util.CommentHelper;
import com.microsoft.util.Utils;

import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.DocTree.Kind;
import com.sun.source.doctree.ParamTree;
import com.sun.source.doctree.ReturnTree;
import com.sun.source.doctree.ThrowsTree;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import javax.lang.model.element.*;
import javax.lang.model.type.TypeKind;

Expand Down Expand Up @@ -64,6 +71,7 @@ protected ExtendedMetadataFileItem buildMetadataFileItem(Element element) {
result.setReturn(extractReturn(exeElement));
if (exeElement.getKind() == ElementKind.METHOD) {
result.setOverridden(extractOverriddenUid(utils.overriddenMethod(exeElement)));
result.setSummary(getInheritedInlineCommentString(exeElement));
}
}
result.setNameWithType(String.format("%s.%s", classSNameWithGenericsSupport, result.getName()));
Expand Down Expand Up @@ -145,4 +153,46 @@ String extractOverriddenUid(ExecutableElement ovr) {

return "";
}

/**
* If the item being inherited from is declared from external compiled package,
* or is declared in the packages like java.lang.Object,
* comments may be not available as doclet resolves from byte code.
*/
String getInheritedInlineCommentString(ExecutableElement exeElement) {
CommentHelper ch = getInheritedInlineTags(new CommentHelper(exeElement, utils));
// Remove unresolved "@inheritDoc" tag.
List<? extends DocTree> dctree = utils.removeBlockTag(ch.inlineTags, DocTree.Kind.INHERIT_DOC);
return replaceLinksAndCodes(dctree);
}

CommentHelper getInheritedInlineTags(CommentHelper input) {
CommentHelper output = input.copy();
if (!output.hasInheritDocTag()&& !output.isSimpleOverride()) {
return output;
}

CommentHelper inheritedSearchInput = input.copy();
ExecutableElement overriddenMethod = utils.overriddenMethod((ExecutableElement) input.element);

if (overriddenMethod != null) {
inheritedSearchInput.element = overriddenMethod;
CommentHelper ch = getInheritedInlineTags(inheritedSearchInput);
if (!ch.isSimpleOverride()) {
output = output.inherit(ch);
}
}

TypeElement encl = utils.getEnclosingTypeElement(input.element);
List<Element> implementedMethods = utils.getImplementedMethods(input.element.toString(), encl, new ArrayList<Element>());
for (Element implementedMethod : implementedMethods) {
inheritedSearchInput.element = implementedMethod;
CommentHelper ch = getInheritedInlineTags(inheritedSearchInput);
if (!ch.isSimpleOverride()) {
output = output.inherit(ch);
}
}

return output;
}
}
89 changes: 89 additions & 0 deletions src/main/java/com/microsoft/util/CommentHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.microsoft.util;

import com.sun.source.doctree.DocTree;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import javax.lang.model.element.Element;

public class CommentHelper {
public Element element;
public List<? extends DocTree> inlineTags = Collections.emptyList();
private Utils utils;
private boolean hasInheritDocTag = false;

public CommentHelper(Element element, Utils utils) {
this.element = element;
this.utils = utils;
this.inlineTags = utils.getFullBody(element);
this.hasInheritDocTag = utils.hasInlineTag(inlineTags, DocTree.Kind.INHERIT_DOC);
}

public CommentHelper(Element element, Utils utils, List<? extends DocTree> inlineTags) {
this.element = element;
this.utils = utils;
this.inlineTags = inlineTags;
this.hasInheritDocTag = utils.hasInlineTag(inlineTags, DocTree.Kind.INHERIT_DOC);
}

/**
* Returns true if the method has no comments, or a lone &commat;inheritDoc.
*
* @return true if there are no comments, false otherwise
*/
public boolean isSimpleOverride() {
return inlineTags.isEmpty() ||
(inlineTags.size() == 1 && hasInheritDocTag);
}

public boolean hasInheritDocTag(){
return this.hasInheritDocTag;
}

public CommentHelper copy() {
if (this.element == null) {
throw new NullPointerException();
}
CommentHelper clone = new CommentHelper(this.element, this.utils);
return clone;
}

public CommentHelper inherit(CommentHelper chInheritFrom) {
List<? extends DocTree> mergedTags = new ArrayList<>();

if (this.isSimpleOverride())
mergedTags = chInheritFrom.inlineTags;
else {
mergedTags = inheritInlineTags(this, chInheritFrom);
}

return new CommentHelper(this.element, this.utils, mergedTags);
}

List<? extends DocTree> inheritInlineTags(CommentHelper origin, CommentHelper chInheritFrom) {
List<DocTree> mergedTags = new ArrayList<>();
if (!origin.isSimpleOverride() && !origin.hasInheritDocTag) {
return origin.inlineTags;
}

// Get the index of "{@inheritedDoc}".
int index = origin.inlineTags.stream().map(e -> e.getKind())
.collect(Collectors.toList())
.indexOf(DocTree.Kind.INHERIT_DOC);

// Replace the "{@inheritedDoc}" with inherited inlineTags.
mergedTags = origin.inlineTags.stream().collect(Collectors.toList());
mergedTags.remove(index);

for (DocTree d : chInheritFrom.inlineTags
) {
mergedTags.add(index, d);
index++;
}

return mergedTags;
}
}
61 changes: 57 additions & 4 deletions src/main/java/com/microsoft/util/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@
import javax.lang.model.util.SimpleElementVisitor9;
import javax.lang.model.util.Types;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.*;
import java.util.stream.Collectors;

import jdk.javadoc.doclet.DocletEnvironment;
Expand Down Expand Up @@ -77,6 +74,21 @@ public boolean isSimpleOverride(ExecutableElement m) {
(fullBody.size() == 1 && fullBody.get(0).getKind().equals(DocTree.Kind.INHERIT_DOC));
}

public boolean hasInlineTag(List<? extends DocTree> inlineTags, DocTree.Kind kind) {
for (DocTree dt : inlineTags) {
if (dt.getKind() == kind) {
return true;
}
}
return false;
}

public Element getMemberBySignature(TypeElement te, ElementKind kind, String signature) {
return getMembers(te, kind).stream()
.filter(e -> e.toString().equals(signature))
.findFirst().orElse(null);
}

public TypeElement getObjectType() {
return elementUtils.getTypeElement("java.lang.Object");
}
Expand Down Expand Up @@ -193,6 +205,12 @@ public List<? extends DocTree> getBlockTags(Element element) {
return getBlockTags0(element, (DocTree.Kind[]) null);
}

public List<? extends DocTree> removeBlockTag(List<? extends DocTree> dctree, DocTree.Kind kind) {
return dctree.stream()
.filter(dc -> !dc.getKind().equals(kind))
.collect(Collectors.toList());
}

/**
* Returns a list of visible enclosed members of given kind,
* declared in this type element, and does not include
Expand All @@ -205,4 +223,39 @@ public List<? extends Element> getMembers(TypeElement te, ElementKind kind) {
.filter(e -> e.getKind() == kind && !isPrivateOrPackagePrivate(e))
.collect(Collectors.toList());
}

/**
* Returns a list of methods being implemented by given method.
* When a method in an interface overrides a method its superinterface,
* it will be considered as "implemented", instead of "overridden".
*
* @return a list of implemented methods
*/
public List<Element> getImplementedMethods(String signature, TypeElement encl, List<Element> implementedMethods) {
if (encl == null) {
return implementedMethods;
}

for (TypeElement interfaceType : getImplementedInterfaces(encl)) {
Element implementedMethod = getMemberBySignature(interfaceType, ElementKind.METHOD, signature);
if (implementedMethod != null) {
implementedMethods.add(implementedMethod);
}
// We need to search every implemented interface of the Inheritance chain.
getImplementedMethods(signature, interfaceType, implementedMethods);
}
return implementedMethods;
}

/**
* Returns a list of implemented interface type elements of given type element.
* Follow Standard doclet, search in the order of appearance following the word implements in declaration.
*
* @return a list of implemented interfaces
*/
public List<TypeElement> getImplementedInterfaces(TypeElement element) {
return element.getInterfaces().stream()
.map(e -> asTypeElement(e))
.collect(Collectors.toList());
}
}
28 changes: 28 additions & 0 deletions src/test/java/com/microsoft/samples/commentinheritance/Animal.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.microsoft.samples.commentinheritance;

/**
* Animal.
*/
public abstract class Animal implements Organism{
/**
* Breathe.
*/
public void breathe() {
}

/**
* Communicate verbally.
*/
public abstract void verballyCommunicate();

/**
* Feed offspring.
*/
public abstract void feed();

/**
* {@inheritDoc}
* Get kind from Animal.
*/
public abstract String getKind();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.microsoft.samples.commentinheritance;

/**
* Marks an Animal that eats other animals.
*/
public interface Carnivorous {
/**
* Eat the provided animal.
*
* @param animalBeingEaten Animal that will be eaten.
*/
void eat(Animal animalBeingEaten);

/**
* Get kind from Carnivorous.
*/
String getKind();
}
71 changes: 71 additions & 0 deletions src/test/java/com/microsoft/samples/commentinheritance/Dog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.microsoft.samples.commentinheritance;

import java.awt.Color;

import static java.lang.System.out;

/**
* Canine and man's best friend.
*/
public class Dog extends Mammal implements Omnivorous, Viviparous {
private final Color hairColor = null;

/**
* {@inheritDoc}
*
* @param otherAnimal Tasty treat.
*/
@Override
public void eat(final Animal otherAnimal) {
}

/**
* {@inheritDoc}
*
* @param plantToBeEaten Plant that this dog will eat.
*/
@Override
public void eat(final Plant plantToBeEaten) {
}

/**
* {@inheritDoc} Bark.
*/
public void verballyCommunicate() {
out.println("Woof!");
}

/**
* {@inheritDoc}
*
* @param numberPuppies Number of puppies being born.
*/
@Override
public void giveBirth(final int numberPuppies) {
}

/**
* Provide the color of the dog's hair.
*
* @return Color of the dog's fur.
*/
public Color getHairColor() {
return hairColor;
}

/**
* {@inheritDoc}
*/
@Override
public void feed() {
}

/**
* {@inheritDoc}
* Get kind from Dog.
*/
@Override
public String getKind() {
return "Dog";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.microsoft.samples.commentinheritance;

/**
* Marks animals that eat plants.
*/
public interface Herbivorous {
/**
* Eat the provided plant.
*
* @param plantToBeEaten Plant that will be eaten.
*/
void eat(Plant plantToBeEaten);

/**
* Get kind from Herbivorous.
*/
String getKind();

public class Plant {
}

}
Loading

0 comments on commit 78fc76a

Please sign in to comment.