Skip to content

Commit

Permalink
[#162][#163] Fixed issues for getting outer and inner types and indir…
Browse files Browse the repository at this point in the history
…ectly getting correct binary names. Now all type like class, interface, annotations, enums and records will be taken into account.
  • Loading branch information
tobiasstamann committed Dec 11, 2024
1 parent 3722ff6 commit f2fc768
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 25 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ target
.classpath
*.iml
.idea
atlassian-ide-plugin.xml
atlassian-ide-plugin.xml
**/dependency-reduced-pom.xml
**/.factorypath
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import io.toolisticon.aptk.tools.matcher.impl.IsTypeElementMatcher;
import io.toolisticon.aptk.tools.matcher.impl.IsTypeEqualFqnMatcher;
import io.toolisticon.aptk.tools.matcher.impl.IsTypeEqualMatcher;
import io.toolisticon.aptk.tools.matcher.impl.IsTypeMatcher;
import io.toolisticon.aptk.tools.matcher.impl.IsVariableElementMatcher;

import javax.lang.model.element.Element;
Expand Down Expand Up @@ -320,6 +321,11 @@ protected AptkCoreMatchers() {
* Matcher to check if passed element represents a parameter.
*/
public final static IsElementBasedCoreMatcher<VariableElement> IS_PARAMETER = new IsElementBasedCoreMatcher<>(new IsParameterMatcher<>(), CoreMatcherValidationMessages.IS_PARAMETER);
/**
* Matcher to check if passed element represents a type.(Is either CLASS, INTERFACE, ANNOTATION_TYPE, ENUM or RECORD)
*
*/
public final static IsElementBasedCoreMatcher<TypeElement> IS_TYPE = new IsElementBasedCoreMatcher<>(new IsTypeMatcher<>(), CoreMatcherValidationMessages.IS_TYPE);


}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public enum CoreMatcherValidationMessages implements ValidationMessage {
IS_PACKAGE_ELEMENT("CM_IS_PACKAGE_ELEMENT", "Element must ${0} be a PackageElement"),

IS_CLASS("CM_IS_CLASS", "Element must ${0} represent a Class"),
IS_TYPE("CM_IS_TYPE", "Element must ${0} represent either a class, an interface, an enum, an annotation type or a record"),
IS_ENUM("CM_IS_ENUM", "Element must ${0} represent an enum"),

IS_RECORD("CM_IS_ENUM", "Element must ${0} represent a record"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.toolisticon.aptk.tools.matcher.impl;

import io.toolisticon.aptk.tools.ElementUtils;
import io.toolisticon.aptk.tools.matcher.ImplicitMatcher;

import javax.lang.model.element.Element;


/**
* Implicit matcher that checks if a passed element is a type (i.e of Kind CLASS, INTERFACE or RECORD).
*/
public class IsTypeMatcher<ELEMENT extends Element> implements ImplicitMatcher<ELEMENT> {

@Override
public boolean check(ELEMENT element) {
return ElementUtils.CheckKindOfElement.isClass(element)
|| ElementUtils.CheckKindOfElement.isInterface(element)
|| ElementUtils.CheckKindOfElement.isEnum(element)
|| ElementUtils.CheckKindOfElement.isAnnotation(element)
|| ElementUtils.CheckKindOfElement.isRecord(element);
}

}


Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
Expand All @@ -26,7 +28,24 @@
public class TypeElementWrapper extends ElementWrapper<TypeElement> {

private final static String TYPE_ELEMENT_CLASS_NAME = "javax.lang.model.element.TypeElement";


private static ElementKind[] TYPE_ELEMENT_KINDS;

static {

// need to handle record kind via reflection as long as java versions smaller as 16 are supported.
try {
ElementKind[] typeKinds = {ElementKind.CLASS, ElementKind.INTERFACE, ElementKind.ENUM, ElementKind.ANNOTATION_TYPE, ElementKind.valueOf("RECORD")};
TYPE_ELEMENT_KINDS = typeKinds;
} catch (IllegalArgumentException e) {
ElementKind[] typeKinds = {ElementKind.CLASS, ElementKind.INTERFACE, ElementKind.ENUM, ElementKind.ANNOTATION_TYPE};
TYPE_ELEMENT_KINDS = typeKinds;
}


}


/**
* Hidden constructor.
*
Expand Down Expand Up @@ -253,30 +272,30 @@ public List<ExecutableElementWrapper> getConstructors(Modifier... modifier) {
*/
public List<TypeElementWrapper> getInnerTypes(Modifier... modifier) {
return FluentElementFilter.createFluentElementFilter(
this.element.getEnclosedElements()).applyFilter(AptkCoreMatchers.IS_CLASS)
this.element.getEnclosedElements()).applyFilter(AptkCoreMatchers.IS_TYPE)
.applyFilter(AptkCoreMatchers.BY_MODIFIER).filterByAllOf(modifier)
.getResult()
.stream().map(TypeElementWrapper::wrap).collect(Collectors.toList());
}

/**
* Returns the direct outer type for nested classes.
* Returned type may not be a top level type, since java allows nested classes in nested classes.
* Returns an Optional containing the direct outer type element for nested types.
* Returned type may not be a top level type, since java allows nested types in nested types.
*
* @return an Optional containing the outer type of nested classes, if present.
* @return an Optional containing the outer type of nested types, if present.
*/
public Optional<TypeElementWrapper> getOuterType() {
if (this.element.getNestingKind() != NestingKind.MEMBER) {
return Optional.empty();
}

return Optional.of(TypeElementWrapper.wrap(ElementUtils.AccessEnclosingElements.<TypeElement>getFirstEnclosingElementOfKind(this.element, ElementKind.CLASS)));
return Optional.of(TypeElementWrapper.wrap(ElementUtils.AccessEnclosingElements.<TypeElement>getFirstEnclosingElementOfKind(this.element, TYPE_ELEMENT_KINDS)));
}

/**
* Returns the direct outer type for nested classes.
* Returns the direct outer type element for nested types.
*
* @return an Optional containing the outer type of nested classes, if present.
* @return an Optional containing the outer type of nested types, if present.
*/
public Optional<TypeElementWrapper> getOuterTopLevelType() {
if (this.element.getNestingKind() != NestingKind.MEMBER) {
Expand Down Expand Up @@ -384,4 +403,6 @@ public static Optional<TypeElementWrapper> getByTypeMirror(TypeMirror typeMirror
public static Optional<TypeElementWrapper> getByTypeMirror(TypeMirrorWrapper typeMirror) {
return typeMirror.getTypeElement();
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,6 @@ static class NestedNestedKind {
}



@Test
public void test_getBinaryName_ofDeclaredType() {
Cute.unitTest().when().passInElement().<VariableElement>fromClass(NestedKind.class)
Expand Down Expand Up @@ -804,6 +803,37 @@ public void test_getBinaryName_ofArray() {
}
}).executeTest();
}

static class BinaryNameOfMixedCaseClass {

interface BinaryNameOfMixedCaseInterface {

@interface BinaryNameOfMixedCaseAnnotation {

@PassIn
enum BinaryNameOfMixedCaseEnum {

}

}

}

}

@Test
public void test_getBinaryName_of_mixed_case_multi_level_nested_types() {
Cute.unitTest().when().passInElement().<TypeElement>fromClass(BinaryNameOfMixedCaseClass.BinaryNameOfMixedCaseInterface.BinaryNameOfMixedCaseAnnotation.BinaryNameOfMixedCaseEnum.class)
.intoUnitTest((processingEnvironment, element) -> {
try {
ToolingProvider.setTooling(processingEnvironment);

MatcherAssert.assertThat(TypeMirrorWrapper.wrap(element.asType()).getBinaryName(), Matchers.is(TypeMirrorWrapperTest.class.getCanonicalName() + "$" + BinaryNameOfMixedCaseClass.class.getSimpleName() + "$" + BinaryNameOfMixedCaseClass.BinaryNameOfMixedCaseInterface.class.getSimpleName() + "$" + BinaryNameOfMixedCaseClass.BinaryNameOfMixedCaseInterface.BinaryNameOfMixedCaseAnnotation.class.getSimpleName() + "$" + BinaryNameOfMixedCaseClass.BinaryNameOfMixedCaseInterface.BinaryNameOfMixedCaseAnnotation.BinaryNameOfMixedCaseEnum.class.getSimpleName()));

} finally {
ToolingProvider.clearTooling();
}
}).executeTest();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import javax.lang.model.element.TypeElement;
import java.io.Serializable;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -201,11 +202,11 @@ private GetMethodTestClass(String a) {

}

private static class staticInnerClass {
private static class StaticInnerClass {

}

private class innerClass {
private class InnerClass {

}

Expand All @@ -226,6 +227,18 @@ private void privateMethod() {
private void methodWithParameters(String a, Integer b, int c, Object... d) {

}

enum TestEnum {
INSTANCE;
}

interface TestInterface {

}

@interface TestAnnotation {

}

}

Expand Down Expand Up @@ -341,9 +354,16 @@ public void test_getInnerTypes() {

TypeElementWrapper unit = TypeElementWrapper.wrap(element);

MatcherAssert.assertThat(unit.getInnerTypes().stream().map(ElementWrapper::getSimpleName).collect(Collectors.toList()), Matchers.containsInAnyOrder("innerClass", "staticInnerClass"));
MatcherAssert.assertThat(unit.getInnerTypes(Modifier.PRIVATE).stream().map(ElementWrapper::getSimpleName).collect(Collectors.toList()), Matchers.containsInAnyOrder("innerClass", "staticInnerClass"));
MatcherAssert.assertThat(unit.getInnerTypes(Modifier.PRIVATE, Modifier.STATIC).stream().map(ElementWrapper::getSimpleName).collect(Collectors.toList()), Matchers.containsInAnyOrder("staticInnerClass"));
// Unfortunately we cannot test records directly ...
MatcherAssert.assertThat(unit.getInnerTypes().stream().map(ElementWrapper::getSimpleName).collect(Collectors.toList()), Matchers.containsInAnyOrder(
GetMethodTestClass.InnerClass.class.getSimpleName().toString(),
GetMethodTestClass.StaticInnerClass.class.getSimpleName().toString(),
GetMethodTestClass.TestAnnotation.class.getSimpleName().toString(),
GetMethodTestClass.TestEnum.class.getSimpleName().toString(),
GetMethodTestClass.TestInterface.class.getSimpleName().toString()
));
MatcherAssert.assertThat(unit.getInnerTypes(Modifier.PRIVATE).stream().map(ElementWrapper::getSimpleName).collect(Collectors.toList()), Matchers.containsInAnyOrder("InnerClass", "StaticInnerClass"));
MatcherAssert.assertThat(unit.getInnerTypes(Modifier.PRIVATE, Modifier.STATIC).stream().map(ElementWrapper::getSimpleName).collect(Collectors.toList()), Matchers.containsInAnyOrder("StaticInnerClass"));

} finally {
ToolingProvider.clearTooling();
Expand All @@ -354,12 +374,22 @@ public void test_getInnerTypes() {

private static class GetOuterTypeTestClass {

class MidInnerClass {

@PassIn
private class innerMostClass {

}
interface OfTypeInterface {

@interface OfTypeAnnotation {

enum OfTypeEnum {

INSTANCE;

@PassIn
static class InnerMostClass {

}

}

}

}

Expand All @@ -374,7 +404,24 @@ public void test_getOuterType() {

TypeElementWrapper unit = TypeElementWrapper.wrap(element);

MatcherAssert.assertThat(unit.getOuterType().get().getQualifiedName(), Matchers.is(GetOuterTypeTestClass.MidInnerClass.class.getCanonicalName()));
Optional<TypeElementWrapper> outerType = unit.getOuterType();
MatcherAssert.assertThat(outerType.get().getQualifiedName(), Matchers.is(GetOuterTypeTestClass.OfTypeInterface.OfTypeAnnotation.OfTypeEnum.class.getCanonicalName()));

outerType = outerType.get().getOuterType();
MatcherAssert.assertThat(outerType.get().getQualifiedName(), Matchers.is(GetOuterTypeTestClass.OfTypeInterface.OfTypeAnnotation.class.getCanonicalName()));

outerType = outerType.get().getOuterType();
MatcherAssert.assertThat(outerType.get().getQualifiedName(), Matchers.is(GetOuterTypeTestClass.OfTypeInterface.class.getCanonicalName()));

outerType = outerType.get().getOuterType();
MatcherAssert.assertThat(outerType.get().getQualifiedName(), Matchers.is(GetOuterTypeTestClass.class.getCanonicalName()));

outerType = outerType.get().getOuterType();
MatcherAssert.assertThat(outerType.get().getQualifiedName(), Matchers.is(TypeElementWrapperTest.class.getCanonicalName()));

outerType = outerType.get().getOuterType();
MatcherAssert.assertThat(outerType.isEmpty(), Matchers.is(true));

MatcherAssert.assertThat(unit.getOuterTopLevelType().get().getQualifiedName(), Matchers.is(TypeElementWrapperTest.class.getCanonicalName()));

MatcherAssert.assertThat("Should return empty Optional for Top Level Class", !unit.getOuterTopLevelType().get().getOuterType().isPresent());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.toolisticon.aptk.tools.ElementUtils;
import io.toolisticon.aptk.tools.MessagerUtils;
import io.toolisticon.aptk.tools.corematcher.AptkCoreMatchers;
import io.toolisticon.aptk.tools.corematcher.IsCoreMatcher;
import io.toolisticon.aptk.tools.fluentfilter.FluentElementFilter;
import io.toolisticon.aptk.tools.fluentvalidator.FluentElementValidator;
import io.toolisticon.cute.CompileTestBuilder;
Expand Down Expand Up @@ -279,7 +280,7 @@ public void aptkUnitTest(ProcessingEnvironment processingEnvironment, TypeElemen


// Test
MatcherAssert.assertThat("Should be successfully validated", FluentElementValidator.createFluentElementValidator(elements.get(0)).is(AptkCoreMatchers.IS_EXECUTABLE_ELEMENT).validateAndIssueMessages())
MatcherAssert.assertThat("Should be successfully validated", FluentElementValidator.createFluentElementValidator(elements.get(0)).is( AptkCoreMatchers.IS_EXECUTABLE_ELEMENT).validateAndIssueMessages())
;

}
Expand Down

0 comments on commit f2fc768

Please sign in to comment.