diff --git a/archunit-example/example-junit4/src/test/java/com/tngtech/archunit/exampletest/junit4/SessionBeanRulesTest.java b/archunit-example/example-junit4/src/test/java/com/tngtech/archunit/exampletest/junit4/SessionBeanRulesTest.java index bf95c83497..cfc680d8dc 100644 --- a/archunit-example/example-junit4/src/test/java/com/tngtech/archunit/exampletest/junit4/SessionBeanRulesTest.java +++ b/archunit-example/example-junit4/src/test/java/com/tngtech/archunit/exampletest/junit4/SessionBeanRulesTest.java @@ -66,8 +66,8 @@ private static DescribedPredicate haveLocalBeanSubclass() { return new DescribedPredicate("have subclass that is a local bean") { @Override public boolean apply(JavaClass input) { - for (JavaClass subClass : input.getAllSubClasses()) { - if (isLocalBeanImplementation(subClass, input)) { + for (JavaClass subclass : input.getAllSubclasses()) { + if (isLocalBeanImplementation(subclass, input)) { return true; } } @@ -93,13 +93,13 @@ private static ArchCondition haveAUniqueImplementation() { @Override public void check(JavaClass businessInterface, ConditionEvents events) { events.add(new SimpleConditionEvent(businessInterface, - businessInterface.getAllSubClasses().size() <= 1, + businessInterface.getAllSubclasses().size() <= 1, describe(businessInterface))); } private String describe(JavaClass businessInterface) { return String.format("%s is implemented by %s", - businessInterface.getSimpleName(), joinNamesOf(businessInterface.getAllSubClasses())); + businessInterface.getSimpleName(), joinNamesOf(businessInterface.getAllSubclasses())); } private String joinNamesOf(Set implementations) { diff --git a/archunit-example/example-junit4/src/test/java/com/tngtech/archunit/exampletest/junit4/ThirdPartyRulesTest.java b/archunit-example/example-junit4/src/test/java/com/tngtech/archunit/exampletest/junit4/ThirdPartyRulesTest.java index a1811b3b02..3568cebb29 100644 --- a/archunit-example/example-junit4/src/test/java/com/tngtech/archunit/exampletest/junit4/ThirdPartyRulesTest.java +++ b/archunit-example/example-junit4/src/test/java/com/tngtech/archunit/exampletest/junit4/ThirdPartyRulesTest.java @@ -42,10 +42,10 @@ private static ArchCondition notCreateProblematicClassesOutsideOfWork target(is(constructor())).and(targetOwner(is(assignableTo(ThirdPartyClassWithProblem.class)))); DescribedPredicate> notFromWithinThirdPartyClass = - originOwner(is(not(assignableTo(ThirdPartyClassWithProblem.class)))).forSubType(); + originOwner(is(not(assignableTo(ThirdPartyClassWithProblem.class)))).forSubtype(); DescribedPredicate> notFromWorkaroundFactory = - originOwner(is(not(equivalentTo(ThirdPartyClassWorkaroundFactory.class)))).forSubType(); + originOwner(is(not(equivalentTo(ThirdPartyClassWorkaroundFactory.class)))).forSubtype(); DescribedPredicate> targetIsIllegalConstructorOfThirdPartyClass = constructorCallOfThirdPartyClass. diff --git a/archunit-example/example-junit4/src/test/resources/frozen/a81a2b54-5a18-4145-b544-7a580aba0425 b/archunit-example/example-junit4/src/test/resources/frozen/a81a2b54-5a18-4145-b544-7a580aba0425 index b3440d5d81..5f85d78b3e 100644 --- a/archunit-example/example-junit4/src/test/resources/frozen/a81a2b54-5a18-4145-b544-7a580aba0425 +++ b/archunit-example/example-junit4/src/test/resources/frozen/a81a2b54-5a18-4145-b544-7a580aba0425 @@ -2,8 +2,10 @@ Class has annotation member of type in (ServiceViolatingLayerRules.java:0) Class has annotation member of type in (ServiceViolatingLayerRules.java:0) Class is annotated with in (ServiceViolatingLayerRules.java:0) +Class extends class in (SpecialServiceHelper.java:0) Class implements interface in (ServiceImplementation.java:0) Constructor (com.tngtech.archunit.example.layers.service.ServiceViolatingLayerRules)> has parameter of type in (SomeMediator.java:0) +Constructor ()> calls constructor ()> in (SpecialServiceHelper.java:9) Field has type in (SomeMediator.java:0) Field has type in (SomeController.java:0) Field has type in (SomeController.java:0) diff --git a/archunit-example/example-junit5/src/test/java/com/tngtech/archunit/exampletest/junit5/SessionBeanRulesTest.java b/archunit-example/example-junit5/src/test/java/com/tngtech/archunit/exampletest/junit5/SessionBeanRulesTest.java index a8b1095d55..30acfacaaa 100644 --- a/archunit-example/example-junit5/src/test/java/com/tngtech/archunit/exampletest/junit5/SessionBeanRulesTest.java +++ b/archunit-example/example-junit5/src/test/java/com/tngtech/archunit/exampletest/junit5/SessionBeanRulesTest.java @@ -63,8 +63,8 @@ private static DescribedPredicate haveLocalBeanSubclass() { return new DescribedPredicate("have subclass that is a local bean") { @Override public boolean apply(JavaClass input) { - for (JavaClass subClass : input.getAllSubClasses()) { - if (isLocalBeanImplementation(subClass, input)) { + for (JavaClass subclass : input.getAllSubclasses()) { + if (isLocalBeanImplementation(subclass, input)) { return true; } } @@ -90,13 +90,13 @@ private static ArchCondition haveAUniqueImplementation() { @Override public void check(JavaClass businessInterface, ConditionEvents events) { events.add(new SimpleConditionEvent(businessInterface, - businessInterface.getAllSubClasses().size() <= 1, + businessInterface.getAllSubclasses().size() <= 1, describe(businessInterface))); } private String describe(JavaClass businessInterface) { return String.format("%s is implemented by %s", - businessInterface.getSimpleName(), joinNamesOf(businessInterface.getAllSubClasses())); + businessInterface.getSimpleName(), joinNamesOf(businessInterface.getAllSubclasses())); } private String joinNamesOf(Set implementations) { diff --git a/archunit-example/example-junit5/src/test/java/com/tngtech/archunit/exampletest/junit5/ThirdPartyRulesTest.java b/archunit-example/example-junit5/src/test/java/com/tngtech/archunit/exampletest/junit5/ThirdPartyRulesTest.java index 5394969f59..fc0951cf6c 100644 --- a/archunit-example/example-junit5/src/test/java/com/tngtech/archunit/exampletest/junit5/ThirdPartyRulesTest.java +++ b/archunit-example/example-junit5/src/test/java/com/tngtech/archunit/exampletest/junit5/ThirdPartyRulesTest.java @@ -39,10 +39,10 @@ private static ArchCondition notCreateProblematicClassesOutsideOfWork target(is(constructor())).and(targetOwner(is(assignableTo(ThirdPartyClassWithProblem.class)))); DescribedPredicate> notFromWithinThirdPartyClass = - originOwner(is(not(assignableTo(ThirdPartyClassWithProblem.class)))).forSubType(); + originOwner(is(not(assignableTo(ThirdPartyClassWithProblem.class)))).forSubtype(); DescribedPredicate> notFromWorkaroundFactory = - originOwner(is(not(equivalentTo(ThirdPartyClassWorkaroundFactory.class)))).forSubType(); + originOwner(is(not(equivalentTo(ThirdPartyClassWorkaroundFactory.class)))).forSubtype(); DescribedPredicate> targetIsIllegalConstructorOfThirdPartyClass = constructorCallOfThirdPartyClass. diff --git a/archunit-example/example-junit5/src/test/resources/frozen/a81a2b54-5a18-4145-b544-7a580aba0425 b/archunit-example/example-junit5/src/test/resources/frozen/a81a2b54-5a18-4145-b544-7a580aba0425 index b3440d5d81..5f85d78b3e 100644 --- a/archunit-example/example-junit5/src/test/resources/frozen/a81a2b54-5a18-4145-b544-7a580aba0425 +++ b/archunit-example/example-junit5/src/test/resources/frozen/a81a2b54-5a18-4145-b544-7a580aba0425 @@ -2,8 +2,10 @@ Class has annotation member of type in (ServiceViolatingLayerRules.java:0) Class has annotation member of type in (ServiceViolatingLayerRules.java:0) Class is annotated with in (ServiceViolatingLayerRules.java:0) +Class extends class in (SpecialServiceHelper.java:0) Class implements interface in (ServiceImplementation.java:0) Constructor (com.tngtech.archunit.example.layers.service.ServiceViolatingLayerRules)> has parameter of type in (SomeMediator.java:0) +Constructor ()> calls constructor ()> in (SpecialServiceHelper.java:9) Field has type in (SomeMediator.java:0) Field has type in (SomeController.java:0) Field has type in (SomeController.java:0) diff --git a/archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/ClassViolatingThirdPartyRules.java b/archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/ClassViolatingThirdPartyRules.java index 36cb21c5bb..56d88411b2 100644 --- a/archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/ClassViolatingThirdPartyRules.java +++ b/archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/ClassViolatingThirdPartyRules.java @@ -2,7 +2,7 @@ import com.tngtech.archunit.example.layers.thirdparty.ThirdPartyClassWithProblem; import com.tngtech.archunit.example.layers.thirdparty.ThirdPartyClassWorkaroundFactory; -import com.tngtech.archunit.example.layers.thirdparty.ThirdPartySubClassWithProblem; +import com.tngtech.archunit.example.layers.thirdparty.ThirdPartySubclassWithProblem; public class ClassViolatingThirdPartyRules { ThirdPartyClassWithProblem illegallyInstantiateThirdPartyClass() { @@ -13,11 +13,11 @@ ThirdPartyClassWithProblem correctlyInstantiateThirdPartyClass() { return new ThirdPartyClassWorkaroundFactory().create(); } - ThirdPartySubClassWithProblem illegallyInstantiateThirdPartySubClass() { - return new ThirdPartySubClassWithProblem(); + ThirdPartySubclassWithProblem illegallyInstantiateThirdPartySubclass() { + return new ThirdPartySubclassWithProblem(); } - ThirdPartySubClassWithProblem correctlyInstantiateThirdPartySubClass() { - return new ThirdPartyClassWorkaroundFactory().createSubClass(); + ThirdPartySubclassWithProblem correctlyInstantiateThirdPartySubclass() { + return new ThirdPartyClassWorkaroundFactory().createSubclass(); } } diff --git a/archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/service/SpecialServiceHelper.java b/archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/service/SpecialServiceHelper.java new file mode 100644 index 0000000000..09e70824eb --- /dev/null +++ b/archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/service/SpecialServiceHelper.java @@ -0,0 +1,10 @@ +package com.tngtech.archunit.example.layers.service; + +import java.util.HashMap; +import java.util.Set; + +import com.tngtech.archunit.example.layers.controller.SomeUtility; +import com.tngtech.archunit.example.layers.controller.one.SomeEnum; + +public class SpecialServiceHelper extends ServiceHelper>> { +} diff --git a/archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/thirdparty/ThirdPartyClassWorkaroundFactory.java b/archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/thirdparty/ThirdPartyClassWorkaroundFactory.java index 5a123cec06..c30a9ea8c0 100644 --- a/archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/thirdparty/ThirdPartyClassWorkaroundFactory.java +++ b/archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/thirdparty/ThirdPartyClassWorkaroundFactory.java @@ -9,8 +9,8 @@ public ThirdPartyClassWithProblem create() { return new ThirdPartyClassWithProblem(); } - public ThirdPartySubClassWithProblem createSubClass() { + public ThirdPartySubclassWithProblem createSubclass() { // some workaround here - return new ThirdPartySubClassWithProblem(); + return new ThirdPartySubclassWithProblem(); } } diff --git a/archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/thirdparty/ThirdPartySubClassWithProblem.java b/archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/thirdparty/ThirdPartySubclassWithProblem.java similarity index 53% rename from archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/thirdparty/ThirdPartySubClassWithProblem.java rename to archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/thirdparty/ThirdPartySubclassWithProblem.java index 435b517a0a..b994c401c6 100644 --- a/archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/thirdparty/ThirdPartySubClassWithProblem.java +++ b/archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/layers/thirdparty/ThirdPartySubclassWithProblem.java @@ -1,4 +1,4 @@ package com.tngtech.archunit.example.layers.thirdparty; -public class ThirdPartySubClassWithProblem extends ThirdPartyClassWithProblem { +public class ThirdPartySubclassWithProblem extends ThirdPartyClassWithProblem { } diff --git a/archunit-example/example-plain/src/test/java/com/tngtech/archunit/exampletest/SessionBeanRulesTest.java b/archunit-example/example-plain/src/test/java/com/tngtech/archunit/exampletest/SessionBeanRulesTest.java index 2257e7117d..c903b9f96d 100644 --- a/archunit-example/example-plain/src/test/java/com/tngtech/archunit/exampletest/SessionBeanRulesTest.java +++ b/archunit-example/example-plain/src/test/java/com/tngtech/archunit/exampletest/SessionBeanRulesTest.java @@ -65,8 +65,8 @@ private static DescribedPredicate> originNeitherConstructorNorPost new DescribedPredicate("have subclass that is a local bean") { @Override public boolean apply(JavaClass input) { - for (JavaClass subClass : input.getAllSubClasses()) { - if (isLocalBeanImplementation(subClass, input)) { + for (JavaClass subclass : input.getAllSubclasses()) { + if (isLocalBeanImplementation(subclass, input)) { return true; } } @@ -90,13 +90,13 @@ private boolean isLocalBeanImplementation(JavaClass bean, JavaClass businessInte @Override public void check(JavaClass businessInterface, ConditionEvents events) { events.add(new SimpleConditionEvent(businessInterface, - businessInterface.getAllSubClasses().size() <= 1, + businessInterface.getAllSubclasses().size() <= 1, describe(businessInterface))); } private String describe(JavaClass businessInterface) { return String.format("%s is implemented by %s", - businessInterface.getSimpleName(), joinNamesOf(businessInterface.getAllSubClasses())); + businessInterface.getSimpleName(), joinNamesOf(businessInterface.getAllSubclasses())); } private String joinNamesOf(Set implementations) { diff --git a/archunit-example/example-plain/src/test/java/com/tngtech/archunit/exampletest/ThirdPartyRulesTest.java b/archunit-example/example-plain/src/test/java/com/tngtech/archunit/exampletest/ThirdPartyRulesTest.java index 8a5c3b6cda..dd8e3049bd 100644 --- a/archunit-example/example-plain/src/test/java/com/tngtech/archunit/exampletest/ThirdPartyRulesTest.java +++ b/archunit-example/example-plain/src/test/java/com/tngtech/archunit/exampletest/ThirdPartyRulesTest.java @@ -43,10 +43,10 @@ private ArchCondition notCreateProblematicClassesOutsideOfWorkaroundF target(is(constructor())).and(targetOwner(is(assignableTo(ThirdPartyClassWithProblem.class)))); DescribedPredicate> notFromWithinThirdPartyClass = - originOwner(is(not(assignableTo(ThirdPartyClassWithProblem.class)))).forSubType(); + originOwner(is(not(assignableTo(ThirdPartyClassWithProblem.class)))).forSubtype(); DescribedPredicate> notFromWorkaroundFactory = - originOwner(is(not(equivalentTo(ThirdPartyClassWorkaroundFactory.class)))).forSubType(); + originOwner(is(not(equivalentTo(ThirdPartyClassWorkaroundFactory.class)))).forSubtype(); DescribedPredicate> targetIsIllegalConstructorOfThirdPartyClass = constructorCallOfThirdPartyClass. diff --git a/archunit-example/example-plain/src/test/resources/frozen/a81a2b54-5a18-4145-b544-7a580aba0425 b/archunit-example/example-plain/src/test/resources/frozen/a81a2b54-5a18-4145-b544-7a580aba0425 index b3440d5d81..5f85d78b3e 100644 --- a/archunit-example/example-plain/src/test/resources/frozen/a81a2b54-5a18-4145-b544-7a580aba0425 +++ b/archunit-example/example-plain/src/test/resources/frozen/a81a2b54-5a18-4145-b544-7a580aba0425 @@ -2,8 +2,10 @@ Class has annotation member of type in (ServiceViolatingLayerRules.java:0) Class has annotation member of type in (ServiceViolatingLayerRules.java:0) Class is annotated with in (ServiceViolatingLayerRules.java:0) +Class extends class in (SpecialServiceHelper.java:0) Class implements interface in (ServiceImplementation.java:0) Constructor (com.tngtech.archunit.example.layers.service.ServiceViolatingLayerRules)> has parameter of type in (SomeMediator.java:0) +Constructor ()> calls constructor ()> in (SpecialServiceHelper.java:9) Field has type in (SomeMediator.java:0) Field has type in (SomeController.java:0) Field has type in (SomeController.java:0) diff --git a/archunit-integration-test/src/test/java/com/tngtech/archunit/ArchUnitArchitectureTest.java b/archunit-integration-test/src/test/java/com/tngtech/archunit/ArchUnitArchitectureTest.java index c856ccfb36..b99bf0fc97 100644 --- a/archunit-integration-test/src/test/java/com/tngtech/archunit/ArchUnitArchitectureTest.java +++ b/archunit-integration-test/src/test/java/com/tngtech/archunit/ArchUnitArchitectureTest.java @@ -71,7 +71,7 @@ public class ArchUnitArchitectureTest { private static DescribedPredicate> typeIsIllegallyResolvedViaReflection() { DescribedPredicate> explicitlyAllowedUsage = origin(is(annotatedWith(MayResolveTypesViaReflection.class))) - .or(contextIsAnnotatedWith(MayResolveTypesViaReflection.class)).forSubType(); + .or(contextIsAnnotatedWith(MayResolveTypesViaReflection.class)).forSubtype(); return classIsResolvedViaReflection().and(not(explicitlyAllowedUsage)); } @@ -100,7 +100,7 @@ private static DescribedPredicate> classIsResolvedViaReflection() { target(HasOwner.Functions.Get.owner() .is(equivalentTo(Class.class))) .and(target(has(name("forName")))) - .forSubType(); + .forSubtype(); DescribedPredicate> targetIsMarked = annotatedWith(ResolvesTypesViaReflection.class).onResultOf(Get.target()); diff --git a/archunit-integration-test/src/test/java/com/tngtech/archunit/PublicAPIRules.java b/archunit-integration-test/src/test/java/com/tngtech/archunit/PublicAPIRules.java index 8924f532a1..298f888179 100644 --- a/archunit-integration-test/src/test/java/com/tngtech/archunit/PublicAPIRules.java +++ b/archunit-integration-test/src/test/java/com/tngtech/archunit/PublicAPIRules.java @@ -136,13 +136,13 @@ public class PublicAPIRules { .andShould().haveRawReturnType(not(guavaClass()).as("that are no Guava types"))); private static DescribedPredicate publicAPI() { - return annotatedWith(PublicAPI.class).forSubType() + return annotatedWith(PublicAPI.class).forSubtype() .or(haveMemberThatBelongsToPublicApi()) .or(markedAsPublicAPIForInheritance()); } private static DescribedPredicate internal() { - return annotatedWith(Internal.class).forSubType() + return annotatedWith(Internal.class).forSubtype() .or(equivalentTo(Internal.class)); } @@ -215,8 +215,8 @@ public boolean apply(JavaClass input) { } } return input.getConstructors().isEmpty() && - input.getSuperClass().isPresent() && - haveAPublicConstructor().apply(input.getSuperClass().get()); + input.getRawSuperclass().isPresent() && + haveAPublicConstructor().apply(input.getRawSuperclass().get()); } }; } @@ -236,8 +236,8 @@ public boolean apply(JavaClass input) { } private static DescribedPredicate withoutAPIMarking() { - return not(annotatedWith(PublicAPI.class)).forSubType() - .and(not(annotatedWith(Internal.class)).forSubType()) + return not(annotatedWith(PublicAPI.class)).forSubtype() + .and(not(annotatedWith(Internal.class)).forSubtype()) .and(declaredIn(modifier(PUBLIC))) .as("without API marking"); } diff --git a/archunit-integration-test/src/test/java/com/tngtech/archunit/integration/ExamplesIntegrationTest.java b/archunit-integration-test/src/test/java/com/tngtech/archunit/integration/ExamplesIntegrationTest.java index 797252a007..9a672db5f9 100644 --- a/archunit-integration-test/src/test/java/com/tngtech/archunit/integration/ExamplesIntegrationTest.java +++ b/archunit-integration-test/src/test/java/com/tngtech/archunit/integration/ExamplesIntegrationTest.java @@ -103,12 +103,13 @@ import com.tngtech.archunit.example.layers.service.ServiceInterface; import com.tngtech.archunit.example.layers.service.ServiceViolatingDaoRules; import com.tngtech.archunit.example.layers.service.ServiceViolatingLayerRules; +import com.tngtech.archunit.example.layers.service.SpecialServiceHelper; import com.tngtech.archunit.example.layers.service.impl.ServiceImplementation; import com.tngtech.archunit.example.layers.service.impl.SomeInterfacePlacedInTheWrongPackage; import com.tngtech.archunit.example.layers.service.impl.WronglyNamedSvc; import com.tngtech.archunit.example.layers.thirdparty.ThirdPartyClassWithProblem; import com.tngtech.archunit.example.layers.thirdparty.ThirdPartyClassWorkaroundFactory; -import com.tngtech.archunit.example.layers.thirdparty.ThirdPartySubClassWithProblem; +import com.tngtech.archunit.example.layers.thirdparty.ThirdPartySubclassWithProblem; import com.tngtech.archunit.example.layers.web.AnnotatedController; import com.tngtech.archunit.example.layers.web.InheritedControllerImpl; import com.tngtech.archunit.example.onionarchitecture.adapter.cli.AdministrationCLI; @@ -172,6 +173,7 @@ import static com.tngtech.archunit.testutils.ExpectedDependency.annotatedClass; import static com.tngtech.archunit.testutils.ExpectedDependency.constructor; import static com.tngtech.archunit.testutils.ExpectedDependency.field; +import static com.tngtech.archunit.testutils.ExpectedDependency.genericSuperclass; import static com.tngtech.archunit.testutils.ExpectedDependency.inheritanceFrom; import static com.tngtech.archunit.testutils.ExpectedDependency.method; import static com.tngtech.archunit.testutils.ExpectedDependency.typeParameter; @@ -775,6 +777,8 @@ Stream LayerDependencyRulesTest() { .inLine(25).asDependency()) .by(typeParameter(ServiceHelper.class, "TYPE_PARAMETER_VIOLATING_LAYER_RULE").dependingOn(SomeUtility.class)) .by(typeParameter(ServiceHelper.class, "ANOTHER_TYPE_PARAMETER_VIOLATING_LAYER_RULE").dependingOn(SomeEnum.class)) + .by(genericSuperclass(SpecialServiceHelper.class, ServiceHelper.class).dependingOn(SomeUtility.class)) + .by(genericSuperclass(SpecialServiceHelper.class, ServiceHelper.class).dependingOn(SomeEnum.class)) .by(method(ServiceViolatingLayerRules.class, dependentMethod).withParameter(UseCaseTwoController.class)) .by(method(ServiceViolatingLayerRules.class, dependentMethod).withReturnType(SomeGuiController.class)) .by(method(ServiceViolatingLayerRules.class, dependentOnComponentTypeMethod).withParameter(UseCaseTwoController[].class)) @@ -834,6 +838,8 @@ Stream LayerDependencyRulesTest() { .inLine(25).asDependency()) .by(typeParameter(ServiceHelper.class, "TYPE_PARAMETER_VIOLATING_LAYER_RULE").dependingOn(SomeUtility.class)) .by(typeParameter(ServiceHelper.class, "ANOTHER_TYPE_PARAMETER_VIOLATING_LAYER_RULE").dependingOn(SomeEnum.class)) + .by(genericSuperclass(SpecialServiceHelper.class, ServiceHelper.class).dependingOn(SomeUtility.class)) + .by(genericSuperclass(SpecialServiceHelper.class, ServiceHelper.class).dependingOn(SomeEnum.class)) .by(method(ServiceViolatingLayerRules.class, dependentMethod).withParameter(UseCaseTwoController.class)) .by(method(ServiceViolatingLayerRules.class, dependentMethod).withReturnType(SomeGuiController.class)) .by(method(ServiceViolatingLayerRules.class, dependentOnComponentTypeMethod).withParameter(UseCaseTwoController[].class)) @@ -903,6 +909,8 @@ Stream LayeredArchitectureTest() { .by(typeParameter(ServiceHelper.class, "TYPE_PARAMETER_VIOLATING_LAYER_RULE").dependingOn(SomeUtility.class)) .by(typeParameter(ServiceHelper.class, "ANOTHER_TYPE_PARAMETER_VIOLATING_LAYER_RULE").dependingOn(SomeEnum.class)) + .by(genericSuperclass(SpecialServiceHelper.class, ServiceHelper.class).dependingOn(SomeUtility.class)) + .by(genericSuperclass(SpecialServiceHelper.class, ServiceHelper.class).dependingOn(SomeEnum.class)) .by(method(ServiceViolatingLayerRules.class, dependentMethod).withParameter(UseCaseTwoController.class)) .by(method(ServiceViolatingLayerRules.class, dependentMethod).withReturnType(SomeGuiController.class)) .by(method(ServiceViolatingLayerRules.class, dependentOnComponentTypeMethod) @@ -1370,8 +1378,8 @@ Stream ThirdPartyRulesTest() { .by(callFromMethod(ClassViolatingThirdPartyRules.class, "illegallyInstantiateThirdPartyClass") .toConstructor(ThirdPartyClassWithProblem.class) .inLine(9)) - .by(callFromMethod(ClassViolatingThirdPartyRules.class, "illegallyInstantiateThirdPartySubClass") - .toConstructor(ThirdPartySubClassWithProblem.class) + .by(callFromMethod(ClassViolatingThirdPartyRules.class, "illegallyInstantiateThirdPartySubclass") + .toConstructor(ThirdPartySubclassWithProblem.class) .inLine(17)) .toDynamicTests(); diff --git a/archunit-integration-test/src/test/java/com/tngtech/archunit/testutils/ExpectedDependency.java b/archunit-integration-test/src/test/java/com/tngtech/archunit/testutils/ExpectedDependency.java index 7a19fce21d..4a2a19b5dd 100644 --- a/archunit-integration-test/src/test/java/com/tngtech/archunit/testutils/ExpectedDependency.java +++ b/archunit-integration-test/src/test/java/com/tngtech/archunit/testutils/ExpectedDependency.java @@ -29,6 +29,10 @@ public static TypeParameterCreator typeParameter(Class clazz, String typePara return new TypeParameterCreator(clazz, typeParameterName); } + public static GenericSuperclassTypeArgumentCreator genericSuperclass(Class clazz, Class genericSuperclassErasure) { + return new GenericSuperclassTypeArgumentCreator(clazz, genericSuperclassErasure); + } + public static AnnotationDependencyCreator annotatedClass(Class clazz) { return new AnnotationDependencyCreator(clazz); } @@ -78,9 +82,9 @@ private InheritanceCreator(Class clazz) { this.clazz = clazz; } - public ExpectedDependency extending(Class superClass) { - return new ExpectedDependency(clazz, superClass, - getDependencyPattern(clazz.getName(), "extends", superClass.getName(), 0)); + public ExpectedDependency extending(Class superclass) { + return new ExpectedDependency(clazz, superclass, + getDependencyPattern(clazz.getName(), "extends", superclass.getName(), 0)); } public ExpectedDependency implementing(Class anInterface) { @@ -104,6 +108,24 @@ public ExpectedDependency dependingOn(Class typeParameterDependency) { } } + public static class GenericSuperclassTypeArgumentCreator { + private final Class childClass; + private final Class genericSuperclassErasure; + + private GenericSuperclassTypeArgumentCreator(Class childClass, Class genericSuperclassErasure) { + this.childClass = childClass; + this.genericSuperclassErasure = genericSuperclassErasure; + } + + public ExpectedDependency dependingOn(Class superclassTypeArgumentDependency) { + return new ExpectedDependency(childClass, superclassTypeArgumentDependency, + getDependencyPattern(childClass.getName(), + "has generic superclass <" + genericSuperclassErasure.getName() + "> with type argument depending on", + superclassTypeArgumentDependency.getName(), + 0)); + } + } + public static class AccessCreator { private final Class originClass; diff --git a/archunit-junit/src/main/java/com/tngtech/archunit/junit/ReflectionUtils.java b/archunit-junit/src/main/java/com/tngtech/archunit/junit/ReflectionUtils.java index 828789ad76..ec20792280 100644 --- a/archunit-junit/src/main/java/com/tngtech/archunit/junit/ReflectionUtils.java +++ b/archunit-junit/src/main/java/com/tngtech/archunit/junit/ReflectionUtils.java @@ -38,16 +38,16 @@ class ReflectionUtils { private ReflectionUtils() { } - static Set> getAllSuperTypes(Class type) { + static Set> getAllSupertypes(Class type) { if (type == null) { return Collections.emptySet(); } ImmutableSet.Builder> result = ImmutableSet.>builder() .add(type) - .addAll(getAllSuperTypes(type.getSuperclass())); + .addAll(getAllSupertypes(type.getSuperclass())); for (Class c : type.getInterfaces()) { - result.addAll(getAllSuperTypes(c)); + result.addAll(getAllSupertypes(c)); } return result.build(); } @@ -71,7 +71,7 @@ protected Collection extractFrom(Class type) { } private static List getAll(Class type, Collector collector) { - for (Class t : getAllSuperTypes(type)) { + for (Class t : getAllSupertypes(type)) { collector.collectFrom(t); } return collector.collected; diff --git a/archunit-junit/src/test/java/com/tngtech/archunit/junit/ReflectionUtilsTest.java b/archunit-junit/src/test/java/com/tngtech/archunit/junit/ReflectionUtilsTest.java index bb540c38fe..0772fb69ed 100644 --- a/archunit-junit/src/test/java/com/tngtech/archunit/junit/ReflectionUtilsTest.java +++ b/archunit-junit/src/test/java/com/tngtech/archunit/junit/ReflectionUtilsTest.java @@ -39,7 +39,7 @@ public void getAllMethods() { @Test public void getAllSupertypes() { - assertThat(ReflectionUtils.getAllSuperTypes(Child.class)).containsOnly( + assertThat(ReflectionUtils.getAllSupertypes(Child.class)).containsOnly( Child.class, ChildInterface.class, UpperMiddle.class, LowerMiddle.class, Parent.class, SomeInterface.class, OtherInterface.class, Object.class ); @@ -47,7 +47,7 @@ public void getAllSupertypes() { @Test public void getAllMethods_of_interface() { - assertThat(ReflectionUtils.getAllMethods(SubInterface.class, always(true))) + assertThat(ReflectionUtils.getAllMethods(Subinterface.class, always(true))) .containsOnly( method(SomeInterface.class, "foo"), method(OtherInterface.class, "bar")); @@ -64,7 +64,7 @@ public boolean apply(T input) { @Test public void getAllFields_of_interface() { - assertThat(ReflectionUtils.getAllFields(SubInterface.class, always(true))) + assertThat(ReflectionUtils.getAllFields(Subinterface.class, always(true))) .containsOnly( field(SomeInterface.class, "SOME_CONSTANT"), field(OtherInterface.class, "OTHER_CONSTANT")); @@ -162,6 +162,6 @@ private interface OtherInterface { void bar(); } - private interface SubInterface extends SomeInterface, OtherInterface { + private interface Subinterface extends SomeInterface, OtherInterface { } -} \ No newline at end of file +} diff --git a/archunit/src/main/java/com/tngtech/archunit/base/DescribedPredicate.java b/archunit/src/main/java/com/tngtech/archunit/base/DescribedPredicate.java index a8139caac4..0e66297c6e 100644 --- a/archunit/src/main/java/com/tngtech/archunit/base/DescribedPredicate.java +++ b/archunit/src/main/java/com/tngtech/archunit/base/DescribedPredicate.java @@ -60,10 +60,18 @@ public DescribedPredicate onResultOf(final Function} Can't specify this contravariant type at the language level */ @SuppressWarnings("unchecked") // DescribedPredicate is contravariant - public DescribedPredicate forSubType() { + public final DescribedPredicate forSubtype() { return (DescribedPredicate) this; } + /** + * @deprecated Use {@link #forSubtype()} instead. + */ + @Deprecated + public final DescribedPredicate forSubType() { + return forSubtype(); + } + @Override public String toString() { return getDescription(); @@ -114,15 +122,15 @@ public static > DescribedPredicate greaterThanOrEqual } public static DescribedPredicate describe(String description, Predicate predicate) { - return new DescribePredicate<>(description, predicate).forSubType(); + return new DescribePredicate<>(description, predicate).forSubtype(); } public static DescribedPredicate doesNot(final DescribedPredicate predicate) { - return not(predicate).as("does not %s", predicate.getDescription()).forSubType(); + return not(predicate).as("does not %s", predicate.getDescription()).forSubtype(); } public static DescribedPredicate doNot(final DescribedPredicate predicate) { - return not(predicate).as("do not %s", predicate.getDescription()).forSubType(); + return not(predicate).as("do not %s", predicate.getDescription()).forSubtype(); } public static DescribedPredicate not(final DescribedPredicate predicate) { @@ -208,7 +216,7 @@ private static class NotPredicate extends DescribedPredicate { NotPredicate(DescribedPredicate predicate) { super("not " + predicate.getDescription()); - this.predicate = checkNotNull(predicate).forSubType(); + this.predicate = checkNotNull(predicate).forSubtype(); } @Override @@ -317,7 +325,7 @@ private static class AnyElementPredicate extends DescribedPredicate predicate) { super("any element that " + predicate.getDescription()); - this.predicate = predicate.forSubType(); + this.predicate = predicate.forSubtype(); } @Override @@ -336,7 +344,7 @@ private static class AllElementsPredicate extends DescribedPredicate predicate) { super("all elements " + predicate.getDescription()); - this.predicate = predicate.forSubType(); + this.predicate = predicate.forSubtype(); } @Override diff --git a/archunit/src/main/java/com/tngtech/archunit/base/Optional.java b/archunit/src/main/java/com/tngtech/archunit/base/Optional.java index 3a59e8fbc3..b05b4c3f75 100644 --- a/archunit/src/main/java/com/tngtech/archunit/base/Optional.java +++ b/archunit/src/main/java/com/tngtech/archunit/base/Optional.java @@ -72,7 +72,7 @@ public static Optional absent() { public abstract T or(T value); @PublicAPI(usage = ACCESS) - public abstract Optional or(Optional value); + public abstract Optional or(Optional value); @PublicAPI(usage = ACCESS) public abstract Set asSet(); @@ -106,8 +106,9 @@ public T or(T value) { } @Override - public Optional or(Optional value) { - return value; + @SuppressWarnings("unchecked") // cast is safe because Optional is covariant + public Optional or(Optional value) { + return (Optional) value; } @Override @@ -169,7 +170,7 @@ public T or(T value) { } @Override - public Optional or(Optional value) { + public Optional or(Optional value) { return this; } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/AccessTarget.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/AccessTarget.java index 6cef6a01ac..2e87b75b45 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/AccessTarget.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/AccessTarget.java @@ -522,7 +522,7 @@ public static DescribedPredicate declaredIn(String className) { public static DescribedPredicate declaredIn(DescribedPredicate predicate) { return Get.owner().is(predicate) .as("declared in %s", predicate.getDescription()) - .forSubType(); + .forSubtype(); } @PublicAPI(usage = ACCESS) diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/Dependency.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/Dependency.java index 346bf0029a..120c2c8c55 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/Dependency.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/Dependency.java @@ -78,23 +78,23 @@ static Set tryCreateFromAccess(JavaAccess access) { return dependencies.build(); } - static Dependency fromInheritance(JavaClass origin, JavaClass targetSuperType) { + static Dependency fromInheritance(JavaClass origin, JavaClass targetSupertype) { String originType = origin.isInterface() ? "Interface" : "Class"; String originDescription = originType + " " + bracketFormat(origin.getName()); - String dependencyType = !origin.isInterface() && targetSuperType.isInterface() ? "implements" : "extends"; + String dependencyType = !origin.isInterface() && targetSupertype.isInterface() ? "implements" : "extends"; - String targetType = targetSuperType.isInterface() ? "interface" : "class"; - String targetDescription = bracketFormat(targetSuperType.getName()); + String targetType = targetSupertype.isInterface() ? "interface" : "class"; + String targetDescription = bracketFormat(targetSupertype.getName()); String dependencyDescription = originDescription + " " + dependencyType + " " + targetType + " " + targetDescription; String description = dependencyDescription + " in " + origin.getSourceCodeLocation(); - Optional result = tryCreateDependency(origin, targetSuperType, description, 0); + Optional result = tryCreateDependency(origin, targetSupertype, description, 0); if (!result.isPresent()) { throw new IllegalStateException(String.format("Tried to create illegal inheritance dependency '%s' (%s -> %s), this is likely a bug!", - description, origin.getSimpleName(), targetSuperType.getSimpleName())); + description, origin.getSimpleName(), targetSupertype.getSimpleName())); } return result.get(); } @@ -135,6 +135,11 @@ static Set tryCreateFromTypeParameter(JavaTypeVariable typeParame return tryCreateDependency(origin.originClass, origin.originDescription, dependencyType, typeParameterDependency); } + static Set tryCreateFromGenericSuperclassTypeArguments(JavaClass originClass, JavaType superclass, JavaClass typeArgumentDependency) { + String dependencyType = "has generic superclass " + bracketFormat(superclass.getName()) + " with type argument depending on"; + return tryCreateDependency(originClass, originClass.getDescription(), dependencyType, typeArgumentDependency); + } + private static Origin findSuitableOrigin(Object dependencyCause, Object originCandidate) { if (originCandidate instanceof JavaMember) { JavaMember member = (JavaMember) originCandidate; diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/DomainObjectCreationContext.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/DomainObjectCreationContext.java index 8486545f08..9ba42f6ec7 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/DomainObjectCreationContext.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/DomainObjectCreationContext.java @@ -78,6 +78,10 @@ public static void completeTypeParameters(JavaClass javaClass, ImportContext imp javaClass.completeTypeParametersFrom(importContext); } + public static void completeGenericSuperclass(JavaClass javaClass, ImportContext importContext) { + javaClass.completeGenericSuperclassFrom(importContext); + } + public static void completeMembers(JavaClass javaClass, ImportContext importContext) { javaClass.completeMembers(importContext); } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/ImportContext.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/ImportContext.java index c4ec07c65e..0a5f505f5f 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/ImportContext.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/ImportContext.java @@ -24,7 +24,9 @@ @Internal public interface ImportContext { - Optional createSuperClass(JavaClass owner); + Optional createSuperclass(JavaClass owner); + + Optional createGenericSuperclass(JavaClass owner); Set createInterfaces(JavaClass owner); diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java index 4a0d7dd55a..d48fed6bd7 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java @@ -54,6 +54,7 @@ import static com.tngtech.archunit.core.domain.JavaClass.Functions.GET_SIMPLE_NAME; import static com.tngtech.archunit.core.domain.JavaConstructor.CONSTRUCTOR_NAME; import static com.tngtech.archunit.core.domain.JavaModifier.ENUM; +import static com.tngtech.archunit.core.domain.JavaType.Functions.TO_ERASURE; import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Utils.toAnnotationOfType; import static com.tngtech.archunit.core.domain.properties.HasName.Functions.GET_NAME; import static com.tngtech.archunit.core.domain.properties.HasType.Functions.GET_RAW_TYPE; @@ -80,14 +81,14 @@ public class JavaClass implements JavaType, HasName.AndFullName, HasAnnotations< private Set members = emptySet(); private Set constructors = emptySet(); private Optional staticInitializer = Optional.absent(); - private Optional superClass = Optional.absent(); - private final Supplier> allSuperClasses = Suppliers.memoize(new Supplier>() { + private Superclass superclass = Superclass.ABSENT; + private final Supplier> allRawSuperclasses = Suppliers.memoize(new Supplier>() { @Override public List get() { ImmutableList.Builder result = ImmutableList.builder(); JavaClass current = JavaClass.this; - while (current.getSuperClass().isPresent()) { - current = current.getSuperClass().get(); + while (current.getRawSuperclass().isPresent()) { + current = current.getRawSuperclass().get(); result.add(current); } return result.build(); @@ -102,9 +103,7 @@ public Set get() { result.add(i); result.addAll(i.getAllInterfaces()); } - if (superClass.isPresent()) { - result.addAll(superClass.get().getAllInterfaces()); - } + result.addAll(superclass.getAllInterfaces()); return result.build(); } }); @@ -113,18 +112,18 @@ public Set get() { public List get() { ImmutableList.Builder result = ImmutableList.builder(); result.add(JavaClass.this); - result.addAll(getAllSuperClasses()); + result.addAll(getAllRawSuperclasses()); return result.build(); } }); - private final Set subClasses = new HashSet<>(); - private final Supplier> allSubClasses = Suppliers.memoize(new Supplier>() { + private final Set subclasses = new HashSet<>(); + private final Supplier> allSubclasses = Suppliers.memoize(new Supplier>() { @Override public Set get() { Set result = new HashSet<>(); - for (JavaClass subClass : subClasses) { - result.add(subClass); - result.addAll(subClass.getAllSubClasses()); + for (JavaClass subclass : subclasses) { + result.add(subclass); + result.addAll(subclass.getAllSubclasses()); } return result; } @@ -655,13 +654,27 @@ public JavaClass toErasure() { return this; } + @PublicAPI(usage = ACCESS) + public Optional getRawSuperclass() { + return superclass.getRaw(); + } + + @PublicAPI(usage = ACCESS) + public Optional getSuperclass() { + return superclass.get(); + } + + /** + * @deprecated Use {@link #getRawSuperclass()} instead + */ + @Deprecated @PublicAPI(usage = ACCESS) public Optional getSuperClass() { - return superClass; + return getRawSuperclass(); } /** - * @return The complete class hierarchy, i.e. the class itself and the result of {@link #getAllSuperClasses()} + * @return The complete class hierarchy, i.e. the class itself and the result of {@link #getAllRawSuperclasses()} */ @PublicAPI(usage = ACCESS) public List getClassHierarchy() { @@ -673,13 +686,31 @@ public List getClassHierarchy() { * then the super class of the super class and so on. Includes Object.class in the result. */ @PublicAPI(usage = ACCESS) + public List getAllRawSuperclasses() { + return allRawSuperclasses.get(); + } + + /** + * @deprecated Use {@link #getAllRawSuperclasses()} instead. + */ + @Deprecated + @PublicAPI(usage = ACCESS) public List getAllSuperClasses() { - return allSuperClasses.get(); + return getAllRawSuperclasses(); + } + + @PublicAPI(usage = ACCESS) + public Set getSubclasses() { + return subclasses; } + /** + * @deprecated Use {@link #getSubclasses()} instead. + */ + @Deprecated @PublicAPI(usage = ACCESS) public Set getSubClasses() { - return subClasses; + return getSubclasses(); } @PublicAPI(usage = ACCESS) @@ -704,7 +735,7 @@ public Set getAllInterfaces() { public Set getAllClassesSelfIsAssignableTo() { return ImmutableSet.builder() .add(this) - .addAll(getAllSuperClasses()) + .addAll(getAllRawSuperclasses()) .addAll(getAllInterfaces()) .build(); } @@ -714,9 +745,18 @@ public Optional getEnclosingClass() { return enclosingClass; } + @PublicAPI(usage = ACCESS) + public Set getAllSubclasses() { + return allSubclasses.get(); + } + + /** + * @deprecated Use {@link #getAllSubclasses()} instead. + */ + @Deprecated @PublicAPI(usage = ACCESS) public Set getAllSubClasses() { - return allSubClasses.get(); + return getAllSubclasses(); } @PublicAPI(usage = ACCESS) @@ -1194,7 +1234,7 @@ public boolean isAssignableFrom(String typeName) { @PublicAPI(usage = ACCESS) public boolean isAssignableFrom(DescribedPredicate predicate) { List possibleTargets = ImmutableList.builder() - .add(this).addAll(getAllSubClasses()).build(); + .add(this).addAll(getAllSubclasses()).build(); return anyMatches(possibleTargets, predicate); } @@ -1240,7 +1280,7 @@ public Class reflect() { } void completeClassHierarchyFrom(ImportContext context) { - completeSuperClassFrom(context); + completeSuperclassFrom(context); completeInterfacesFrom(context); allFields = Suppliers.memoize(new Supplier>() { @Override @@ -1274,17 +1314,18 @@ public Set get() { }); } - private void completeSuperClassFrom(ImportContext context) { - superClass = context.createSuperClass(this); - if (superClass.isPresent()) { - superClass.get().subClasses.add(this); + private void completeSuperclassFrom(ImportContext context) { + Optional rawSuperclass = context.createSuperclass(this); + if (rawSuperclass.isPresent()) { + rawSuperclass.get().subclasses.add(this); + this.superclass = this.superclass.withRawType(rawSuperclass.get()); } } private void completeInterfacesFrom(ImportContext context) { interfaces.addAll(context.createInterfaces(this)); for (JavaClass i : interfaces) { - i.subClasses.add(this); + i.subclasses.add(this); } } @@ -1296,6 +1337,13 @@ void completeTypeParametersFrom(ImportContext context) { typeParameters = context.createTypeParameters(this); } + void completeGenericSuperclassFrom(ImportContext context) { + Optional genericSuperclass = context.createGenericSuperclass(this); + if (genericSuperclass.isPresent()) { + superclass = superclass.withGenericType(genericSuperclass.get()); + } + } + void completeMembers(final ImportContext context) { fields = context.createFields(this); methods = context.createMethods(this); @@ -1372,6 +1420,42 @@ public boolean isAnonymous() { return isAnonymousClass(); } + private static class Superclass { + private static final Superclass ABSENT = new Superclass(Optional.absent()); + + private final Optional rawType; + private final Optional type; + + private Superclass(JavaType type) { + this(Optional.of(type)); + } + + private Superclass(Optional type) { + this.rawType = type.transform(TO_ERASURE); + this.type = type; + } + + Optional getRaw() { + return rawType; + } + + Optional get() { + return type.or(rawType); + } + + Set getAllInterfaces() { + return rawType.isPresent() ? rawType.get().getAllInterfaces() : Collections.emptySet(); + } + + Superclass withRawType(JavaClass newRawType) { + return new Superclass(newRawType); + } + + Superclass withGenericType(JavaType newGenericType) { + return new Superclass(newGenericType); + } + } + public static final class Functions { private Functions() { } @@ -1663,7 +1747,7 @@ public static DescribedPredicate implement(final String typeName) { @PublicAPI(usage = ACCESS) public static DescribedPredicate implement(final DescribedPredicate predicate) { DescribedPredicate selfIsImplementation = not(INTERFACES); - DescribedPredicate interfacePredicate = predicate.forSubType().and(INTERFACES); + DescribedPredicate interfacePredicate = predicate.forSubtype().and(INTERFACES); return selfIsImplementation.and(assignableTo(interfacePredicate)) .as("implement " + predicate.getDescription()); } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassDependencies.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassDependencies.java index 9e24e2e197..c08dfd60e4 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassDependencies.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassDependencies.java @@ -15,6 +15,7 @@ */ package com.tngtech.archunit.core.domain; +import java.util.List; import java.util.Set; import com.google.common.base.Supplier; @@ -26,6 +27,7 @@ import static com.google.common.base.Suppliers.memoize; import static com.google.common.collect.Iterables.concat; +import static java.util.Collections.emptySet; class JavaClassDependencies { private final JavaClass javaClass; @@ -70,8 +72,23 @@ private Set dependenciesFromAccesses(Set> accesses) { private Set inheritanceDependenciesFromSelf() { ImmutableSet.Builder result = ImmutableSet.builder(); - for (JavaClass superType : FluentIterable.from(javaClass.getInterfaces()).append(javaClass.getSuperClass().asSet())) { - result.add(Dependency.fromInheritance(javaClass, superType)); + for (JavaClass supertype : FluentIterable.from(javaClass.getInterfaces()).append(javaClass.getRawSuperclass().asSet())) { + result.add(Dependency.fromInheritance(javaClass, supertype)); + } + result.addAll(genericSuperclassTypeArgumentDependencies()); + return result.build(); + } + + private Set genericSuperclassTypeArgumentDependencies() { + if (!javaClass.getSuperclass().isPresent() || !(javaClass.getSuperclass().get() instanceof JavaParameterizedType)) { + return emptySet(); + } + JavaParameterizedType genericSuperclass = (JavaParameterizedType) javaClass.getSuperclass().get(); + + List actualTypeArguments = genericSuperclass.getActualTypeArguments(); + ImmutableSet.Builder result = ImmutableSet.builder(); + for (JavaClass superclassTypeArgumentDependency : dependenciesOfTypes(actualTypeArguments)) { + result.addAll(Dependency.tryCreateFromGenericSuperclassTypeArguments(javaClass, genericSuperclass, superclassTypeArgumentDependency)); } return result.build(); } @@ -151,14 +168,22 @@ private Set typeParameterDependenciesFromSelf() { private Set getDependenciesFromTypeParameter(JavaTypeVariable typeVariable) { ImmutableSet.Builder dependenciesBuilder = ImmutableSet.builder(); - for (JavaType bound : typeVariable.getUpperBounds()) { - for (JavaClass typeParameterDependency : dependenciesOfType(bound)) { - dependenciesBuilder.addAll(Dependency.tryCreateFromTypeParameter(typeVariable, typeParameterDependency)); - } + for (JavaClass typeParameterDependency : dependenciesOfTypes(typeVariable.getUpperBounds())) { + dependenciesBuilder.addAll(Dependency.tryCreateFromTypeParameter(typeVariable, typeParameterDependency)); } return dependenciesBuilder.build(); } + private Set dependenciesOfTypes(Iterable types) { + ImmutableSet.Builder result = ImmutableSet.builder(); + for (JavaType type : types) { + for (JavaClass typeParameterDependency : dependenciesOfType(type)) { + result.add(typeParameterDependency); + } + } + return result.build(); + } + private static Iterable dependenciesOfType(JavaType javaType) { ImmutableSet.Builder result = ImmutableSet.builder(); if (javaType instanceof JavaClass) { diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaMember.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaMember.java index 7164cb4ccd..b3bb27eda6 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaMember.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaMember.java @@ -215,7 +215,7 @@ public static DescribedPredicate declaredIn(String className) { public static DescribedPredicate declaredIn(DescribedPredicate predicate) { return Get.owner().is(predicate) .as("declared in %s", predicate.getDescription()) - .forSubType(); + .forSubtype(); } } } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaPackage.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaPackage.java index e8c449b8a7..3fbff1e921 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaPackage.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaPackage.java @@ -56,15 +56,15 @@ public final class JavaPackage implements HasName, HasAnnotations { private final String relativeName; private final Set classes; private final Optional packageInfo; - private final Map subPackages; + private final Map subpackages; private Optional parent = Optional.absent(); - private JavaPackage(String name, Set classes, Map subPackages) { + private JavaPackage(String name, Set classes, Map subpackages) { this.name = checkNotNull(name); relativeName = name.substring(name.lastIndexOf(".") + 1); this.classes = ImmutableSet.copyOf(classes); this.packageInfo = tryGetClassWithSimpleName("package-info"); - this.subPackages = ImmutableMap.copyOf(subPackages); + this.subpackages = ImmutableMap.copyOf(subpackages); } /** @@ -221,36 +221,52 @@ public Set getClasses() { @PublicAPI(usage = ACCESS) public Set getAllClasses() { ImmutableSet.Builder result = ImmutableSet.builder().addAll(classes); - for (JavaPackage subPackage : getSubPackages()) { - result.addAll(subPackage.getAllClasses()); + for (JavaPackage subpackage : getSubpackages()) { + result.addAll(subpackage.getAllClasses()); } return result.build(); } /** * @return all (direct) sub-packages contained in this package, e.g. {@code [java.lang, java.io, ...]} for package {@code java} - * (compare {@link #getAllSubPackages()}) + * (compare {@link #getAllSubpackages()}) + */ + @PublicAPI(usage = ACCESS) + public Set getSubpackages() { + return ImmutableSet.copyOf(subpackages.values()); + } + + /** + * @deprecated Use {@link #getSubpackages()} instead. */ @PublicAPI(usage = ACCESS) public Set getSubPackages() { - return ImmutableSet.copyOf(subPackages.values()); + return getSubpackages(); } /** * @return all sub-packages including nested sub-packages contained in this package, * e.g. {@code [java.lang, java.lang.annotation, java.util, java.util.concurrent, ...]} for package {@code java} - * (compare {@link #getSubPackages()}) + * (compare {@link #getSubpackages()}) */ @PublicAPI(usage = ACCESS) - public Set getAllSubPackages() { + public Set getAllSubpackages() { ImmutableSet.Builder result = ImmutableSet.builder(); - for (JavaPackage subPackage : getSubPackages()) { - result.add(subPackage); - result.addAll(subPackage.getAllSubPackages()); + for (JavaPackage subpackage : getSubpackages()) { + result.add(subpackage); + result.addAll(subpackage.getAllSubpackages()); } return result.build(); } + /** + * @deprecated Use {@link #getAllSubpackages()} instead. + */ + @PublicAPI(usage = ACCESS) + public Set getAllSubPackages() { + return getAllSubpackages(); + } + /** * @param clazz a {@link JavaClass} * @return true if this package (directly) contains this {@link JavaClass} @@ -388,10 +404,10 @@ private Optional tryGetPackage(JavaPackage currentPackage, Deque predicate, ClassVisitor visitor) for (JavaClass javaClass : getClassesWith(predicate)) { visitor.visit(javaClass); } - for (JavaPackage subPackage : getSubPackages()) { - subPackage.accept(predicate, visitor); + for (JavaPackage subpackage : getSubpackages()) { + subpackage.accept(predicate, visitor); } } @@ -487,8 +503,8 @@ public void accept(Predicate predicate, PackageVisitor visi if (predicate.apply(this)) { visitor.visit(this); } - for (JavaPackage subPackage : getSubPackages()) { - subPackage.accept(predicate, visitor); + for (JavaPackage subpackage : getSubpackages()) { + subpackage.accept(predicate, visitor); } } @@ -522,7 +538,7 @@ static JavaPackage from(Iterable classes) { private static class Tree { private final String packageName; - private final Map subPackageTrees; + private final Map subpackageTrees; private final Set classes = new HashSet<>(); Tree(Iterable classes) { @@ -537,14 +553,14 @@ private Tree(String packageName, Iterable classes) { if (clazz.getPackageName().equals(packageName)) { this.classes.add(clazz); } else { - String subPackageName = findSubPackageName(packageName, clazz); - childPackages.put(subPackageName, clazz); + String subpackageName = findSubpackageName(packageName, clazz); + childPackages.put(subpackageName, clazz); } } - this.subPackageTrees = createSubTrees(packageName, childPackages); + this.subpackageTrees = createSubTrees(packageName, childPackages); } - private String findSubPackageName(String packageName, JavaClass clazz) { + private String findSubpackageName(String packageName, JavaClass clazz) { String packageRest = !packageName.isEmpty() ? clazz.getPackageName().substring(packageName.length() + 1) : clazz.getPackageName(); @@ -567,18 +583,18 @@ private String joinSkippingEmpty(String first, String second) { JavaPackage toJavaPackage() { JavaPackage result = createJavaPackage(); - for (JavaPackage subPackage : result.getSubPackages()) { - subPackage.setParent(result); + for (JavaPackage subpackage : result.getSubpackages()) { + subpackage.setParent(result); } return result; } private JavaPackage createJavaPackage() { - ImmutableMap.Builder subPackages = ImmutableMap.builder(); - for (Map.Entry entry : subPackageTrees.entrySet()) { - subPackages.put(entry.getKey(), entry.getValue().toJavaPackage()); + ImmutableMap.Builder subpackages = ImmutableMap.builder(); + for (Map.Entry entry : subpackageTrees.entrySet()) { + subpackages.put(entry.getKey(), entry.getValue().toJavaPackage()); } - return new JavaPackage(packageName, classes, subPackages.build()); + return new JavaPackage(packageName, classes, subpackages.build()); } } @@ -619,7 +635,7 @@ public Set apply(JavaPackage javaPackage) { new ChainableFunction>() { @Override public Set apply(JavaPackage javaPackage) { - return javaPackage.getSubPackages(); + return javaPackage.getSubpackages(); } }; } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaType.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaType.java index 7aebefea55..7b386eed85 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaType.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaType.java @@ -16,6 +16,7 @@ package com.tngtech.archunit.core.domain; import com.tngtech.archunit.PublicAPI; +import com.tngtech.archunit.base.ChainableFunction; import com.tngtech.archunit.core.domain.properties.HasName; import static com.tngtech.archunit.PublicAPI.Usage.ACCESS; @@ -37,4 +38,17 @@ public interface JavaType extends HasName { */ @PublicAPI(usage = ACCESS) JavaClass toErasure(); + + final class Functions { + private Functions() { + } + + @PublicAPI(usage = ACCESS) + public static final ChainableFunction TO_ERASURE = new ChainableFunction() { + @Override + public JavaClass apply(JavaType input) { + return input.toErasure(); + } + }; + } } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/ReverseDependencies.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/ReverseDependencies.java index d0135c96ba..a88fc1b898 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/ReverseDependencies.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/ReverseDependencies.java @@ -272,7 +272,7 @@ public Set load(MEMBER member) { private Set getPossibleTargetClassesForAccess(JavaClass owner) { return ImmutableSet.builder() .add(owner) - .addAll(owner.getAllSubClasses()) + .addAll(owner.getAllSubclasses()) .build(); } } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/properties/HasThrowsClause.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/properties/HasThrowsClause.java index 771566db89..ca74114463 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/properties/HasThrowsClause.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/properties/HasThrowsClause.java @@ -70,7 +70,7 @@ public static DescribedPredicate> throwsClauseContainingType( @PublicAPI(usage = ACCESS) public static DescribedPredicate> throwsClauseContainingType(DescribedPredicate predicate) { - DescribedPredicate> declarationPredicate = GET_RAW_TYPE.is(predicate).forSubType(); + DescribedPredicate> declarationPredicate = GET_RAW_TYPE.is(predicate).forSubtype(); return throwsClause(anyElementThat(declarationPredicate)).as("throws clause containing type " + predicate.getDescription()); } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/AccessRecord.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/AccessRecord.java index f372a776d9..8a0dc24e99 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/AccessRecord.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/AccessRecord.java @@ -356,7 +356,7 @@ public ClassHierarchyPath(JavaClassDescriptor childType, JavaClass parent) { } private Optional tryFindChildInHierarchy(JavaClassDescriptor childType, JavaClass parent) { - for (JavaClass subclass : parent.getAllSubClasses()) { + for (JavaClass subclass : parent.getAllSubclasses()) { if (subclass.getName().equals(childType.getFullyQualifiedClassName())) { return Optional.of(subclass); } @@ -413,12 +413,12 @@ private ClassHierarchyResolutionStrategy(JavaClass child, JavaClass parent) { @Override public boolean hasNext() { - return !current.equals(parent) && current.getSuperClass().isPresent(); + return !current.equals(parent) && current.getRawSuperclass().isPresent(); } @Override public JavaClass next() { - current = current.getSuperClass().get(); + current = current.getRawSuperclass().get(); return current; } } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileImportRecord.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileImportRecord.java index c838527351..7cacf48340 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileImportRecord.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileImportRecord.java @@ -30,6 +30,7 @@ import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaMember; import com.tngtech.archunit.core.domain.JavaMethod; +import com.tngtech.archunit.core.importer.DomainBuilders.JavaParameterizedTypeBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaTypeParameterBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.TypeParametersBuilder; import org.slf4j.Logger; @@ -41,13 +42,14 @@ class ClassFileImportRecord { private static final Logger LOG = LoggerFactory.getLogger(ClassFileImportRecord.class); private static final TypeParametersBuilder NO_TYPE_PARAMETERS = - new TypeParametersBuilder(Collections.>emptySet()); + new TypeParametersBuilder(Collections.>emptyList()); private final Map classes = new HashMap<>(); - private final Map superClassNamesByOwner = new HashMap<>(); + private final Map superclassNamesByOwner = new HashMap<>(); private final SetMultimap interfaceNamesByOwner = HashMultimap.create(); private final Map typeParametersBuilderByOwner = new HashMap<>(); + private final Map> genericSuperclassBuilderByOwner = new HashMap<>(); private final SetMultimap fieldBuildersByOwner = HashMultimap.create(); private final SetMultimap methodBuildersByOwner = HashMultimap.create(); private final SetMultimap constructorBuildersByOwner = HashMultimap.create(); @@ -60,21 +62,25 @@ class ClassFileImportRecord { private final Set rawMethodCallRecords = new HashSet<>(); private final Set rawConstructorCallRecords = new HashSet<>(); - void setSuperClass(String ownerName, String superClassName) { - checkState(!superClassNamesByOwner.containsKey(ownerName), + void setSuperclass(String ownerName, String superclassName) { + checkState(!superclassNamesByOwner.containsKey(ownerName), "Attempted to add %s as a second superclass to %s, this is most likely a bug", - superClassName, ownerName); - superClassNamesByOwner.put(ownerName, superClassName); + superclassName, ownerName); + superclassNamesByOwner.put(ownerName, superclassName); } void addInterfaces(String ownerName, Set interfaceNames) { interfaceNamesByOwner.putAll(ownerName, interfaceNames); } - public void addTypeParameters(String ownerName, TypeParametersBuilder builder) { + void addTypeParameters(String ownerName, TypeParametersBuilder builder) { typeParametersBuilderByOwner.put(ownerName, builder); } + void addGenericSuperclass(String ownerName, JavaParameterizedTypeBuilder genericSuperclassBuilder) { + genericSuperclassBuilderByOwner.put(ownerName, genericSuperclassBuilder); + } + void addField(String ownerName, DomainBuilders.JavaFieldBuilder fieldBuilder) { fieldBuildersByOwner.put(ownerName, fieldBuilder); } @@ -110,8 +116,8 @@ void setEnclosingClass(String ownerName, String enclosingClassName) { enclosingClassNamesByOwner.register(ownerName, enclosingClassName); } - Optional getSuperClassFor(String name) { - return Optional.fromNullable(superClassNamesByOwner.get(name)); + Optional getSuperclassFor(String name) { + return Optional.fromNullable(superclassNamesByOwner.get(name)); } Set getInterfaceNamesFor(String ownerName) { @@ -125,6 +131,10 @@ TypeParametersBuilder getTypeParameterBuildersFor(String ownerName) { return typeParametersBuilderByOwner.get(ownerName); } + Optional> getGenericSuperclassFor(JavaClass owner) { + return Optional.fromNullable(genericSuperclassBuilderByOwner.get(owner.getName())); + } + Set getFieldBuildersFor(String ownerName) { return fieldBuildersByOwner.get(ownerName); } @@ -229,11 +239,11 @@ Set getAccessRecords() { .build(); } - Set getAllSuperClassNames() { - return ImmutableSet.copyOf(superClassNamesByOwner.values()); + Set getAllSuperclassNames() { + return ImmutableSet.copyOf(superclassNamesByOwner.values()); } - Set getAllSuperInterfaceNames() { + Set getAllSuperinterfaceNames() { return ImmutableSet.copyOf(interfaceNamesByOwner.values()); } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java index 8b8d9ec0d6..a9c93fe465 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java @@ -77,10 +77,10 @@ public boolean isNew(String className) { } @Override - public void onNewClass(String className, Optional superClassName, Set interfaceNames) { + public void onNewClass(String className, Optional superclassName, Set interfaceNames) { ownerName = className; - if (superClassName.isPresent()) { - importRecord.setSuperClass(ownerName, superClassName.get()); + if (superclassName.isPresent()) { + importRecord.setSuperclass(ownerName, superclassName.get()); } importRecord.addInterfaces(ownerName, interfaceNames); } @@ -90,6 +90,11 @@ public void onDeclaredTypeParameters(TypeParametersBuilder typeParametersBuilder importRecord.addTypeParameters(ownerName, typeParametersBuilder); } + @Override + public void onGenericSuperclass(DomainBuilders.JavaParameterizedTypeBuilder genericSuperclassBuilder) { + importRecord.addGenericSuperclass(ownerName, genericSuperclassBuilder); + } + @Override public void onDeclaredField(DomainBuilders.JavaFieldBuilder fieldBuilder) { importRecord.addField(ownerName, fieldBuilder); diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java index 68cf36cf4e..8e93dcd7ea 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java @@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.google.common.collect.SetMultimap; +import com.google.common.collect.Sets; import com.tngtech.archunit.base.Function; import com.tngtech.archunit.base.HasDescription; import com.tngtech.archunit.base.Optional; @@ -42,18 +43,21 @@ import com.tngtech.archunit.core.domain.JavaMethod; import com.tngtech.archunit.core.domain.JavaMethodCall; import com.tngtech.archunit.core.domain.JavaStaticInitializer; +import com.tngtech.archunit.core.domain.JavaType; import com.tngtech.archunit.core.domain.JavaTypeVariable; import com.tngtech.archunit.core.importer.AccessRecord.FieldAccessRecord; import com.tngtech.archunit.core.importer.DomainBuilders.JavaAnnotationBuilder.ValueBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaConstructorCallBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaFieldAccessBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaMethodCallBuilder; +import com.tngtech.archunit.core.importer.DomainBuilders.JavaParameterizedTypeBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.TypeParametersBuilder; import com.tngtech.archunit.core.importer.resolvers.ClassResolver; import static com.tngtech.archunit.core.domain.DomainObjectCreationContext.completeAnnotations; import static com.tngtech.archunit.core.domain.DomainObjectCreationContext.completeClassHierarchy; import static com.tngtech.archunit.core.domain.DomainObjectCreationContext.completeEnclosingClass; +import static com.tngtech.archunit.core.domain.DomainObjectCreationContext.completeGenericSuperclass; import static com.tngtech.archunit.core.domain.DomainObjectCreationContext.completeMembers; import static com.tngtech.archunit.core.domain.DomainObjectCreationContext.completeTypeParameters; import static com.tngtech.archunit.core.domain.DomainObjectCreationContext.createJavaClasses; @@ -68,21 +72,21 @@ class ClassGraphCreator implements ImportContext { private final SetMultimap processedFieldAccessRecords = HashMultimap.create(); private final SetMultimap> processedMethodCallRecords = HashMultimap.create(); private final SetMultimap> processedConstructorCallRecords = HashMultimap.create(); - private final Function> superClassStrategy; + private final Function> superclassStrategy; private final Function> interfaceStrategy; ClassGraphCreator(ClassFileImportRecord importRecord, ClassResolver classResolver) { this.importRecord = importRecord; classes = new ImportedClasses(importRecord.getClasses(), classResolver); - superClassStrategy = createSuperClassStrategy(); + superclassStrategy = createSuperclassStrategy(); interfaceStrategy = createInterfaceStrategy(); } - private Function> createSuperClassStrategy() { + private Function> createSuperclassStrategy() { return new Function>() { @Override public Set apply(JavaClass input) { - return importRecord.getSuperClassFor(input.getName()).asSet(); + return importRecord.getSuperclassFor(input.getName()).asSet(); } }; } @@ -112,12 +116,12 @@ private void ensureCallTargetsArePresent() { } private void ensureClassesOfInheritanceHierarchiesArePresent() { - for (String superClassName : importRecord.getAllSuperClassNames()) { - resolveInheritance(superClassName, superClassStrategy); + for (String superclassName : importRecord.getAllSuperclassNames()) { + resolveInheritance(superclassName, superclassStrategy); } - for (String superInterfaceName : importRecord.getAllSuperInterfaceNames()) { - resolveInheritance(superInterfaceName, interfaceStrategy); + for (String superinterfaceName : importRecord.getAllSuperinterfaceNames()) { + resolveInheritance(superinterfaceName, interfaceStrategy); } } @@ -132,6 +136,7 @@ private void completeClasses() { completeClassHierarchy(javaClass, this); completeEnclosingClass(javaClass, this); completeTypeParameters(javaClass, this); + completeGenericSuperclass(javaClass, this); completeMembers(javaClass, this); completeAnnotations(javaClass, this); } @@ -220,13 +225,30 @@ B accessBuilderFrom(B builder, AccessRecord record) { } @Override - public Optional createSuperClass(JavaClass owner) { - Optional superClassName = importRecord.getSuperClassFor(owner.getName()); - return superClassName.isPresent() ? - Optional.of(classes.getOrResolve(superClassName.get())) : + public Optional createSuperclass(JavaClass owner) { + Optional superclassName = importRecord.getSuperclassFor(owner.getName()); + return superclassName.isPresent() ? + Optional.of(classes.getOrResolve(superclassName.get())) : Optional.absent(); } + @Override + public Optional createGenericSuperclass(JavaClass owner) { + Optional> genericSuperclassBuilder = importRecord.getGenericSuperclassFor(owner); + return genericSuperclassBuilder.isPresent() + ? Optional.of(genericSuperclassBuilder.get().build(owner, getTypeParametersInContextOf(owner), classes.byTypeName())) + : Optional.absent(); + } + + private static Iterable> getTypeParametersInContextOf(JavaClass javaClass) { + Set> result = Sets.>newHashSet(javaClass.getTypeParameters()); + while (javaClass.getEnclosingClass().isPresent()) { + javaClass = javaClass.getEnclosingClass().get(); + result.addAll(javaClass.getTypeParameters()); + } + return result; + } + @Override public Set createInterfaces(JavaClass owner) { ImmutableSet.Builder result = ImmutableSet.builder(); diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/DomainBuilders.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/DomainBuilders.java index 7568944c65..a50f751d12 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/DomainBuilders.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/DomainBuilders.java @@ -16,7 +16,6 @@ package com.tngtech.archunit.core.importer; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; @@ -541,9 +540,9 @@ public List getUpperBounds(Iterable> all } static class TypeParametersBuilder { - private final Collection> typeParameterBuilders; + private final List> typeParameterBuilders; - TypeParametersBuilder(Collection> typeParameterBuilders) { + TypeParametersBuilder(List> typeParameterBuilders) { this.typeParameterBuilders = typeParameterBuilders; } @@ -633,14 +632,21 @@ void addTypeArgument(JavaTypeCreationProcess typeCreationProcess) { } @Override - public JavaParameterizedType build(OWNER owner, Iterable> allTypeParametersInContext, ClassesByTypeName classes) { + public JavaType build(OWNER owner, Iterable> allTypeParametersInContext, ClassesByTypeName classes) { List typeArguments = buildJavaTypes(typeArgumentCreationProcesses, owner, allTypeParametersInContext, classes); - return new ImportedParameterizedType(classes.get(type.getFullyQualifiedClassName()), typeArguments); + return typeArguments.isEmpty() + ? classes.get(type.getFullyQualifiedClassName()) + : new ImportedParameterizedType(classes.get(type.getFullyQualifiedClassName()), typeArguments); } String getTypeName() { return type.getFullyQualifiedClassName(); } + + JavaParameterizedTypeBuilder forInnerClass(String simpleInnerClassName) { + return new JavaParameterizedTypeBuilder<>(JavaClassDescriptorImporter.createFromAsmObjectTypeName( + type.getFullyQualifiedClassName() + '$' + simpleInnerClassName)); + } } private static List buildJavaTypes(List> typeCreationProcesses, OWNER owner, Iterable> allGenericParametersInContext, ClassesByTypeName classes) { diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java index f01f3c4083..a578d7b568 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java @@ -101,8 +101,8 @@ public void visit(int version, int access, String name, String signature, String boolean opCodeForInterfaceIsPresent = (access & Opcodes.ACC_INTERFACE) != 0; boolean opCodeForEnumIsPresent = (access & Opcodes.ACC_ENUM) != 0; boolean opCodeForAnnotationIsPresent = (access & Opcodes.ACC_ANNOTATION) != 0; - Optional superClassName = getSuperClassName(superName, opCodeForInterfaceIsPresent); - LOG.trace("Found superclass {} on class '{}'", superClassName.orNull(), name); + Optional superclassName = getSuperclassName(superName, opCodeForInterfaceIsPresent); + LOG.trace("Found superclass {} on class '{}'", superclassName.orNull(), name); javaClassBuilder = new DomainBuilders.JavaClassBuilder() .withSourceDescriptor(sourceDescriptor) @@ -113,8 +113,8 @@ public void visit(int version, int access, String name, String signature, String .withModifiers(JavaModifier.getModifiersForClass(access)); className = descriptor.getFullyQualifiedClassName(); - declarationHandler.onNewClass(className, superClassName, interfaceNames); - JavaGenericTypeImporter.parseAsmTypeSignature(signature, declarationHandler); + declarationHandler.onNewClass(className, superclassName, interfaceNames); + JavaClassSignatureImporter.parseAsmTypeSignature(signature, declarationHandler); } private boolean alreadyImported(JavaClassDescriptor descriptor) { @@ -122,8 +122,8 @@ private boolean alreadyImported(JavaClassDescriptor descriptor) { } // NOTE: For some reason ASM claims superName == java/lang/Object for Interfaces??? - // This is inconsistent with the behavior of Class.getSuperClass() - private Optional getSuperClassName(String superName, boolean isInterface) { + // This is inconsistent with the behavior of Class.getSuperclass() + private Optional getSuperclassName(String superName, boolean isInterface) { return superName != null && !isInterface ? Optional.of(createTypeName(superName)) : Optional.absent(); @@ -424,10 +424,12 @@ public void setArrayResult(ValueBuilder valueBuilder) { interface DeclarationHandler { boolean isNew(String className); - void onNewClass(String className, Optional superClassName, Set interfaceNames); + void onNewClass(String className, Optional superclassName, Set interfaceNames); void onDeclaredTypeParameters(DomainBuilders.TypeParametersBuilder typeParametersBuilder); + void onGenericSuperclass(DomainBuilders.JavaParameterizedTypeBuilder genericSuperclassBuilder); + void onDeclaredField(DomainBuilders.JavaFieldBuilder fieldBuilder); void onDeclaredConstructor(DomainBuilders.JavaConstructorBuilder constructorBuilder); diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaGenericTypeImporter.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassSignatureImporter.java similarity index 83% rename from archunit/src/main/java/com/tngtech/archunit/core/importer/JavaGenericTypeImporter.java rename to archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassSignatureImporter.java index ba0b7e4b74..97185ee53f 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaGenericTypeImporter.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassSignatureImporter.java @@ -21,6 +21,7 @@ import com.google.common.base.Function; import com.google.common.base.Functions; import com.tngtech.archunit.base.HasDescription; +import com.tngtech.archunit.base.Optional; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaClassDescriptor; import com.tngtech.archunit.core.domain.JavaType; @@ -41,8 +42,8 @@ import static com.tngtech.archunit.core.domain.DomainObjectCreationContext.createGenericArrayType; import static com.tngtech.archunit.core.importer.ClassFileProcessor.ASM_API_VERSION; -class JavaGenericTypeImporter { - private static final Logger log = LoggerFactory.getLogger(JavaGenericTypeImporter.class); +class JavaClassSignatureImporter { + private static final Logger log = LoggerFactory.getLogger(JavaClassSignatureImporter.class); static void parseAsmTypeSignature(String signature, DeclarationHandler declarationHandler) { if (signature == null) { @@ -51,26 +52,41 @@ static void parseAsmTypeSignature(String signature, DeclarationHandler declarati log.trace("Analyzing signature: {}", signature); - JavaTypeVariableProcessor typeVariableProcessor = new JavaTypeVariableProcessor(); - new SignatureReader(signature).accept(typeVariableProcessor); - declarationHandler.onDeclaredTypeParameters(new TypeParametersBuilder(typeVariableProcessor.typeParameterBuilders)); - } + SignatureProcessor signatureProcessor = new SignatureProcessor(); + new SignatureReader(signature).accept(signatureProcessor); + declarationHandler.onDeclaredTypeParameters(new TypeParametersBuilder(signatureProcessor.getTypeParameterBuilders())); - private static class JavaTypeVariableProcessor extends SignatureVisitor { - private static final BoundProcessor boundProcessor = new BoundProcessor(); + Optional> genericSuperclass = signatureProcessor.getGenericSuperclass(); + if (genericSuperclass.isPresent()) { + declarationHandler.onGenericSuperclass(genericSuperclass.get()); + } + } - private final List> typeParameterBuilders = new ArrayList<>(); + private static class SignatureProcessor extends SignatureVisitor { + private final BoundProcessor boundProcessor = new BoundProcessor(); + private final GenericSuperclassProcessor superclassProcessor = new GenericSuperclassProcessor(); - JavaTypeVariableProcessor() { + SignatureProcessor() { super(ASM_API_VERSION); } + List> getTypeParameterBuilders() { + return boundProcessor.typeParameterBuilders; + } + + public Optional> getGenericSuperclass() { + return Optional.fromNullable(superclassProcessor.superclass); + } + @Override public void visitFormalTypeParameter(String name) { log.trace("Encountered type parameter {}", name); - JavaTypeParameterBuilder type = new JavaTypeParameterBuilder<>(name); - boundProcessor.setCurrentType(type); - typeParameterBuilders.add(type); + boundProcessor.addTypeParameter(name); + } + + @Override + public SignatureVisitor visitSuperclass() { + return superclassProcessor; } @Override @@ -84,6 +100,8 @@ public SignatureVisitor visitInterfaceBound() { } private static class BoundProcessor extends SignatureVisitor { + private final List> typeParameterBuilders = new ArrayList<>(); + private JavaTypeParameterBuilder currentType; private JavaParameterizedTypeBuilder currentBound; @@ -91,8 +109,10 @@ private static class BoundProcessor extends SignatureVisitor { super(ASM_API_VERSION); } - void setCurrentType(JavaTypeParameterBuilder type) { - this.currentType = type; + void addTypeParameter(String typeName) { + currentType = new JavaTypeParameterBuilder<>(typeName); + currentBound = null; + typeParameterBuilders.add(currentType); } @Override @@ -117,7 +137,30 @@ public void visitTypeVariable(String name) { @Override public SignatureVisitor visitTypeArgument(char wildcard) { - return TypeArgumentProcessor.create(wildcard, currentBound, Functions.identity(), ReferenceCreationProcess.JavaTypeVariableFinisher.IDENTITY); + return TypeArgumentProcessor.create(wildcard, currentBound); + } + } + + private static class GenericSuperclassProcessor extends SignatureVisitor { + private JavaParameterizedTypeBuilder superclass; + + GenericSuperclassProcessor() { + super(ASM_API_VERSION); + } + + @Override + public void visitClassType(String internalObjectName) { + superclass = new JavaParameterizedTypeBuilder<>(JavaClassDescriptorImporter.createFromAsmObjectTypeName(internalObjectName)); + } + + @Override + public void visitInnerClassType(String name) { + superclass = superclass.forInnerClass(name); + } + + @Override + public SignatureVisitor visitTypeArgument(char wildcard) { + return TypeArgumentProcessor.create(wildcard, superclass); } } } @@ -234,7 +277,6 @@ String getFinishedName(String name) { } @Override - @SuppressWarnings("ConstantConditions") // we never return null by convention public void visitClassType(String internalObjectName) { JavaClassDescriptor type = typeMapping.apply(JavaClassDescriptorImporter.createFromAsmObjectTypeName(internalObjectName)); log.trace("Encountered {} for {}: Class type {}", typeArgumentType.description, parameterizedType.getTypeName(), type.getFullyQualifiedClassName()); @@ -266,6 +308,10 @@ public SignatureVisitor visitArrayType() { return new TypeArgumentProcessor(typeArgumentType, parameterizedType, compose(typeMapping, TO_ARRAY_TYPE), typeVariableFinisher.after(GENERIC_ARRAY_CREATOR)); } + static TypeArgumentProcessor create(char identifier, JavaParameterizedTypeBuilder parameterizedType) { + return create(identifier, parameterizedType, Functions.identity(), ReferenceCreationProcess.JavaTypeVariableFinisher.IDENTITY); + } + static TypeArgumentProcessor create( char identifier, JavaParameterizedTypeBuilder parameterizedType, diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/ArchCondition.java b/archunit/src/main/java/com/tngtech/archunit/lang/ArchCondition.java index 45fb6c06b7..4255518cf6 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/ArchCondition.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/ArchCondition.java @@ -59,11 +59,11 @@ public void finish(ConditionEvents events) { } public ArchCondition and(ArchCondition condition) { - return new AndCondition<>(this, condition.forSubType()); + return new AndCondition<>(this, condition.forSubtype()); } public ArchCondition or(ArchCondition condition) { - return new OrCondition<>(this, condition.forSubType()); + return new OrCondition<>(this, condition.forSubtype()); } public String getDescription() { @@ -95,10 +95,18 @@ public String toString() { } @SuppressWarnings("unchecked") // Cast is safe since input parameter is contravariant - public ArchCondition forSubType() { + public ArchCondition forSubtype() { return (ArchCondition) this; } + /** + * @deprecated Use {@link #forSubtype()} instead. + */ + @Deprecated + public ArchCondition forSubType() { + return forSubtype(); + } + private abstract static class JoinCondition extends ArchCondition { private final Collection> conditions; diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/conditions/ArchConditions.java b/archunit/src/main/java/com/tngtech/archunit/lang/conditions/ArchConditions.java index c6d9682255..6ed6e00983 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/conditions/ArchConditions.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/conditions/ArchConditions.java @@ -173,7 +173,7 @@ public static ArchCondition accessFieldWhere(DescribedPredicate onlyAccessFieldsThat(final DescribedPredicate predicate) { ChainableFunction getTarget = JavaAccess.Functions.Get.target(); DescribedPredicate accessPredicate = getTarget.then(FieldAccessTarget.Functions.RESOLVE) - .is(anyElementThat(predicate.forSubType()).or(empty())); + .is(anyElementThat(predicate.forSubtype()).or(empty())); return new ClassOnlyAccessesCondition<>(accessPredicate, GET_FIELD_ACCESSES_FROM_SELF) .as("only access fields that " + predicate.getDescription()); } @@ -206,7 +206,7 @@ public static ArchCondition callMethodWhere(final DescribedPredicate< public static ArchCondition onlyCallMethodsThat(final DescribedPredicate predicate) { ChainableFunction getTarget = JavaAccess.Functions.Get.target(); DescribedPredicate callPredicate = getTarget.then(MethodCallTarget.Functions.RESOLVE) - .is(anyElementThat(predicate.forSubType()).or(empty())); + .is(anyElementThat(predicate.forSubtype()).or(empty())); return new ClassOnlyAccessesCondition<>(callPredicate, GET_METHOD_CALLS_FROM_SELF) .as("only call methods that " + predicate.getDescription()); } @@ -239,7 +239,7 @@ public static ArchCondition callConstructorWhere(final DescribedPredi public static ArchCondition onlyCallConstructorsThat(final DescribedPredicate predicate) { ChainableFunction getTarget = JavaAccess.Functions.Get.target(); DescribedPredicate callPredicate = getTarget.then(ConstructorCallTarget.Functions.RESOLVE) - .is(anyElementThat(predicate.forSubType()).or(empty())); + .is(anyElementThat(predicate.forSubtype()).or(empty())); return new ClassOnlyAccessesCondition<>(callPredicate, GET_CONSTRUCTOR_CALLS_FROM_SELF) .as("only call constructors that " + predicate.getDescription()); } @@ -254,7 +254,7 @@ public static ArchCondition callCodeUnitWhere(DescribedPredicate onlyCallCodeUnitsThat(final DescribedPredicate predicate) { ChainableFunction, CodeUnitCallTarget> getTarget = JavaAccess.Functions.Get.target(); DescribedPredicate> callPredicate = getTarget.then(CodeUnitCallTarget.Functions.RESOLVE) - .is(anyElementThat(predicate.forSubType()).or(empty())); + .is(anyElementThat(predicate.forSubtype()).or(empty())); return new ClassOnlyAccessesCondition<>(callPredicate, GET_CALLS_FROM_SELF) .as("only call code units that " + predicate.getDescription()); } @@ -263,7 +263,7 @@ public static ArchCondition onlyCallCodeUnitsThat(final DescribedPred public static ArchCondition onlyAccessMembersThat(final DescribedPredicate predicate) { ChainableFunction, AccessTarget> getTarget = JavaAccess.Functions.Get.target(); DescribedPredicate> accessPredicate = getTarget.then(AccessTarget.Functions.RESOLVE) - .is(anyElementThat(predicate.forSubType()).or(empty())); + .is(anyElementThat(predicate.forSubtype()).or(empty())); return new ClassOnlyAccessesCondition<>(accessPredicate, GET_ACCESSES_FROM_SELF) .as("only access members that " + predicate.getDescription()); } @@ -534,7 +534,7 @@ public static ArchCondition haveSimpleNameNotEndingWith(String suffix @PublicAPI(usage = ACCESS) public static ArchCondition haveNameMatching(final String regex) { - final DescribedPredicate haveNameMatching = have(nameMatching(regex)).forSubType(); + final DescribedPredicate haveNameMatching = have(nameMatching(regex)).forSubtype(); return new MatchingCondition<>(haveNameMatching, regex); } @@ -546,7 +546,7 @@ public static ArchCondition haveFullNameMatching(String regex) { - final DescribedPredicate haveFullNameMatching = have(fullNameMatching(regex)).forSubType(); + final DescribedPredicate haveFullNameMatching = have(fullNameMatching(regex)).forSubtype(); return new MatchingCondition<>(haveFullNameMatching, regex); } @@ -559,42 +559,42 @@ ArchCondition haveFullNameMatching(String regex) { @PublicAPI(usage = ACCESS) public static ArchCondition haveNameStartingWith(String prefix) { - final DescribedPredicate haveNameStartingWith = have(nameStartingWith(prefix)).forSubType(); + final DescribedPredicate haveNameStartingWith = have(nameStartingWith(prefix)).forSubtype(); return new StartingCondition<>(haveNameStartingWith, prefix); } @PublicAPI(usage = ACCESS) public static ArchCondition haveNameNotStartingWith(String prefix) { - final DescribedPredicate haveNameStartingWith = have(nameStartingWith(prefix)).forSubType(); + final DescribedPredicate haveNameStartingWith = have(nameStartingWith(prefix)).forSubtype(); return not(new StartingCondition<>(haveNameStartingWith, prefix)).as("have name not starting with '%s'", prefix); } @PublicAPI(usage = ACCESS) public static ArchCondition haveNameContaining(String infix) { - final DescribedPredicate haveNameContaining = have(nameContaining(infix)).forSubType(); + final DescribedPredicate haveNameContaining = have(nameContaining(infix)).forSubtype(); return new ContainingCondition<>(haveNameContaining, infix); } @PublicAPI(usage = ACCESS) public static ArchCondition haveNameNotContaining(String infix) { - final DescribedPredicate haveNameContaining = have(nameContaining(infix)).forSubType(); + final DescribedPredicate haveNameContaining = have(nameContaining(infix)).forSubtype(); return not(new ContainingCondition<>(haveNameContaining, infix)).as("have name not containing '%s'", infix); } @PublicAPI(usage = ACCESS) public static ArchCondition haveNameEndingWith(String suffix) { - final DescribedPredicate haveNameEndingWith = have(nameEndingWith(suffix)).forSubType(); + final DescribedPredicate haveNameEndingWith = have(nameEndingWith(suffix)).forSubtype(); return new EndingCondition<>(haveNameEndingWith, suffix); } @PublicAPI(usage = ACCESS) public static ArchCondition haveNameNotEndingWith(String suffix) { - final DescribedPredicate haveNameEndingWith = have(nameEndingWith(suffix)).forSubType(); + final DescribedPredicate haveNameEndingWith = have(nameEndingWith(suffix)).forSubtype(); return not(new EndingCondition<>(haveNameEndingWith, suffix)).as("have name not ending with '%s'", suffix); } @@ -1145,7 +1145,7 @@ private static class NumberOfElementsCondition extends ArchCondition NumberOfElementsCondition(DescribedPredicate predicate) { super("contain number of elements " + predicate.getDescription()); - this.predicate = predicate.forSubType(); + this.predicate = predicate.forSubtype(); allClassNames = new TreeSet<>(); } @@ -1374,7 +1374,7 @@ private static class IsConditionByPredicate predicate) { super(ArchPredicates.be(predicate).getDescription()); this.eventDescription = eventDescription; - this.predicate = predicate.forSubType(); + this.predicate = predicate.forSubtype(); } @Override @@ -1391,7 +1391,7 @@ private static class HaveConditionByPredicate rawType) { super(ArchPredicates.have(rawType).getDescription()); - this.rawType = rawType.forSubType(); + this.rawType = rawType.forSubtype(); } @Override diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/conditions/ArchPredicates.java b/archunit/src/main/java/com/tngtech/archunit/lang/conditions/ArchPredicates.java index 11c00b3082..871313b646 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/conditions/ArchPredicates.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/conditions/ArchPredicates.java @@ -33,7 +33,7 @@ private ArchPredicates() { */ @PublicAPI(usage = ACCESS) public static DescribedPredicate is(DescribedPredicate predicate) { - return predicate.as("is " + predicate.getDescription()).forSubType(); + return predicate.as("is " + predicate.getDescription()).forSubtype(); } /** @@ -45,7 +45,7 @@ public static DescribedPredicate is(DescribedPredicate predica */ @PublicAPI(usage = ACCESS) public static DescribedPredicate are(DescribedPredicate predicate) { - return predicate.as("are " + predicate.getDescription()).forSubType(); + return predicate.as("are " + predicate.getDescription()).forSubtype(); } /** @@ -57,7 +57,7 @@ public static DescribedPredicate are(DescribedPredicate predic */ @PublicAPI(usage = ACCESS) public static DescribedPredicate has(DescribedPredicate predicate) { - return predicate.as("has " + predicate.getDescription()).forSubType(); + return predicate.as("has " + predicate.getDescription()).forSubtype(); } /** @@ -69,7 +69,7 @@ public static DescribedPredicate has(DescribedPredicate predic */ @PublicAPI(usage = ACCESS) public static DescribedPredicate have(DescribedPredicate predicate) { - return predicate.as("have " + predicate.getDescription()).forSubType(); + return predicate.as("have " + predicate.getDescription()).forSubtype(); } /** @@ -81,6 +81,6 @@ public static DescribedPredicate have(DescribedPredicate predi */ @PublicAPI(usage = ACCESS) public static DescribedPredicate be(DescribedPredicate predicate) { - return predicate.as("be " + predicate.getDescription()).forSubType(); + return predicate.as("be " + predicate.getDescription()).forSubtype(); } } diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/conditions/FieldAccessCondition.java b/archunit/src/main/java/com/tngtech/archunit/lang/conditions/FieldAccessCondition.java index b4d294907f..a9b1854e23 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/conditions/FieldAccessCondition.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/conditions/FieldAccessCondition.java @@ -41,13 +41,13 @@ public void check(JavaFieldAccess item, ConditionEvents events) { static class FieldGetAccessCondition extends FieldAccessCondition { FieldGetAccessCondition(DescribedPredicate predicate) { - super(predicate.forSubType().and(accessType(GET))); + super(predicate.forSubtype().and(accessType(GET))); } } static class FieldSetAccessCondition extends FieldAccessCondition { FieldSetAccessCondition(DescribedPredicate predicate) { - super(predicate.forSubType().and(accessType(SET))); + super(predicate.forSubtype().and(accessType(SET))); } } } diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/AbstractGivenCodeUnitsInternal.java b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/AbstractGivenCodeUnitsInternal.java index 1d11b4fa72..fe9e03dd10 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/AbstractGivenCodeUnitsInternal.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/AbstractGivenCodeUnitsInternal.java @@ -93,7 +93,7 @@ public CodeUnitsShouldInternal should() { @Override public CodeUnitsShouldInternal should(ArchCondition condition) { - return new CodeUnitsShouldInternal(finishedClassesTransformer(), priority, condition.forSubType(), prepareCondition); + return new CodeUnitsShouldInternal(finishedClassesTransformer(), priority, condition.forSubtype(), prepareCondition); } private static class GivenCodeUnitsFactory implements Factory { diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/AbstractGivenMembersInternal.java b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/AbstractGivenMembersInternal.java index f8cb219b2a..a169b9d72e 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/AbstractGivenMembersInternal.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/AbstractGivenMembersInternal.java @@ -90,7 +90,7 @@ private GivenMembersInternal( @Override public MembersShouldConjunction should(ArchCondition condition) { - return new MembersShouldInternal(finishedClassesTransformer(), priority, condition.forSubType(), prepareCondition); + return new MembersShouldInternal(finishedClassesTransformer(), priority, condition.forSubtype(), prepareCondition); } @Override diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/AbstractMembersShouldInternal.java b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/AbstractMembersShouldInternal.java index de347f138b..e711302953 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/AbstractMembersShouldInternal.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/AbstractMembersShouldInternal.java @@ -295,7 +295,7 @@ public SELF apply(DescribedPredicate predicate) { } private SELF copyWithNewCondition(ArchCondition newCondition) { - return copyWithNewCondition(new ConditionAggregator<>(newCondition.forSubType())); + return copyWithNewCondition(new ConditionAggregator<>(newCondition.forSubtype())); } abstract SELF copyWithNewCondition(ConditionAggregator newCondition); diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenClassInternal.java b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenClassInternal.java index 0d7e7f355d..baab9b316e 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenClassInternal.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenClassInternal.java @@ -43,6 +43,6 @@ public ClassesShould should() { @Override public ClassesShouldConjunction should(ArchCondition condition) { - return new ClassesShouldInternal(classesTransformer, priority, condition.forSubType(), prepareCondition); + return new ClassesShouldInternal(classesTransformer, priority, condition.forSubtype(), prepareCondition); } } diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenClassesInternal.java b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenClassesInternal.java index 82fbc5dfd5..69604675bf 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenClassesInternal.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenClassesInternal.java @@ -90,7 +90,7 @@ public GivenClassesConjunction apply(DescribedPredicate input @Override public ClassesShouldConjunction should(ArchCondition condition) { - return new ClassesShouldInternal(finishedClassesTransformer(), priority, condition.forSubType(), prepareCondition); + return new ClassesShouldInternal(finishedClassesTransformer(), priority, condition.forSubtype(), prepareCondition); } private static class GivenClassesFactory implements Factory { diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenConstructorsInternal.java b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenConstructorsInternal.java index 2e2e7c4560..4fc0d526b5 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenConstructorsInternal.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenConstructorsInternal.java @@ -63,7 +63,7 @@ public ConstructorsShouldInternal should() { @Override public ConstructorsShouldInternal should(ArchCondition condition) { - return new ConstructorsShouldInternal(finishedClassesTransformer(), priority, condition.forSubType(), prepareCondition); + return new ConstructorsShouldInternal(finishedClassesTransformer(), priority, condition.forSubtype(), prepareCondition); } private static class GivenConstructorsFactory implements Factory { diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenFieldsInternal.java b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenFieldsInternal.java index 2efb4b2883..2ad0cfdbd2 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenFieldsInternal.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenFieldsInternal.java @@ -64,7 +64,7 @@ public FieldsShouldInternal should() { @Override public FieldsShouldInternal should(ArchCondition condition) { - return new FieldsShouldInternal(finishedClassesTransformer(), priority, condition.forSubType(), prepareCondition); + return new FieldsShouldInternal(finishedClassesTransformer(), priority, condition.forSubtype(), prepareCondition); } @Override diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenMethodsInternal.java b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenMethodsInternal.java index 3615e138e9..97af4163b7 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenMethodsInternal.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenMethodsInternal.java @@ -79,7 +79,7 @@ public MethodsShouldInternal should() { @Override public MethodsShouldInternal should(ArchCondition condition) { - return new MethodsShouldInternal(finishedClassesTransformer(), priority, condition.forSubType(), prepareCondition); + return new MethodsShouldInternal(finishedClassesTransformer(), priority, condition.forSubtype(), prepareCondition); } private static class GivenMethodsFactory implements Factory { diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenObjectsInternal.java b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenObjectsInternal.java index afd85e612b..43af9d840c 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenObjectsInternal.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenObjectsInternal.java @@ -49,7 +49,7 @@ private GivenObjectsInternal( @Override public ArchRule should(ArchCondition condition) { - return new ObjectsShouldInternal<>(finishedClassesTransformer(), priority, condition.forSubType(), prepareCondition); + return new ObjectsShouldInternal<>(finishedClassesTransformer(), priority, condition.forSubtype(), prepareCondition); } private static class GivenObjectsFactory implements AbstractGivenObjects.Factory> { diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/ObjectsShouldInternal.java b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/ObjectsShouldInternal.java index 22b5d38ac0..705622abbe 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/ObjectsShouldInternal.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/ObjectsShouldInternal.java @@ -152,7 +152,7 @@ static AddMode and(final Function, ArchCondition> pre return new AddMode() { @Override ArchCondition apply(Optional> first, ArchCondition other) { - ArchCondition second = prepareCondition.apply(other.forSubType()); + ArchCondition second = prepareCondition.apply(other.forSubtype()); return first.isPresent() ? first.get().and(second) : second; } }; @@ -162,7 +162,7 @@ static AddMode or(final Function, ArchCondition> prep return new AddMode() { @Override ArchCondition apply(Optional> first, ArchCondition other) { - ArchCondition second = prepareCondition.apply(other.forSubType()); + ArchCondition second = prepareCondition.apply(other.forSubtype()); return first.isPresent() ? first.get().or(second) : second; } }; diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/PredicateAggregator.java b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/PredicateAggregator.java index ce98b0226d..309750b881 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/PredicateAggregator.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/PredicateAggregator.java @@ -58,7 +58,7 @@ static AddMode and() { return new AddMode() { @Override DescribedPredicate apply(Optional> first, DescribedPredicate other) { - DescribedPredicate second = other.forSubType(); + DescribedPredicate second = other.forSubtype(); return first.isPresent() ? first.get().and(second) : second; } }; @@ -68,7 +68,7 @@ static AddMode or() { return new AddMode() { @Override DescribedPredicate apply(Optional> first, DescribedPredicate other) { - DescribedPredicate second = other.forSubType(); + DescribedPredicate second = other.forSubtype(); return first.isPresent() ? first.get().or(second) : second; } }; diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/elements/MembersShould.java b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/elements/MembersShould.java index 01a1b323cb..8d94cb97c1 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/elements/MembersShould.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/elements/MembersShould.java @@ -440,7 +440,7 @@ public interface MembersShould> *

* E.g. *

-     * {@link ArchRuleDefinition#members() members()}.{@link GivenMembers#should() should()}.{@link MembersShould#beDeclaredInClassesThat(DescribedPredicate) beDeclaredInClassesThat(areSubTypeOf(Example.class))}
+     * {@link ArchRuleDefinition#members() members()}.{@link GivenMembers#should() should()}.{@link MembersShould#beDeclaredInClassesThat(DescribedPredicate) beDeclaredInClassesThat(areSubtypeOf(Example.class))}
      * 
* would be violated by someField in * diff --git a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/elements/MembersThat.java b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/elements/MembersThat.java index f863cc6e5d..48d5dd37ba 100644 --- a/archunit/src/main/java/com/tngtech/archunit/lang/syntax/elements/MembersThat.java +++ b/archunit/src/main/java/com/tngtech/archunit/lang/syntax/elements/MembersThat.java @@ -451,7 +451,7 @@ public interface MembersThat> { * * will be matched by *

-     * {@link ArchRuleDefinition#members() members()}.{@link GivenMembers#that() that()}.{@link MembersThat#areDeclaredInClassesThat(DescribedPredicate) areDeclaredInClassesThat(areSubTypeOf(Object.class))}
+     * {@link ArchRuleDefinition#members() members()}.{@link GivenMembers#that() that()}.{@link MembersThat#areDeclaredInClassesThat(DescribedPredicate) areDeclaredInClassesThat(areSubtypeOf(Object.class))}
      * 
* * @param predicate A predicate which matches classes where members have to be declared in diff --git a/archunit/src/main/java/com/tngtech/archunit/library/Architectures.java b/archunit/src/main/java/com/tngtech/archunit/library/Architectures.java index 7f14d84474..edb564febd 100644 --- a/archunit/src/main/java/com/tngtech/archunit/library/Architectures.java +++ b/archunit/src/main/java/com/tngtech/archunit/library/Architectures.java @@ -362,7 +362,7 @@ private LayerDefinition(String name, boolean optional) { @PublicAPI(usage = ACCESS) public LayeredArchitecture definedBy(DescribedPredicate predicate) { checkNotNull(predicate, "Supplied predicate must not be null"); - this.containsPredicate = predicate.forSubType(); + this.containsPredicate = predicate.forSubtype(); return LayeredArchitecture.this.addLayerDefinition(this); } diff --git a/archunit/src/main/java/com/tngtech/archunit/library/dependencies/GivenSlicesInternal.java b/archunit/src/main/java/com/tngtech/archunit/library/dependencies/GivenSlicesInternal.java index 5e15e845a6..d2072244c1 100644 --- a/archunit/src/main/java/com/tngtech/archunit/library/dependencies/GivenSlicesInternal.java +++ b/archunit/src/main/java/com/tngtech/archunit/library/dependencies/GivenSlicesInternal.java @@ -43,7 +43,7 @@ class GivenSlicesInternal implements GivenSlices, SlicesShould, GivenSlicesConju @Override public ArchRule should(ArchCondition condition) { - return ArchRule.Factory.create(classesTransformer, condition.forSubType(), priority); + return ArchRule.Factory.create(classesTransformer, condition.forSubtype(), priority); } @Override diff --git a/archunit/src/test/java/com/tngtech/archunit/core/domain/DependencyTest.java b/archunit/src/test/java/com/tngtech/archunit/core/domain/DependencyTest.java index 112b1c29c5..3d7aed0736 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/domain/DependencyTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/domain/DependencyTest.java @@ -1,6 +1,7 @@ package com.tngtech.archunit.core.domain; import java.io.IOException; +import java.io.Serializable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Field; @@ -9,6 +10,7 @@ import com.google.common.base.MoreObjects; import com.tngtech.archunit.base.DescribedPredicate; +import com.tngtech.archunit.core.domain.testobjects.ClassWithArrayDependencies; import com.tngtech.archunit.core.domain.testobjects.ClassWithDependencyOnInstanceofCheck; import com.tngtech.archunit.core.domain.testobjects.ClassWithDependencyOnInstanceofCheck.InstanceOfCheckTarget; import com.tngtech.archunit.core.importer.ClassFileImporter; @@ -86,26 +88,17 @@ public void Dependency_from_access() { } @DataProvider - public static Object[][] method_calls_to_array_types() throws NoSuchMethodException { - @SuppressWarnings("unused") - class ClassWithArrayDependencies { - private void oneDimArray() { - new String[0].clone(); - } - - private void multiDimArray() { - new String[0][0].clone(); - } - } + public static Object[][] method_calls_to_array_types() { return $$( - $(ClassWithArrayDependencies.class.getDeclaredMethod("oneDimArray"), String[].class, 93), - $(ClassWithArrayDependencies.class.getDeclaredMethod("multiDimArray"), String[][].class, 97) + $(ClassWithArrayDependencies.class, "oneDimArray", String[].class, 6), + $(ClassWithArrayDependencies.class, "multiDimArray", String[][].class, 10) ); } @Test @UseDataProvider("method_calls_to_array_types") - public void Dependency_from_access_with_component_type(Method reflectionMethodWithArrayMethodCall, Class arrayType, int expectedLineNumber) { + public void Dependency_from_access_with_component_type(Class classDependingOnArray, String nameOfMethodWithArrayMethodCall, Class arrayType, int expectedLineNumber) throws NoSuchMethodException { + Method reflectionMethodWithArrayMethodCall = classDependingOnArray.getDeclaredMethod(nameOfMethodWithArrayMethodCall); Class reflectionDeclaringClass = reflectionMethodWithArrayMethodCall.getDeclaringClass(); JavaMethod method = new ClassFileImporter().importClasses(reflectionDeclaringClass) .get(reflectionDeclaringClass).getMethod(reflectionMethodWithArrayMethodCall.getName()); @@ -115,12 +108,12 @@ public void Dependency_from_access_with_component_type(Method reflectionMethodWi DependenciesAssertion.ExpectedDependencies expectedDependencies = from(reflectionDeclaringClass).to(arrayType) .withDescriptionContaining("Method <%s> calls method <%s>", method.getFullName(), arrayType.getName() + ".clone()") - .inLocation(DependencyTest.class, expectedLineNumber); + .inLocation(classDependingOnArray, expectedLineNumber); Class expectedComponentType = arrayType.getComponentType(); while (expectedComponentType != null) { expectedDependencies.from(reflectionDeclaringClass).to(expectedComponentType) .withDescriptionContaining("Method <%s> depends on component type <%s>", method.getFullName(), expectedComponentType.getName()) - .inLocation(DependencyTest.class, expectedLineNumber); + .inLocation(classDependingOnArray, expectedLineNumber); expectedComponentType = expectedComponentType.getComponentType(); } @@ -140,7 +133,7 @@ public void Dependency_from_origin_and_target() { assertThat(dependency.getDescription()).as("description") .contains("Class <" + origin.getName() + "> implements interface <" + target.getName() + ">"); - origin = importClassWithContext(DependencySubInterface.class); + origin = importClassWithContext(DependencySubinterface.class); dependency = createDependency(origin, target); assertThat(dependency.getDescription()).as("description") .contains("Interface <" + origin.getName() + "> extends interface <" + target.getName() + ">"); @@ -275,6 +268,27 @@ class ClassWithTypeParameters { ClassWithTypeParameters.class.getName(), typeParameter.getName(), String.class.getName(), getClass().getSimpleName())); } + @Test + public void Dependency_from_generic_superclass_type_arguments() { + @SuppressWarnings("unused") + class Base { + } + @SuppressWarnings("unused") + class ClassWithTypeParameters extends Base { + } + + JavaClass javaClass = importClassesWithContext(ClassWithTypeParameters.class, String.class, Serializable.class).get(ClassWithTypeParameters.class); + JavaParameterizedType genericSuperClass = (JavaParameterizedType) javaClass.getSuperclass().get(); + + Dependency dependency = getOnlyElement(Dependency.tryCreateFromGenericSuperclassTypeArguments(javaClass, genericSuperClass, (JavaClass) genericSuperClass.getActualTypeArguments().get(0))); + + assertThatType(dependency.getOriginClass()).matches(ClassWithTypeParameters.class); + assertThatType(dependency.getTargetClass()).matches(String.class); + assertThat(dependency.getDescription()).as("description").contains(String.format( + "Class <%s> has generic superclass <%s> with type argument depending on <%s> in (%s.java:0)", + ClassWithTypeParameters.class.getName(), Base.class.getName(), String.class.getName(), getClass().getSimpleName())); + } + @Test public void origin_predicates_match() { assertThatDependency(Origin.class, Target.class) @@ -361,7 +375,7 @@ public String toString() { private static class DependencyClass { } - private interface DependencySubInterface extends DependencyInterface { + private interface DependencySubinterface extends DependencyInterface { } private interface DependencyInterface { diff --git a/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaClassTest.java b/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaClassTest.java index bfb0c160df..ca7c62f975 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaClassTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaClassTest.java @@ -244,23 +244,27 @@ public void inner_class_has_package_of_declaring_class() { @Test public void superclasses_are_found() { - JavaClass clazz = importClassesWithContext(ClassWithTwoFieldsAndTwoMethods.class, SuperClassWithFieldAndMethod.class, Parent.class) + JavaClass clazz = importClassesWithContext(ClassWithTwoFieldsAndTwoMethods.class, SuperclassWithFieldAndMethod.class, Parent.class) .get(ClassWithTwoFieldsAndTwoMethods.class); - assertThat(clazz.getAllSuperClasses()).extracting("name").containsExactly( - SuperClassWithFieldAndMethod.class.getName(), + assertThat(clazz.getAllRawSuperclasses()).extracting("name").containsExactly( + SuperclassWithFieldAndMethod.class.getName(), + Parent.class.getName(), + Object.class.getName()); + assertThat(clazz.getAllRawSuperclasses()).extracting("name").containsExactly( + SuperclassWithFieldAndMethod.class.getName(), Parent.class.getName(), Object.class.getName()); } @Test public void hierarchy_is_found() { - JavaClass clazz = importClassesWithContext(ClassWithTwoFieldsAndTwoMethods.class, SuperClassWithFieldAndMethod.class, Parent.class) + JavaClass clazz = importClassesWithContext(ClassWithTwoFieldsAndTwoMethods.class, SuperclassWithFieldAndMethod.class, Parent.class) .get(ClassWithTwoFieldsAndTwoMethods.class); assertThat(clazz.getClassHierarchy()).extracting("name").containsExactly( clazz.getName(), - SuperClassWithFieldAndMethod.class.getName(), + SuperclassWithFieldAndMethod.class.getName(), Parent.class.getName(), Object.class.getName()); } @@ -350,15 +354,15 @@ public void isMetaAnnotatedWith_correctly_resolves_cyclic_annotations() { @Test public void allAccesses_contains_accesses_from_superclass() { - JavaClass javaClass = importClasses(ClassWithTwoFieldsAndTwoMethods.class, SuperClassWithFieldAndMethod.class, Parent.class) + JavaClass javaClass = importClasses(ClassWithTwoFieldsAndTwoMethods.class, SuperclassWithFieldAndMethod.class, Parent.class) .get(ClassWithTwoFieldsAndTwoMethods.class); JavaClass anotherClass = importClassWithContext(Object.class); simulateCall().from(javaClass.getMethod("stringMethod"), 8).to(anotherClass.getMethod("toString")); - simulateCall().from(javaClass.getSuperClass().get().getMethod("objectMethod"), 8).to(anotherClass.getMethod("toString")); + simulateCall().from(javaClass.getRawSuperclass().get().getMethod("objectMethod"), 8).to(anotherClass.getMethod("toString")); assertThat(javaClass.getAccessesFromSelf()).extractingResultOf("getOriginOwner").containsOnly(javaClass); assertThat(javaClass.getAllAccessesFromSelf()).extractingResultOf("getOriginOwner") - .contains(javaClass, javaClass.getSuperClass().get()); + .contains(javaClass, javaClass.getRawSuperclass().get()); } @Test @@ -482,6 +486,34 @@ public void direct_dependencies_from_self_by_inheritance() { .inLineNumber(0)); } + @Test + public void direct_dependencies_from_self_by_inheritance_through_generic_superclass_parameters() { + @SuppressWarnings("unused") + class Base { + } + class Child extends Base< + Comparable>, + Map< + Map.Entry>, + Map>>>>>>>> { + } + + JavaClass javaClass = importClassWithContext(Child.class); + + assertThatDependencies(javaClass.getDirectDependenciesFromSelf()) + .contain(from(Child.class).to(Base.class).inLocation(getClass(), 0) + .withDescriptionContaining("extends") + + .from(Child.class) + .withExpectedDescriptionTemplate("has generic superclass <" + Base.class.getName() + "> with type argument depending on <#target>") + .to(Comparable.class, Map.class, Map.Entry.class, String.class, BufferedInputStream[][].class, Serializable.class, List.class, Set.class, Iterable.class, File.class) + + .from(Child.class).to(BufferedInputStream.class).inLocation(getClass(), 0) + .withDescriptionContaining("depends on component type <%s>", BufferedInputStream.class.getName()) + ); + } + @Test public void direct_dependencies_from_self_by_member_declarations() { JavaClass javaClass = importClasses(AhavingMembersOfTypeB.class, B.class).get(AhavingMembersOfTypeB.class); @@ -592,41 +624,18 @@ class ClassWithTypeParameters< JavaClass javaClass = importClasses(ClassWithTypeParameters.class).get(ClassWithTypeParameters.class); assertThatDependencies(javaClass.getDirectDependenciesFromSelf()) - .contain(from(ClassWithTypeParameters.class).to(List.class).inLocation(getClass(), 0) - .withDescriptionContaining("type parameter 'FIRST' depending on") - - .from(ClassWithTypeParameters.class).to(Serializable.class).inLocation(getClass(), 0) - .withDescriptionContaining("type parameter 'FIRST' depending on") - - .from(ClassWithTypeParameters.class).to(Comparable.class).inLocation(getClass(), 0) - .withDescriptionContaining("type parameter 'FIRST' depending on") - - .from(ClassWithTypeParameters.class).to(Map.class).inLocation(getClass(), 0) - .withDescriptionContaining("type parameter 'SECOND' depending on") - - .from(ClassWithTypeParameters.class).to(Map.Entry.class).inLocation(getClass(), 0) - .withDescriptionContaining("type parameter 'SECOND' depending on") - - .from(ClassWithTypeParameters.class).to(String.class).inLocation(getClass(), 0) - .withDescriptionContaining("type parameter 'SECOND' depending on") - - .from(ClassWithTypeParameters.class).to(BufferedInputStream[][].class).inLocation(getClass(), 0) - .withDescriptionContaining("type parameter 'SECOND' depending on") - - .from(ClassWithTypeParameters.class).to(Serializable.class).inLocation(getClass(), 0) - .withDescriptionContaining("type parameter 'SECOND' depending on") - - .from(ClassWithTypeParameters.class).to(List.class).inLocation(getClass(), 0) - .withDescriptionContaining("type parameter 'SECOND' depending on") - - .from(ClassWithTypeParameters.class).to(Set.class).inLocation(getClass(), 0) - .withDescriptionContaining("type parameter 'SECOND' depending on") - - .from(ClassWithTypeParameters.class).to(Iterable.class).inLocation(getClass(), 0) - .withDescriptionContaining("type parameter 'SECOND' depending on") - - .from(ClassWithTypeParameters.class).to(File.class).inLocation(getClass(), 0) - .withDescriptionContaining("type parameter 'SECOND' depending on") + .contain(from(ClassWithTypeParameters.class) + .withExpectedDescriptionTemplate("type parameter 'FIRST' depending on") + .to(List.class, Serializable.class, Comparable.class) + .inLocation(getClass(), 0) + + .from(ClassWithTypeParameters.class) + .withExpectedDescriptionTemplate("type parameter 'SECOND' depending on") + .to(Map.class, Map.Entry.class, String.class, BufferedInputStream[][].class, Serializable.class, List.class, Set.class, Iterable.class, File.class) + .inLocation(getClass(), 0) + + .from(ClassWithTypeParameters.class).to(BufferedInputStream.class).inLocation(getClass(), 0) + .withDescriptionContaining("depends on component type <%s>", BufferedInputStream.class.getName()) ); } @@ -639,7 +648,7 @@ public void direct_dependencies_from_self_finds_correct_set_of_target_types() { assertThatTypes(targets).matchInAnyOrder( B.class, AhavingMembersOfTypeB.class, Object.class, String.class, - List.class, Serializable.class, SomeSuperClass.class, + List.class, Serializable.class, SomeSuperclass.class, WithType.class, WithNestedAnnotations.class, OnClass.class, OnMethod.class, OnConstructor.class, OnField.class, MetaAnnotated.class, WithEnum.class, WithPrimitive.class, WithOtherEnum.class, WithOtherType.class, @@ -671,9 +680,9 @@ public void direct_dependencies_to_self_by_accesses() { @Test public void direct_dependencies_to_self_by_inheritance() { - JavaClass superClass = importClassesWithContext(SuperA.class, AExtendingSuperAImplementingInterfaceForA.class).get(SuperA.class); + JavaClass superclass = importClassesWithContext(SuperA.class, AExtendingSuperAImplementingInterfaceForA.class).get(SuperA.class); - assertThat(superClass.getDirectDependenciesToSelf()) + assertThat(superclass.getDirectDependenciesToSelf()) .areAtLeastOne(extendsDependency() .from(AExtendingSuperAImplementingInterfaceForA.class) .to(SuperA.class) @@ -689,6 +698,42 @@ public void direct_dependencies_to_self_by_inheritance() { .inLineNumber(0)); } + @Test + public void direct_dependencies_to_self_by_inheritance_through_generic_superclass_parameters() { + @SuppressWarnings("unused") + class FirstBase { + } + class FirstChild extends FirstBase< + Comparable, + Map< + Map.Entry>, + Map>>>>>>>> { + } + @SuppressWarnings("unused") + class SecondBase { + } + class SecondChild extends SecondBase> { + } + + JavaClasses javaClasses = importClassesWithContext(FirstChild.class, SecondChild.class, BufferedInputStream.class, File.class); + + assertThatDependencies(javaClasses.get(File.class).getDirectDependenciesToSelf()) + .contain(from(FirstChild.class).to(File.class).inLocation(getClass(), 0) + .withDescriptionContaining("Class <%s> has generic superclass <%s> with type argument depending on <%s>", + FirstChild.class.getName(), FirstBase.class.getName(), File.class.getName()) + + .from(SecondChild.class).to(File.class).inLocation(getClass(), 0) + .withDescriptionContaining("Class <%s> has generic superclass <%s> with type argument depending on <%s>", + SecondChild.class.getName(), SecondBase.class.getName(), File.class.getName()) + ); + + assertThatDependencies(javaClasses.get(BufferedInputStream.class).getDirectDependenciesToSelf()) + .contain(from(FirstChild.class).to(BufferedInputStream.class).inLocation(getClass(), 0) + .withDescriptionContaining("depends on component type <%s>", BufferedInputStream.class.getName()) + ); + } + @Test public void direct_dependencies_to_self_by_member_declarations() { JavaClass javaClass = importClassesWithContext(AhavingMembersOfTypeB.class, B.class).get(B.class); @@ -886,7 +931,7 @@ public void functions_get_members() { public void predicate_withType() { assertThat(type(Parent.class)) .accepts(importClassWithContext(Parent.class)) - .rejects(importClassWithContext(SuperClassWithFieldAndMethod.class)); + .rejects(importClassWithContext(SuperclassWithFieldAndMethod.class)); assertThat(type(System.class)).hasDescription("type java.lang.System"); } @@ -895,7 +940,7 @@ public void predicate_withType() { public void predicate_simpleName() { assertThat(simpleName(Parent.class.getSimpleName())) .accepts(importClassWithContext(Parent.class)) - .rejects(importClassWithContext(SuperClassWithFieldAndMethod.class)); + .rejects(importClassWithContext(SuperclassWithFieldAndMethod.class)); assertThat(simpleName("Simple")).hasDescription("simple name 'Simple'"); } @@ -962,17 +1007,17 @@ public void predicate_simpleNameEndingWith() { @Test public void predicate_assignableFrom() { - assertThatAssignable().from(SuperClassWithFieldAndMethod.class) - .to(SuperClassWithFieldAndMethod.class) + assertThatAssignable().from(SuperclassWithFieldAndMethod.class) + .to(SuperclassWithFieldAndMethod.class) .isTrue(); assertThatAssignable().from(ClassWithTwoFieldsAndTwoMethods.class) - .to(SuperClassWithFieldAndMethod.class) + .to(SuperclassWithFieldAndMethod.class) .isTrue(); - assertThatAssignable().from(SuperClassWithFieldAndMethod.class) + assertThatAssignable().from(SuperclassWithFieldAndMethod.class) .to(InterfaceWithMethod.class) .isTrue(); assertThatAssignable().from(ClassWithTwoFieldsAndTwoMethods.class) - .via(SuperClassWithFieldAndMethod.class) + .via(SuperclassWithFieldAndMethod.class) .to(InterfaceWithMethod.class) .isTrue(); assertThatAssignable().from(InterfaceWithMethod.class) @@ -981,14 +1026,14 @@ public void predicate_assignableFrom() { assertThatAssignable().from(Parent.class) .to(InterfaceWithMethod.class) .isFalse(); - assertThatAssignable().from(SuperClassWithFieldAndMethod.class) + assertThatAssignable().from(SuperclassWithFieldAndMethod.class) .to(Parent.class) .isTrue(); - assertThatAssignable().from(SuperClassWithFieldAndMethod.class) + assertThatAssignable().from(SuperclassWithFieldAndMethod.class) .to(ClassWithTwoFieldsAndTwoMethods.class) .isFalse(); assertThatAssignable().from(Parent.class) - .to(SuperClassWithFieldAndMethod.class) + .to(SuperclassWithFieldAndMethod.class) .isFalse(); assertThat(assignableFrom(System.class)).hasDescription("assignable from java.lang.System"); @@ -996,33 +1041,33 @@ public void predicate_assignableFrom() { @Test public void predicate_assignableTo() { - assertThatAssignable().to(SuperClassWithFieldAndMethod.class) - .from(SuperClassWithFieldAndMethod.class) + assertThatAssignable().to(SuperclassWithFieldAndMethod.class) + .from(SuperclassWithFieldAndMethod.class) .isTrue(); assertThatAssignable().to(ClassWithTwoFieldsAndTwoMethods.class) - .from(SuperClassWithFieldAndMethod.class) + .from(SuperclassWithFieldAndMethod.class) .isFalse(); assertThatAssignable().to(InterfaceWithMethod.class) .from(InterfaceWithMethod.class) .isTrue(); assertThatAssignable().to(InterfaceWithMethod.class) - .from(SuperClassWithFieldAndMethod.class) + .from(SuperclassWithFieldAndMethod.class) .isTrue(); assertThatAssignable().to(InterfaceWithMethod.class) - .via(SuperClassWithFieldAndMethod.class) + .via(SuperclassWithFieldAndMethod.class) .from(ClassWithTwoFieldsAndTwoMethods.class) .isTrue(); assertThatAssignable().to(InterfaceWithMethod.class) .from(Parent.class) .isFalse(); - assertThatAssignable().to(SuperClassWithFieldAndMethod.class) + assertThatAssignable().to(SuperclassWithFieldAndMethod.class) .from(Parent.class) .isFalse(); - assertThatAssignable().to(SuperClassWithFieldAndMethod.class) + assertThatAssignable().to(SuperclassWithFieldAndMethod.class) .from(ClassWithTwoFieldsAndTwoMethods.class) .isTrue(); assertThatAssignable().to(Parent.class) - .from(SuperClassWithFieldAndMethod.class) + .from(SuperclassWithFieldAndMethod.class) .isTrue(); assertThat(assignableTo(System.class)).hasDescription("assignable to java.lang.System"); @@ -1124,9 +1169,9 @@ public void predicate_reside_in_any_package() { @Test public void predicate_equivalentTo() { - JavaClass javaClass = importClasses(SuperClassWithFieldAndMethod.class, Parent.class).get(SuperClassWithFieldAndMethod.class); + JavaClass javaClass = importClasses(SuperclassWithFieldAndMethod.class, Parent.class).get(SuperclassWithFieldAndMethod.class); - assertThat(equivalentTo(SuperClassWithFieldAndMethod.class)) + assertThat(equivalentTo(SuperclassWithFieldAndMethod.class)) .accepts(javaClass); assertThat(equivalentTo(Parent.class)) .rejects(javaClass) @@ -1429,7 +1474,7 @@ public void isFalse() { } @SuppressWarnings("unused") - static class ClassWithTwoFieldsAndTwoMethods extends SuperClassWithFieldAndMethod { + static class ClassWithTwoFieldsAndTwoMethods extends SuperclassWithFieldAndMethod { String stringField; private int intField; @@ -1442,7 +1487,7 @@ protected String stringMethod() { } @SuppressWarnings("unused") - abstract static class SuperClassWithFieldAndMethod extends Parent implements InterfaceWithMethod { + abstract static class SuperclassWithFieldAndMethod extends Parent implements InterfaceWithMethod { private Object objectField; @Override @@ -1577,7 +1622,7 @@ private class NestedNamedInnerClass { } } - private static class SomeSuperClass { + private static class SomeSuperclass { } @OnClass @@ -1595,7 +1640,7 @@ private static class SomeSuperClass { ) @MetaAnnotated @SuppressWarnings("unused") - private static class ClassWithAnnotationDependencies extends SomeSuperClass { + private static class ClassWithAnnotationDependencies extends SomeSuperclass { @OnField(SomeEnumAsAnnotationParameter.ANNOTATION_PARAMETER) Object field; diff --git a/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaPackageTest.java b/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaPackageTest.java index 70dd924535..f574f72a42 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaPackageTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaPackageTest.java @@ -191,6 +191,9 @@ public void iterates_sub_packages() { JavaPackage java = defaultPackage.getPackage("java"); + assertThatPackages(java.getSubpackages()).containRelativeNames("lang", "util", "io", "security"); + assertThatPackages(java.getSubpackages()).containNames("java.lang", "java.util", "java.io", "java.security"); + assertThatPackages(java.getSubPackages()).containRelativeNames("lang", "util", "io", "security"); assertThatPackages(java.getSubPackages()).containNames("java.lang", "java.util", "java.io", "java.security"); } @@ -211,6 +214,9 @@ public void iterates_all_sub_packages() { JavaPackage java = defaultPackage.getPackage("java"); + assertThatPackages(java.getAllSubpackages()).containPackagesOf( + Object.class, Annotation.class, Collection.class, BlockingQueue.class, Security.class); + assertThatPackages(java.getAllSubPackages()).containPackagesOf( Object.class, Annotation.class, Collection.class, BlockingQueue.class, Security.class); } @@ -522,8 +528,8 @@ private JavaPackage importDefaultPackage(Class... classes) { return new ClassFileImporter().importClasses(classes).getDefaultPackage(); } - private JavaPackage importPackage(String subPackageName) { - String packageName = getClass().getPackage().getName() + "." + subPackageName; + private JavaPackage importPackage(String subpackageName) { + String packageName = getClass().getPackage().getName() + "." + subpackageName; JavaClasses classes = new ClassFileImporter().importPackages(packageName); return classes.getPackage(packageName); } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaTypeTest.java b/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaTypeTest.java new file mode 100644 index 0000000000..f355ffeb57 --- /dev/null +++ b/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaTypeTest.java @@ -0,0 +1,21 @@ +package com.tngtech.archunit.core.domain; + +import com.tngtech.archunit.core.importer.ClassFileImporter; +import org.junit.Test; + +import static com.tngtech.archunit.core.domain.JavaType.Functions.TO_ERASURE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class JavaTypeTest { + + @Test + public void function_TO_ERASURE() { + JavaType javaType = mock(JavaType.class); + JavaClass erasure = new ClassFileImporter().importClass(getClass()); + when(javaType.toErasure()).thenReturn(erasure); + + assertThat(TO_ERASURE.apply(javaType)).isEqualTo(erasure); + } +} diff --git a/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaTypeVariableTest.java b/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaTypeVariableTest.java index 1ce12894cc..88e8bd8c19 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaTypeVariableTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/domain/JavaTypeVariableTest.java @@ -9,7 +9,7 @@ import static com.tngtech.archunit.testutil.Assertions.assertThat; import static com.tngtech.archunit.testutil.Assertions.assertThatType; -import static com.tngtech.archunit.testutil.Assertions.assertThatTypes; +import static com.tngtech.archunit.testutil.Assertions.assertThatTypeErasuresOf; public class JavaTypeVariableTest { @@ -45,8 +45,8 @@ class ClassWithUnboundTypeParameter & Serializ JavaTypeVariable type = new ClassFileImporter().importClass(ClassWithUnboundTypeParameter.class).getTypeParameters().get(0); - assertThatTypes(type.getBounds()).matchExactly(HashMap.class, Serializable.class); - assertThatTypes(type.getUpperBounds()).matchExactly(HashMap.class, Serializable.class); + assertThatTypeErasuresOf(type.getBounds()).matchExactly(HashMap.class, Serializable.class); + assertThatTypeErasuresOf(type.getUpperBounds()).matchExactly(HashMap.class, Serializable.class); } @Test diff --git a/archunit/src/test/java/com/tngtech/archunit/core/domain/testobjects/ClassWithArrayDependencies.java b/archunit/src/test/java/com/tngtech/archunit/core/domain/testobjects/ClassWithArrayDependencies.java new file mode 100644 index 0000000000..9e0d913ac0 --- /dev/null +++ b/archunit/src/test/java/com/tngtech/archunit/core/domain/testobjects/ClassWithArrayDependencies.java @@ -0,0 +1,12 @@ +package com.tngtech.archunit.core.domain.testobjects; + +@SuppressWarnings("unused") +public class ClassWithArrayDependencies { + private void oneDimArray() { + new String[0].clone(); + } + + private void multiDimArray() { + new String[0][0].clone(); + } +} diff --git a/archunit/src/test/java/com/tngtech/archunit/core/domain/testobjects/InterfaceForA.java b/archunit/src/test/java/com/tngtech/archunit/core/domain/testobjects/InterfaceForA.java index a04ddf7e1d..eed23febde 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/domain/testobjects/InterfaceForA.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/domain/testobjects/InterfaceForA.java @@ -1,4 +1,4 @@ package com.tngtech.archunit.core.domain.testobjects; -public interface InterfaceForA extends SuperInterface { +public interface InterfaceForA extends Superinterface { } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/domain/testobjects/SuperInterface.java b/archunit/src/test/java/com/tngtech/archunit/core/domain/testobjects/Superinterface.java similarity index 62% rename from archunit/src/test/java/com/tngtech/archunit/core/domain/testobjects/SuperInterface.java rename to archunit/src/test/java/com/tngtech/archunit/core/domain/testobjects/Superinterface.java index 3a85718559..5cf8da74de 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/domain/testobjects/SuperInterface.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/domain/testobjects/Superinterface.java @@ -1,4 +1,4 @@ package com.tngtech.archunit.core.domain.testobjects; -public interface SuperInterface { +public interface Superinterface { } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterAnnotationsTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterAnnotationsTest.java index 3e38e19336..278cbba2c7 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterAnnotationsTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterAnnotationsTest.java @@ -66,7 +66,7 @@ public void imports_simple_annotation() { assertThat(javaClass.getSimpleName()).as("simple name").isEqualTo(AnnotationToImport.class.getSimpleName()); assertThat(javaClass.getPackageName()).as("package name").isEqualTo(AnnotationToImport.class.getPackage().getName()); assertThat(javaClass.getModifiers()).as("modifiers").containsOnly(JavaModifier.PUBLIC, JavaModifier.ABSTRACT); - assertThat(javaClass.getSuperClass()).as("super class").isAbsent(); + assertThat(javaClass.getRawSuperclass()).as("super class").isAbsent(); assertThatTypes(javaClass.getInterfaces()).as("interfaces").matchInAnyOrder(Annotation.class); assertThat(javaClass.isInterface()).as("is interface").isTrue(); assertThat(javaClass.isEnum()).as("is enum").isFalse(); diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericClassesTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericClassesTest.java index 7fd6dc6935..e02c19017a 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericClassesTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericClassesTest.java @@ -21,10 +21,11 @@ import static com.tngtech.archunit.testutil.Assertions.assertThat; import static com.tngtech.archunit.testutil.Assertions.assertThatType; -import static com.tngtech.archunit.testutil.assertion.JavaTypeVariableAssertion.ExpectedConcreteTypeVariable.typeVariable; -import static com.tngtech.archunit.testutil.assertion.JavaTypeVariableAssertion.ExpectedConcreteTypeVariableArray.typeVariableArray; -import static com.tngtech.archunit.testutil.assertion.JavaTypeVariableAssertion.ExpectedConcreteWildcardType.wildcardType; -import static com.tngtech.archunit.testutil.assertion.JavaTypeVariableAssertion.parameterizedType; +import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteClass.concreteClass; +import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteParameterizedType.parameterizedType; +import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteTypeVariable.typeVariable; +import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteTypeVariableArray.typeVariableArray; +import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteWildcardType.wildcardType; import static com.tngtech.java.junit.dataprovider.DataProviders.$; import static com.tngtech.java.junit.dataprovider.DataProviders.$$; @@ -492,7 +493,7 @@ class ClassWithComplexTypeParameters< .hasTypeParameter("A") .withBoundsMatching( parameterizedType(List.class).withWildcardTypeParameter(), - parameterizedType(Serializable.class), + concreteClass(Serializable.class), parameterizedType(Comparable.class).withTypeArguments(typeVariable("A"))) .hasTypeParameter("B") .withBoundsMatching(typeVariable("A")) @@ -502,7 +503,7 @@ class ClassWithComplexTypeParameters< parameterizedType(Map.Entry.class).withTypeArguments( typeVariable("A"), parameterizedType(Map.Entry.class).withTypeArguments( - parameterizedType(String.class), typeVariable("B"))), + concreteClass(String.class), typeVariable("B"))), parameterizedType(Map.class).withTypeArguments( wildcardType().withUpperBound(String.class), parameterizedType(Map.class).withTypeArguments( @@ -558,7 +559,7 @@ class ClassWithComplexTypeParametersWithConcreteArrayBounds< parameterizedType(Map.class).withTypeArguments( wildcardType().withLowerBound(String[][][].class), wildcardType()), - parameterizedType(Serializable[][].class))) + concreteClass(Serializable[][].class))) ); } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericSuperclassTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericSuperclassTest.java new file mode 100644 index 0000000000..f4bcbe4c16 --- /dev/null +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericSuperclassTest.java @@ -0,0 +1,423 @@ +package com.tngtech.archunit.core.importer; + +import java.io.File; +import java.io.Serializable; +import java.lang.ref.Reference; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.tngtech.archunit.core.domain.JavaType; +import com.tngtech.archunit.testutil.ArchConfigurationRule; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static com.tngtech.archunit.testutil.Assertions.assertThatType; +import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteClass.concreteClass; +import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteParameterizedType.parameterizedType; +import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteTypeVariable.typeVariable; +import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteWildcardType.wildcardType; + +@RunWith(DataProviderRunner.class) +public class ClassFileImporterGenericSuperclassTest { + + @Rule + public final ArchConfigurationRule configurationRule = new ArchConfigurationRule().resolveAdditionalDependenciesFromClassPath(false); + + @Test + public void imports_non_generic_superclass() { + class BaseClass { + } + class Child extends BaseClass { + } + + JavaType genericSuperClass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + + assertThatType(genericSuperClass).as("generic superclass").matches(BaseClass.class); + } + + @Test + public void imports_generic_superclass_with_one_type_argument() { + @SuppressWarnings("unused") + class BaseClass { + } + class Child extends BaseClass { + } + + JavaType genericSuperClass = new ClassFileImporter().importClasses(Child.class, String.class).get(Child.class).getSuperclass().get(); + + assertThatType(genericSuperClass).as("generic superclass") + .hasErasure(BaseClass.class) + .hasActualTypeArguments(String.class); + } + + @Test + public void imports_generic_superclass_with_multiple_type_arguments() { + @SuppressWarnings("unused") + class BaseClass { + } + @SuppressWarnings("unused") + class Child extends BaseClass { + } + + JavaType genericSuperClass = new ClassFileImporter().importClasses(Child.class, String.class, Serializable.class, File.class) + .get(Child.class).getSuperclass().get(); + + assertThatType(genericSuperClass).as("generic superclass") + .hasErasure(BaseClass.class) + .hasActualTypeArguments(String.class, Serializable.class, File.class); + } + + @Test + public void imports_generic_superclass_with_single_actual_type_argument_parameterized_with_concrete_class() { + @SuppressWarnings("unused") + class BaseClass { + } + class Child extends BaseClass> { + } + + JavaType genericSuperClass = new ClassFileImporter().importClasses(Child.class, ClassParameterWithSingleTypeParameter.class, String.class) + .get(Child.class).getSuperclass().get(); + + assertThatType(genericSuperClass).as("generic superclass").hasActualTypeArguments( + parameterizedType(ClassParameterWithSingleTypeParameter.class) + .withTypeArguments(String.class) + ); + } + + @Test + public void imports_generic_superclass_with_multiple_actual_type_arguments_parameterized_with_concrete_classes() { + @SuppressWarnings("unused") + class BaseClass { + } + class Child extends BaseClass< + ClassParameterWithSingleTypeParameter, + InterfaceParameterWithSingleTypeParameter, + InterfaceParameterWithSingleTypeParameter> { + } + + JavaType genericSuperClass = new ClassFileImporter() + .importClasses( + Child.class, ClassParameterWithSingleTypeParameter.class, InterfaceParameterWithSingleTypeParameter.class, + File.class, Serializable.class, String.class) + .get(Child.class).getSuperclass().get(); + + assertThatType(genericSuperClass).as("generic superclass").hasActualTypeArguments( + parameterizedType(ClassParameterWithSingleTypeParameter.class) + .withTypeArguments(File.class), + parameterizedType(InterfaceParameterWithSingleTypeParameter.class) + .withTypeArguments(Serializable.class), + parameterizedType(InterfaceParameterWithSingleTypeParameter.class) + .withTypeArguments(String.class) + ); + } + + @Test + public void imports_generic_superclass_with_single_actual_type_argument_parameterized_with_unbound_wildcard() { + @SuppressWarnings("unused") + class BaseClass { + } + class Child extends BaseClass> { + } + + JavaType genericSuperClass = new ClassFileImporter().importClasses(Child.class, ClassParameterWithSingleTypeParameter.class) + .get(Child.class).getSuperclass().get(); + + assertThatType(genericSuperClass).as("generic superclass").hasActualTypeArguments( + parameterizedType(ClassParameterWithSingleTypeParameter.class) + .withWildcardTypeParameter() + ); + } + + @Test + public void imports_generic_superclass_with_actual_type_arguments_parameterized_with_bounded_wildcards() { + @SuppressWarnings("unused") + class BaseClass { + } + class Child extends BaseClass< + ClassParameterWithSingleTypeParameter, + ClassParameterWithSingleTypeParameter> { + } + + JavaType genericSuperClass = new ClassFileImporter().importClasses(Child.class, ClassParameterWithSingleTypeParameter.class, String.class, File.class) + .get(Child.class).getSuperclass().get(); + + assertThatType(genericSuperClass).as("generic superclass").hasActualTypeArguments( + parameterizedType(ClassParameterWithSingleTypeParameter.class) + .withWildcardTypeParameterWithUpperBound(String.class), + parameterizedType(ClassParameterWithSingleTypeParameter.class) + .withWildcardTypeParameterWithLowerBound(File.class) + ); + } + + @Test + public void imports_generic_superclass_with_actual_type_arguments_with_multiple_wildcards_with_various_bounds() { + @SuppressWarnings("unused") + class BaseClass { + } + class Child extends BaseClass< + ClassParameterWithSingleTypeParameter>, + ClassParameterWithSingleTypeParameter>> { + } + + JavaType genericSuperClass = new ClassFileImporter() + .importClasses( + Child.class, ClassParameterWithSingleTypeParameter.class, + Map.class, Serializable.class, File.class, Reference.class, String.class) + .get(Child.class).getSuperclass().get(); + + assertThatType(genericSuperClass).as("generic superclass").hasActualTypeArguments( + parameterizedType(ClassParameterWithSingleTypeParameter.class) + .withTypeArguments(parameterizedType(Map.class) + .withWildcardTypeParameters( + wildcardType().withUpperBound(Serializable.class), + wildcardType().withLowerBound(File.class))), + parameterizedType(ClassParameterWithSingleTypeParameter.class) + .withTypeArguments(parameterizedType(Reference.class) + .withWildcardTypeParameterWithLowerBound(String.class)) + ); + } + + @Test + public void imports_generic_superclass_with_actual_type_argument_parameterized_with_type_variable() { + @SuppressWarnings("unused") + class BaseClass { + } + class Child extends BaseClass> { + } + + JavaType genericSuperClass = new ClassFileImporter().importClasses(Child.class, ClassParameterWithSingleTypeParameter.class) + .get(Child.class).getSuperclass().get(); + + assertThatType(genericSuperClass).as("generic superclass").hasActualTypeArguments( + parameterizedType(ClassParameterWithSingleTypeParameter.class) + .withTypeArguments(typeVariable("SUB")) + ); + } + + @Test + public void references_type_variable_assigned_to_actual_type_argument_of_generic_superclass() { + @SuppressWarnings("unused") + class BaseClass { + } + class Child extends BaseClass> { + } + + JavaType genericSuperClass = new ClassFileImporter().importClasses(Child.class, ClassParameterWithSingleTypeParameter.class, String.class) + .get(Child.class).getSuperclass().get(); + + assertThatType(genericSuperClass).as("generic superclass").hasActualTypeArguments( + parameterizedType(ClassParameterWithSingleTypeParameter.class) + .withTypeArguments(typeVariable("SUB").withUpperBounds(String.class)) + ); + } + + @Test + public void references_outer_type_variable_assigned_to_actual_type_argument_of_generic_superclass_of_inner_class() { + @SuppressWarnings("unused") + class OuterWithTypeParameter { + class SomeInner { + @SuppressWarnings("unused") + class BaseClass { + } + + class Child extends BaseClass { + } + } + } + + JavaType genericSuperClass = new ClassFileImporter() + .importClasses( + OuterWithTypeParameter.class, + OuterWithTypeParameter.SomeInner.class, + OuterWithTypeParameter.SomeInner.Child.class, + String.class) + .get(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); + + assertThatType(genericSuperClass).as("generic superclass").hasActualTypeArguments( + typeVariable("OUTER").withUpperBounds(String.class) + ); + } + + @Test + public void creates_new_stub_type_variables_for_type_variables_of_enclosing_classes_that_are_out_of_context_for_generic_superclass_of_inner_class() { + @SuppressWarnings("unused") + class OuterWithTypeParameter { + class SomeInner { + @SuppressWarnings("unused") + class BaseClass { + } + + class Child extends BaseClass { + } + } + } + + JavaType genericSuperClass = new ClassFileImporter() + .importClasses(OuterWithTypeParameter.SomeInner.Child.class, String.class) + .get(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); + + assertThatType(genericSuperClass).as("generic superclass").hasActualTypeArguments( + typeVariable("OUTER").withoutUpperBounds() + ); + } + + @Test + public void imports_wildcards_of_generic_superclass_bound_by_type_variables() { + @SuppressWarnings("unused") + class BaseClass { + } + + class Child extends BaseClass< + ClassParameterWithSingleTypeParameter, + ClassParameterWithSingleTypeParameter> { + } + + JavaType genericSuperClass = new ClassFileImporter() + .importClasses(Child.class, ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class) + .get(Child.class).getSuperclass().get(); + + assertThatType(genericSuperClass).as("generic superclass").hasActualTypeArguments( + parameterizedType(ClassParameterWithSingleTypeParameter.class) + .withWildcardTypeParameterWithUpperBound( + typeVariable("FIRST").withUpperBounds(String.class)), + parameterizedType(ClassParameterWithSingleTypeParameter.class) + .withWildcardTypeParameterWithLowerBound( + typeVariable("SECOND").withUpperBounds(Serializable.class)) + ); + } + + @Test + public void imports_wildcards_of_generic_superclass_bound_by_type_variables_of_enclosing_classes() { + @SuppressWarnings("unused") + class OuterWithTypeParameter { + class SomeInner { + @SuppressWarnings("unused") + class BaseClass { + } + + class Child extends BaseClass< + ClassParameterWithSingleTypeParameter, + ClassParameterWithSingleTypeParameter> { + } + } + } + + JavaType genericSuperClass = new ClassFileImporter() + .importClasses( + OuterWithTypeParameter.class, + OuterWithTypeParameter.SomeInner.class, + OuterWithTypeParameter.SomeInner.Child.class, + ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class) + .get(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); + + assertThatType(genericSuperClass).as("generic superclass").hasActualTypeArguments( + parameterizedType(ClassParameterWithSingleTypeParameter.class) + .withWildcardTypeParameterWithUpperBound( + typeVariable("OUTER_ONE").withUpperBounds(String.class)), + parameterizedType(ClassParameterWithSingleTypeParameter.class) + .withWildcardTypeParameterWithLowerBound( + typeVariable("OUTER_TWO").withUpperBounds(Serializable.class)) + ); + } + + @Test + public void creates_new_stub_type_variables_for_wildcards_bound_by_type_variables_of_enclosing_classes_that_are_out_of_context() { + @SuppressWarnings("unused") + class OuterWithTypeParameter { + class SomeInner { + @SuppressWarnings("unused") + class BaseClass { + } + + class Child extends BaseClass< + ClassParameterWithSingleTypeParameter, + ClassParameterWithSingleTypeParameter> { + } + } + } + + JavaType genericSuperClass = new ClassFileImporter() + .importClasses( + OuterWithTypeParameter.SomeInner.Child.class, + ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class) + .get(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); + + assertThatType(genericSuperClass).as("generic superclass").hasActualTypeArguments( + parameterizedType(ClassParameterWithSingleTypeParameter.class) + .withWildcardTypeParameterWithUpperBound( + typeVariable("OUTER_ONE").withoutUpperBounds()), + parameterizedType(ClassParameterWithSingleTypeParameter.class) + .withWildcardTypeParameterWithLowerBound( + typeVariable("OUTER_TWO").withoutUpperBounds()) + ); + } + + @Test + public void imports_complex_type_with_multiple_nested_actual_type_arguments_of_generic_superclass_with_self_referencing_type_definitions() { + @SuppressWarnings("unused") + class BaseClass { + } + + class Child extends BaseClass< + // assigned to BaseClass + List, + // assigned to BaseClass<_,B,_> + Map< + Map.Entry>, + Map>>>>>>>, + // assigned to BaseClass<_,_,C> + Comparable>> { + } + + JavaType genericSuperClass = new ClassFileImporter() + .importClasses(Child.class, String.class, Serializable.class, Cloneable.class, + List.class, Map.class, Map.Entry.class, Set.class, Iterable.class, Comparable.class) + .get(Child.class).getSuperclass().get(); + + // @formatter:off + assertThatType(genericSuperClass).as("generic superclass").hasActualTypeArguments( + // assigned to BaseClass + parameterizedType(List.class) + .withWildcardTypeParameterWithUpperBound( + typeVariable("FIRST").withUpperBounds(String.class, Serializable.class)), + // assigned to BaseClass<_,B,_> + parameterizedType(Map.class).withTypeArguments( + parameterizedType(Map.Entry.class).withTypeArguments( + typeVariable("FIRST").withUpperBounds(String.class, Serializable.class), + parameterizedType(Map.Entry.class).withTypeArguments( + concreteClass(String.class), + typeVariable("SECOND").withUpperBounds(Serializable.class, Cloneable.class))), + parameterizedType(Map.class).withTypeArguments( + wildcardType().withUpperBound(String.class), + parameterizedType(Map.class).withTypeArguments( + wildcardType().withUpperBound(Serializable.class), + parameterizedType(List.class).withTypeArguments( + parameterizedType(List.class).withTypeArguments( + wildcardType().withUpperBound( + parameterizedType(Set.class).withTypeArguments( + wildcardType().withLowerBound( + parameterizedType(Iterable.class).withTypeArguments( + wildcardType().withLowerBound( + parameterizedType(Map.class).withTypeArguments( + typeVariable("SECOND").withUpperBounds(Serializable.class, Cloneable.class), + wildcardType()))))))))))), + // assigned to BaseClass<_,_,C> + parameterizedType(Comparable.class).withTypeArguments( + parameterizedType(Child.class).withTypeArguments( + typeVariable("FIRST").withUpperBounds(String.class, Serializable.class), + typeVariable("SECOND").withUpperBounds(Serializable.class, Cloneable.class)))); + // @formatter:on + } + + @SuppressWarnings("unused") + public static class ClassParameterWithSingleTypeParameter { + } + + @SuppressWarnings("unused") + public interface InterfaceParameterWithSingleTypeParameter { + } +} diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterTest.java index 75f59c74a0..de795b987f 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterTest.java @@ -22,6 +22,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.Callable; import java.util.jar.JarFile; import com.google.common.base.Predicate; @@ -76,19 +77,19 @@ import com.tngtech.archunit.core.importer.testexamples.callimport.CallsOwnMethod; import com.tngtech.archunit.core.importer.testexamples.callimport.ExternalInterfaceMethodCall; import com.tngtech.archunit.core.importer.testexamples.callimport.ExternalOverriddenMethodCall; -import com.tngtech.archunit.core.importer.testexamples.callimport.ExternalSubTypeConstructorCall; +import com.tngtech.archunit.core.importer.testexamples.callimport.ExternalSubtypeConstructorCall; import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.BaseClass; import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.CollectionInterface; import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.GrandParentInterface; import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.OtherInterface; -import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.OtherSubClass; +import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.OtherSubclass; import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.ParentInterface; import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.SomeCollection; -import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.SubClass; -import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.SubInterface; -import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.SubSubClass; -import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.SubSubSubClass; -import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.SubSubSubSubClass; +import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.SubSubSubSubclass; +import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.SubSubSubclass; +import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.SubSubclass; +import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.Subclass; +import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.Subinterface; import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.YetAnotherInterface; import com.tngtech.archunit.core.importer.testexamples.complexexternal.ChildClass; import com.tngtech.archunit.core.importer.testexamples.complexexternal.ParentClass; @@ -119,12 +120,12 @@ import com.tngtech.archunit.core.importer.testexamples.fieldaccesstointerfaces.ParentInterfaceWithFields; import com.tngtech.archunit.core.importer.testexamples.fieldimport.ClassWithIntAndObjectFields; import com.tngtech.archunit.core.importer.testexamples.fieldimport.ClassWithStringField; -import com.tngtech.archunit.core.importer.testexamples.hierarchicalfieldaccess.AccessToSuperAndSubClassField; -import com.tngtech.archunit.core.importer.testexamples.hierarchicalfieldaccess.SubClassWithAccessedField; -import com.tngtech.archunit.core.importer.testexamples.hierarchicalfieldaccess.SuperClassWithAccessedField; -import com.tngtech.archunit.core.importer.testexamples.hierarchicalmethodcall.CallOfSuperAndSubClassMethod; -import com.tngtech.archunit.core.importer.testexamples.hierarchicalmethodcall.SubClassWithCalledMethod; -import com.tngtech.archunit.core.importer.testexamples.hierarchicalmethodcall.SuperClassWithCalledMethod; +import com.tngtech.archunit.core.importer.testexamples.hierarchicalfieldaccess.AccessToSuperAndSubclassField; +import com.tngtech.archunit.core.importer.testexamples.hierarchicalfieldaccess.SubclassWithAccessedField; +import com.tngtech.archunit.core.importer.testexamples.hierarchicalfieldaccess.SuperclassWithAccessedField; +import com.tngtech.archunit.core.importer.testexamples.hierarchicalmethodcall.CallOfSuperAndSubclassMethod; +import com.tngtech.archunit.core.importer.testexamples.hierarchicalmethodcall.SubclassWithCalledMethod; +import com.tngtech.archunit.core.importer.testexamples.hierarchicalmethodcall.SuperclassWithCalledMethod; import com.tngtech.archunit.core.importer.testexamples.innerclassimport.CalledClass; import com.tngtech.archunit.core.importer.testexamples.innerclassimport.ClassWithInnerClass; import com.tngtech.archunit.core.importer.testexamples.instanceofcheck.ChecksInstanceofInConstructor; @@ -133,7 +134,7 @@ import com.tngtech.archunit.core.importer.testexamples.instanceofcheck.InstanceofChecked; import com.tngtech.archunit.core.importer.testexamples.integration.ClassA; import com.tngtech.archunit.core.importer.testexamples.integration.ClassBDependingOnClassA; -import com.tngtech.archunit.core.importer.testexamples.integration.ClassCDependingOnClassB_SuperClassOfX; +import com.tngtech.archunit.core.importer.testexamples.integration.ClassCDependingOnClassB_SuperclassOfX; import com.tngtech.archunit.core.importer.testexamples.integration.ClassD; import com.tngtech.archunit.core.importer.testexamples.integration.ClassXDependingOnClassesABCD; import com.tngtech.archunit.core.importer.testexamples.integration.InterfaceOfClassX; @@ -249,7 +250,7 @@ public void imports_simple_class_details() throws Exception { assertThat(javaClass.getSimpleName()).as("simple name").isEqualTo(ClassToImportOne.class.getSimpleName()); assertThat(javaClass.getPackageName()).as("package name").isEqualTo(ClassToImportOne.class.getPackage().getName()); assertThat(javaClass.getModifiers()).as("modifiers").containsOnly(JavaModifier.PUBLIC); - assertThatType(javaClass.getSuperClass().get()).as("super class").matches(Object.class); + assertThatType(javaClass.getRawSuperclass().get()).as("super class").matches(Object.class); assertThat(javaClass.getInterfaces()).as("interfaces").isEmpty(); assertThat(javaClass.isInterface()).as("is interface").isFalse(); assertThat(javaClass.isEnum()).as("is enum").isFalse(); @@ -273,7 +274,7 @@ public void imports_simple_enum() throws Exception { assertThat(javaClass.getSimpleName()).as("simple name").isEqualTo(EnumToImport.class.getSimpleName()); assertThat(javaClass.getPackageName()).as("package name").isEqualTo(EnumToImport.class.getPackage().getName()); assertThat(javaClass.getModifiers()).as("modifiers").containsOnly(JavaModifier.PUBLIC, JavaModifier.FINAL); - assertThatType(javaClass.getSuperClass().get()).as("super class").matches(Enum.class); + assertThatType(javaClass.getRawSuperclass().get()).as("super class").matches(Enum.class); assertThat(javaClass.getInterfaces()).as("interfaces").isEmpty(); assertThatTypes(javaClass.getAllInterfaces()).matchInAnyOrder(Enum.class.getInterfaces()); assertThat(javaClass.isInterface()).as("is interface").isFalse(); @@ -372,7 +373,7 @@ public void imports_interfaces() throws Exception { assertThat(simpleInterface.getName()).as("full name").isEqualTo(InterfaceToImport.class.getName()); assertThat(simpleInterface.getSimpleName()).as("simple name").isEqualTo(InterfaceToImport.class.getSimpleName()); assertThat(simpleInterface.getPackageName()).as("package name").isEqualTo(InterfaceToImport.class.getPackage().getName()); - assertThat(simpleInterface.getSuperClass()).as("super class").isAbsent(); + assertThat(simpleInterface.getRawSuperclass()).as("super class").isAbsent(); assertThat(simpleInterface.getInterfaces()).as("interfaces").isEmpty(); assertThat(simpleInterface.isInterface()).as("is interface").isTrue(); assertThat(simpleInterface.isEnum()).as("is enum").isFalse(); @@ -681,31 +682,34 @@ public void imports_base_class_in_class_hierarchy_correctly() throws Exception { } @Test - public void imports_sub_class_in_class_hierarchy_correctly() throws Exception { - JavaClass subClass = classesIn("testexamples/classhierarchyimport").get(SubClass.class); + public void imports_subclass_in_class_hierarchy_correctly() throws Exception { + JavaClass subclass = classesIn("testexamples/classhierarchyimport").get(Subclass.class); - assertThat(subClass.getConstructors()).hasSize(3); - assertThat(subClass.getFields()).hasSize(1); - assertThat(subClass.getMethods()).hasSize(3); - assertThat(subClass.getStaticInitializer().get().getMethodCallsFromSelf().size()).isGreaterThan(0); + assertThat(subclass.getConstructors()).hasSize(3); + assertThat(subclass.getFields()).hasSize(1); + assertThat(subclass.getMethods()).hasSize(3); + assertThat(subclass.getStaticInitializer().get().getMethodCallsFromSelf().size()).isGreaterThan(0); } @Test - public void creates_relations_between_super_and_sub_classes() throws Exception { + public void creates_relations_between_super_and_subclasses() throws Exception { ImportedClasses classes = classesIn("testexamples/classhierarchyimport"); JavaClass baseClass = classes.get(BaseClass.class); - JavaClass subClass = classes.get(SubClass.class); - JavaClass otherSubClass = classes.get(OtherSubClass.class); - JavaClass subSubClass = classes.get(SubSubClass.class); - JavaClass subSubSubClass = classes.get(SubSubSubClass.class); - JavaClass subSubSubSubClass = classes.get(SubSubSubSubClass.class); + JavaClass subclass = classes.get(Subclass.class); + JavaClass otherSubclass = classes.get(OtherSubclass.class); + JavaClass subSubclass = classes.get(SubSubclass.class); + JavaClass subSubSubclass = classes.get(SubSubSubclass.class); + JavaClass subSubSubSubclass = classes.get(SubSubSubSubclass.class); - assertThat(baseClass.getSuperClass().get().reflect()).isEqualTo(Object.class); - assertThat(baseClass.getSubClasses()).containsOnly(subClass, otherSubClass); - assertThat(baseClass.getAllSubClasses()).containsOnly(subClass, otherSubClass, subSubClass, subSubSubClass, subSubSubSubClass); - assertThat(subClass.getSuperClass()).contains(baseClass); - assertThat(subClass.getAllSubClasses()).containsOnly(subSubClass, subSubSubClass, subSubSubSubClass); - assertThat(subSubClass.getSuperClass()).contains(subClass); + assertThat(baseClass.getRawSuperclass().get().reflect()).isEqualTo(Object.class); + assertThat(baseClass.getSubclasses()).containsOnly(subclass, otherSubclass); + assertThat(baseClass.getSubclasses()).containsOnly(subclass, otherSubclass); + assertThat(baseClass.getAllSubclasses()).containsOnly(subclass, otherSubclass, subSubclass, subSubSubclass, subSubSubSubclass); + assertThat(baseClass.getAllSubclasses()).containsOnly(subclass, otherSubclass, subSubclass, subSubSubclass, subSubSubSubclass); + assertThat(subclass.getRawSuperclass()).contains(baseClass); + assertThat(subclass.getRawSuperclass()).contains(baseClass); + assertThat(subclass.getAllSubclasses()).containsOnly(subSubclass, subSubSubclass, subSubSubSubclass); + assertThat(subSubclass.getRawSuperclass()).contains(subclass); } @Test @@ -713,9 +717,9 @@ public void creates_relations_between_classes_and_interfaces() throws Exception ImportedClasses classes = classesIn("testexamples/classhierarchyimport"); JavaClass baseClass = classes.get(BaseClass.class); JavaClass otherInterface = classes.get(OtherInterface.class); - JavaClass subClass = classes.get(SubClass.class); - JavaClass subInterface = classes.get(SubInterface.class); - JavaClass otherSubClass = classes.get(OtherSubClass.class); + JavaClass subclass = classes.get(Subclass.class); + JavaClass subinterface = classes.get(Subinterface.class); + JavaClass otherSubclass = classes.get(OtherSubclass.class); JavaClass parentInterface = classes.get(ParentInterface.class); JavaClass grandParentInterface = classes.get(GrandParentInterface.class); JavaClass someCollection = classes.get(SomeCollection.class); @@ -723,21 +727,21 @@ public void creates_relations_between_classes_and_interfaces() throws Exception assertThat(baseClass.getInterfaces()).containsOnly(otherInterface); assertThat(baseClass.getAllInterfaces()).containsOnly(otherInterface, grandParentInterface); - assertThat(subClass.getInterfaces()).containsOnly(subInterface); - assertThat(subClass.getAllInterfaces()).containsOnly( - subInterface, otherInterface, parentInterface, grandParentInterface); - assertThat(otherSubClass.getInterfaces()).containsOnly(parentInterface); - assertThat(otherSubClass.getAllInterfaces()).containsOnly(parentInterface, grandParentInterface, otherInterface); - assertThat(someCollection.getInterfaces()).containsOnly(collectionInterface, otherInterface, subInterface); + assertThat(subclass.getInterfaces()).containsOnly(subinterface); + assertThat(subclass.getAllInterfaces()).containsOnly( + subinterface, otherInterface, parentInterface, grandParentInterface); + assertThat(otherSubclass.getInterfaces()).containsOnly(parentInterface); + assertThat(otherSubclass.getAllInterfaces()).containsOnly(parentInterface, grandParentInterface, otherInterface); + assertThat(someCollection.getInterfaces()).containsOnly(collectionInterface, otherInterface, subinterface); assertThat(someCollection.getAllInterfaces()).extractingResultOf("reflect").containsOnly( - CollectionInterface.class, OtherInterface.class, SubInterface.class, ParentInterface.class, + CollectionInterface.class, OtherInterface.class, Subinterface.class, ParentInterface.class, GrandParentInterface.class, Collection.class, Iterable.class); } @Test public void creates_relations_between_interfaces_and_interfaces() throws Exception { ImportedClasses classes = classesIn("testexamples/classhierarchyimport"); - JavaClass subInterface = classes.get(SubInterface.class); + JavaClass subinterface = classes.get(Subinterface.class); JavaClass parentInterface = classes.get(ParentInterface.class); JavaClass grandParentInterface = classes.get(GrandParentInterface.class); JavaClass collectionInterface = classes.get(CollectionInterface.class); @@ -745,54 +749,54 @@ public void creates_relations_between_interfaces_and_interfaces() throws Excepti assertThat(grandParentInterface.getAllInterfaces()).isEmpty(); assertThat(parentInterface.getInterfaces()).containsOnly(grandParentInterface); assertThat(parentInterface.getAllInterfaces()).containsOnly(grandParentInterface); - assertThat(subInterface.getInterfaces()).containsOnly(parentInterface); - assertThat(subInterface.getAllInterfaces()).containsOnly(parentInterface, grandParentInterface); + assertThat(subinterface.getInterfaces()).containsOnly(parentInterface); + assertThat(subinterface.getAllInterfaces()).containsOnly(parentInterface, grandParentInterface); assertThat(collectionInterface.getInterfaces()).extractingResultOf("reflect").containsOnly(Collection.class); } @Test - public void creates_relations_between_interfaces_and_sub_classes() throws Exception { + public void creates_relations_between_interfaces_and_subclasses() throws Exception { ImportedClasses classes = classesIn("testexamples/classhierarchyimport"); JavaClass baseClass = classes.get(BaseClass.class); JavaClass otherInterface = classes.get(OtherInterface.class); - JavaClass subClass = classes.get(SubClass.class); - JavaClass subSubClass = classes.get(SubSubClass.class); - JavaClass subSubSubClass = classes.get(SubSubSubClass.class); - JavaClass subSubSubSubClass = classes.get(SubSubSubSubClass.class); - JavaClass subInterface = classes.get(SubInterface.class); - JavaClass otherSubClass = classes.get(OtherSubClass.class); + JavaClass subclass = classes.get(Subclass.class); + JavaClass subSubclass = classes.get(SubSubclass.class); + JavaClass subSubSubclass = classes.get(SubSubSubclass.class); + JavaClass subSubSubSubclass = classes.get(SubSubSubSubclass.class); + JavaClass subinterface = classes.get(Subinterface.class); + JavaClass otherSubclass = classes.get(OtherSubclass.class); JavaClass parentInterface = classes.get(ParentInterface.class); JavaClass grandParentInterface = classes.get(GrandParentInterface.class); JavaClass someCollection = classes.get(SomeCollection.class); JavaClass collectionInterface = classes.get(CollectionInterface.class); - assertThat(grandParentInterface.getSubClasses()).containsOnly(parentInterface, otherInterface); - assertThat(grandParentInterface.getAllSubClasses()).containsOnly( - parentInterface, subInterface, otherInterface, - baseClass, subClass, otherSubClass, subSubClass, subSubSubClass, subSubSubSubClass, someCollection + assertThat(grandParentInterface.getSubclasses()).containsOnly(parentInterface, otherInterface); + assertThat(grandParentInterface.getAllSubclasses()).containsOnly( + parentInterface, subinterface, otherInterface, + baseClass, subclass, otherSubclass, subSubclass, subSubSubclass, subSubSubSubclass, someCollection ); - assertThat(parentInterface.getSubClasses()).containsOnly(subInterface, otherSubClass); - assertThat(parentInterface.getAllSubClasses()).containsOnly( - subInterface, subClass, subSubClass, subSubSubClass, subSubSubSubClass, someCollection, otherSubClass); + assertThat(parentInterface.getSubclasses()).containsOnly(subinterface, otherSubclass); + assertThat(parentInterface.getAllSubclasses()).containsOnly( + subinterface, subclass, subSubclass, subSubSubclass, subSubSubSubclass, someCollection, otherSubclass); JavaClass collection = getOnlyElement(collectionInterface.getInterfaces()); - assertThat(collection.getAllSubClasses()).containsOnly(collectionInterface, someCollection); + assertThat(collection.getAllSubclasses()).containsOnly(collectionInterface, someCollection); } @Test public void creates_superclass_and_interface_relations_missing_from_context() { - JavaClass javaClass = new ClassFileImporter().importClass(SubSubSubSubClass.class); + JavaClass javaClass = new ClassFileImporter().importClass(SubSubSubSubclass.class); - assertThat(javaClass.getAllSuperClasses()).extracting("name") + assertThat(javaClass.getAllRawSuperclasses()).extracting("name") .containsExactly( - SubSubSubClass.class.getName(), - SubSubClass.class.getName(), - SubClass.class.getName(), + SubSubSubclass.class.getName(), + SubSubclass.class.getName(), + Subclass.class.getName(), BaseClass.class.getName(), Object.class.getName()); assertThat(javaClass.getAllInterfaces()).extracting("name") .containsOnly( - SubInterface.class.getName(), + Subinterface.class.getName(), YetAnotherInterface.class.getName(), ParentInterface.class.getName(), GrandParentInterface.class.getName(), @@ -826,10 +830,10 @@ public void imports_enclosing_classes() throws Exception { public void imports_overridden_methods_correctly() throws Exception { ImportedClasses classes = classesIn("testexamples/classhierarchyimport"); JavaClass baseClass = classes.get(BaseClass.class); - JavaClass subClass = classes.get(SubClass.class); + JavaClass subclass = classes.get(Subclass.class); assertThat(baseClass.getCodeUnitWithParameterTypes("getSomeField").getModifiers()).containsOnly(PROTECTED); - assertThat(subClass.getCodeUnitWithParameterTypes("getSomeField").getModifiers()).containsOnly(PUBLIC); + assertThat(subclass.getCodeUnitWithParameterTypes("getSomeField").getModifiers()).containsOnly(PUBLIC); } @Test @@ -1059,27 +1063,27 @@ public void imports_external_field_access_with_shadowed_field() throws Exception @Test public void imports_shadowed_and_superclass_field_access() throws Exception { ImportedClasses classes = classesIn("testexamples/hierarchicalfieldaccess"); - JavaClass classThatAccessesFieldOfSuperClass = classes.get(AccessToSuperAndSubClassField.class); - JavaClass superClassWithAccessedField = classes.get(SuperClassWithAccessedField.class); - JavaClass subClassWithAccessedField = classes.get(SubClassWithAccessedField.class); + JavaClass classThatAccessesFieldOfSuperclass = classes.get(AccessToSuperAndSubclassField.class); + JavaClass superclassWithAccessedField = classes.get(SuperclassWithAccessedField.class); + JavaClass subclassWithAccessedField = classes.get(SubclassWithAccessedField.class); - Set accesses = classThatAccessesFieldOfSuperClass.getFieldAccessesFromSelf(); + Set accesses = classThatAccessesFieldOfSuperclass.getFieldAccessesFromSelf(); assertThat(accesses).hasSize(2); - JavaField field = superClassWithAccessedField.getField("field"); - FieldAccessTarget expectedSuperClassFieldAccess = new FieldAccessTargetBuilder() - .withOwner(subClassWithAccessedField) + JavaField field = superclassWithAccessedField.getField("field"); + FieldAccessTarget expectedSuperclassFieldAccess = new FieldAccessTargetBuilder() + .withOwner(subclassWithAccessedField) .withName(field.getName()) .withType(field.getRawType()) .withField(Suppliers.ofInstance(Optional.of(field))) .build(); assertThatAccess(getOnly(accesses, "field", GET)) - .isFrom("accessSuperClassField") - .isTo(expectedSuperClassFieldAccess) + .isFrom("accessSuperclassField") + .isTo(expectedSuperclassFieldAccess) .inLineNumber(5); assertThatAccess(getOnly(accesses, "maskedField", GET)) - .isFrom("accessSubClassField") - .isTo(subClassWithAccessedField.getField("maskedField")) + .isFrom("accessSubclassField") + .isTo(subclassWithAccessedField.getField("maskedField")) .inLineNumber(9); } @@ -1105,35 +1109,35 @@ public void imports_field_accesses_to_fields_from_interfaces() throws Exception @Test public void imports_shadowed_and_superclass_method_calls() throws Exception { ImportedClasses classes = classesIn("testexamples/hierarchicalmethodcall"); - JavaClass classThatCallsMethodOfSuperClass = classes.get(CallOfSuperAndSubClassMethod.class); - JavaClass superClassWithCalledMethod = classes.get(SuperClassWithCalledMethod.class); - JavaClass subClassWithCalledMethod = classes.get(SubClassWithCalledMethod.class); + JavaClass classThatCallsMethodOfSuperclass = classes.get(CallOfSuperAndSubclassMethod.class); + JavaClass superclassWithCalledMethod = classes.get(SuperclassWithCalledMethod.class); + JavaClass subclassWithCalledMethod = classes.get(SubclassWithCalledMethod.class); - Set calls = classThatCallsMethodOfSuperClass.getMethodCallsFromSelf(); + Set calls = classThatCallsMethodOfSuperclass.getMethodCallsFromSelf(); assertThat(calls).hasSize(2); - JavaCodeUnit callSuperClassMethod = classThatCallsMethodOfSuperClass - .getCodeUnitWithParameterTypes(CallOfSuperAndSubClassMethod.callSuperClassMethod); - JavaMethod expectedSuperClassMethod = superClassWithCalledMethod.getMethod(SuperClassWithCalledMethod.method); - MethodCallTarget expectedSuperClassCall = new MethodCallTargetBuilder() - .withOwner(subClassWithCalledMethod) - .withName(expectedSuperClassMethod.getName()) - .withParameters(expectedSuperClassMethod.getRawParameterTypes()) - .withReturnType(expectedSuperClassMethod.getRawReturnType()) - .withMethods(Suppliers.ofInstance(Collections.singleton(expectedSuperClassMethod))) + JavaCodeUnit callSuperclassMethod = classThatCallsMethodOfSuperclass + .getCodeUnitWithParameterTypes(CallOfSuperAndSubclassMethod.callSuperclassMethod); + JavaMethod expectedSuperclassMethod = superclassWithCalledMethod.getMethod(SuperclassWithCalledMethod.method); + MethodCallTarget expectedSuperclassCall = new MethodCallTargetBuilder() + .withOwner(subclassWithCalledMethod) + .withName(expectedSuperclassMethod.getName()) + .withParameters(expectedSuperclassMethod.getRawParameterTypes()) + .withReturnType(expectedSuperclassMethod.getRawReturnType()) + .withMethods(Suppliers.ofInstance(Collections.singleton(expectedSuperclassMethod))) .build(); - assertThatCall(getOnlyByCaller(calls, callSuperClassMethod)) - .isFrom(callSuperClassMethod) - .isTo(expectedSuperClassCall) - .inLineNumber(CallOfSuperAndSubClassMethod.callSuperClassLineNumber); + assertThatCall(getOnlyByCaller(calls, callSuperclassMethod)) + .isFrom(callSuperclassMethod) + .isTo(expectedSuperclassCall) + .inLineNumber(CallOfSuperAndSubclassMethod.callSuperclassLineNumber); - JavaCodeUnit callSubClassMethod = classThatCallsMethodOfSuperClass - .getCodeUnitWithParameterTypes(CallOfSuperAndSubClassMethod.callSubClassMethod); - assertThatCall(getOnlyByCaller(calls, callSubClassMethod)) - .isFrom(callSubClassMethod) - .isTo(subClassWithCalledMethod.getMethod(SubClassWithCalledMethod.maskedMethod)) - .inLineNumber(CallOfSuperAndSubClassMethod.callSubClassLineNumber); + JavaCodeUnit callSubclassMethod = classThatCallsMethodOfSuperclass + .getCodeUnitWithParameterTypes(CallOfSuperAndSubclassMethod.callSubclassMethod); + assertThatCall(getOnlyByCaller(calls, callSubclassMethod)) + .isFrom(callSubclassMethod) + .isTo(subclassWithCalledMethod.getMethod(SubclassWithCalledMethod.maskedMethod)) + .inLineNumber(CallOfSuperAndSubclassMethod.callSubclassLineNumber); } @Test @@ -1208,7 +1212,7 @@ public void imports_constructor_calls_on_external_class() throws Exception { @Test public void imports_constructor_calls_to_sub_type_constructor_on_external_class() throws Exception { - JavaClass classWithExternalConstructorCall = classesIn("testexamples/callimport").get(ExternalSubTypeConstructorCall.class); + JavaClass classWithExternalConstructorCall = classesIn("testexamples/callimport").get(ExternalSubtypeConstructorCall.class); assertConstructorCall(classWithExternalConstructorCall.getCodeUnitWithParameterTypes("call"), ChildClass.class, 9); assertConstructorCall(classWithExternalConstructorCall.getCodeUnitWithParameterTypes("newHashMap"), HashMap.class, 13); @@ -1324,7 +1328,7 @@ public void dependency_target_classes_are_derived_correctly() throws Exception { Set expectedTargetClasses = ImmutableSet.of( classes.get(ClassA.class), classes.get(ClassBDependingOnClassA.class), - classes.get(ClassCDependingOnClassB_SuperClassOfX.class), + classes.get(ClassCDependingOnClassB_SuperclassOfX.class), classes.get(ClassD.class), classes.get(InterfaceOfClassX.class) ); @@ -1340,7 +1344,7 @@ public void dependency_target_classes_are_derived_correctly() throws Exception { @Test public void getDirectDependencies_does_not_return_transitive_dependencies() throws Exception { ImportedClasses classes = classesIn("testexamples/integration"); - JavaClass javaClass = classes.get(ClassCDependingOnClassB_SuperClassOfX.class); + JavaClass javaClass = classes.get(ClassCDependingOnClassB_SuperclassOfX.class); JavaClass expectedTargetClass = classes.get(ClassBDependingOnClassA.class); Set targetClasses = new HashSet<>(); @@ -1706,12 +1710,12 @@ public void resolve_missing_dependencies_from_classpath_can_be_toogled() throws ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(true); JavaClass clazz = classesIn("testexamples/simpleimport").get(ClassToImportOne.class); - assertThat(clazz.getSuperClass().get().getMethods()).isNotEmpty(); + assertThat(clazz.getRawSuperclass().get().getMethods()).isNotEmpty(); ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); clazz = classesIn("testexamples/simpleimport").get(ClassToImportOne.class); - assertThat(clazz.getSuperClass().get().getMethods()).isEmpty(); + assertThat(clazz.getRawSuperclass().get().getMethods()).isEmpty(); } @DataProvider @@ -1722,18 +1726,24 @@ class Element { class DependsOnArray { Element[] array; } - ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(true); - JavaClass resolvedFromClasspath = new ClassFileImporter().importClasses(DependsOnArray.class) - .get(DependsOnArray.class).getField("array").getRawType().getComponentType(); - - ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - JavaClass stub = new ClassFileImporter().importClasses(DependsOnArray.class) - .get(DependsOnArray.class).getField("array").getRawType().getComponentType(); - return $$( - $("Resolved from classpath", resolvedFromClasspath), - $("Stub class", stub) - ); + return ArchConfigurationRule.resetConfigurationAround(new Callable() { + @Override + public Object[][] call() { + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(true); + JavaClass resolvedFromClasspath = new ClassFileImporter().importClasses(DependsOnArray.class) + .get(DependsOnArray.class).getField("array").getRawType().getComponentType(); + + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); + JavaClass stub = new ClassFileImporter().importClasses(DependsOnArray.class) + .get(DependsOnArray.class).getField("array").getRawType().getComponentType(); + + return $$( + $("Resolved from classpath", resolvedFromClasspath), + $("Stub class", stub) + ); + } + }); } @Test diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ImportTestUtils.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ImportTestUtils.java index 09a535b671..c9fecf109c 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ImportTestUtils.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ImportTestUtils.java @@ -38,6 +38,7 @@ import com.tngtech.archunit.core.domain.JavaMethodCall; import com.tngtech.archunit.core.domain.JavaModifier; import com.tngtech.archunit.core.domain.JavaStaticInitializer; +import com.tngtech.archunit.core.domain.JavaType; import com.tngtech.archunit.core.domain.JavaTypeVariable; import com.tngtech.archunit.core.importer.DomainBuilders.JavaMethodCallBuilder; import org.objectweb.asm.Type; @@ -332,7 +333,12 @@ private JavaClass importNew(Class owner) { private static class ImportContextStub implements ImportContext { @Override - public Optional createSuperClass(JavaClass owner) { + public Optional createSuperclass(JavaClass owner) { + return Optional.absent(); + } + + @Override + public Optional createGenericSuperclass(JavaClass owner) { return Optional.absent(); } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/IndependentClasspathRule.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/IndependentClasspathRule.java index dd9779abfa..f86303de9d 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/IndependentClasspathRule.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/IndependentClasspathRule.java @@ -21,7 +21,7 @@ class IndependentClasspathRule extends ExternalResource { private final SystemPropertiesRule systemPropertiesRule = new SystemPropertiesRule(); @Override - protected void before() throws Throwable { + protected void before() { systemPropertiesRule.before(); } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/UrlSourceTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/UrlSourceTest.java index 8be6c2ba1d..b7e2df85aa 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/UrlSourceTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/UrlSourceTest.java @@ -125,11 +125,11 @@ public void handles_jar_uri_with_spaces() throws Exception { @Test public void recursively_resolves_classpath_attributes_in_manifests() throws Exception { File folder = temporaryFolder.newFolder(); - WrittenJarFile grandChildOne = writeJarWithManifestClasspathAttribute(folder, subPath("grandchild", "one")); - WrittenJarFile grandChildTwo = writeJarWithManifestClasspathAttribute(folder, subPath("grandchild", "two")); - WrittenJarFile grandChildThree = writeJarWithManifestClasspathAttribute(folder, subPath("grandchild", "three")); - WrittenJarFile childOne = writeJarWithManifestClasspathAttribute(folder, subPath("child", "one"), grandChildOne.getPathAsAbsoluteUrl(), ManifestClasspathEntry.relativeUrl(grandChildTwo.path)); - WrittenJarFile childTwo = writeJarWithManifestClasspathAttribute(folder, subPath("child", "two"), ManifestClasspathEntry.absoluteUrl(grandChildThree.path)); + WrittenJarFile grandChildOne = writeJarWithManifestClasspathAttribute(folder, subpath("grandchild", "one")); + WrittenJarFile grandChildTwo = writeJarWithManifestClasspathAttribute(folder, subpath("grandchild", "two")); + WrittenJarFile grandChildThree = writeJarWithManifestClasspathAttribute(folder, subpath("grandchild", "three")); + WrittenJarFile childOne = writeJarWithManifestClasspathAttribute(folder, subpath("child", "one"), grandChildOne.getPathAsAbsoluteUrl(), ManifestClasspathEntry.relativeUrl(grandChildTwo.path)); + WrittenJarFile childTwo = writeJarWithManifestClasspathAttribute(folder, subpath("child", "two"), ManifestClasspathEntry.absoluteUrl(grandChildThree.path)); WrittenJarFile parent = writeJarWithManifestClasspathAttribute(folder, "parent", ManifestClasspathEntry.relativePath(childOne.path), ManifestClasspathEntry.absoluteUrl(childTwo.path)); System.setProperty(JAVA_CLASS_PATH_PROP, parent.path.toString()); @@ -163,7 +163,7 @@ public void terminates_recursively_resolving_manifest_classpaths_if_manifests_ha assertThat(urls).containsOnly(toUrl(Paths.get(jarOne.getName())), toUrl(Paths.get(jarTwo.getName()))); } - private String subPath(String... parts) { + private String subpath(String... parts) { return Joiner.on(File.separator).join(parts); } @@ -188,7 +188,7 @@ private Set createManifestClasspathEntries(String infix) Set result = new HashSet<>(); for (int i = 0; i < 10; i++) { result.add(ManifestClasspathEntry - .absolutePath(Paths.get(File.separator + subPath("some", "path", "parent", infix + i, "")).toAbsolutePath())); + .absolutePath(Paths.get(File.separator + subpath("some", "path", "parent", infix + i, "")).toAbsolutePath())); } return result; } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/resolvers/SelectedClassResolverFromClasspathTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/resolvers/SelectedClassResolverFromClasspathTest.java index 9b1ae51f5b..8863700bd1 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/resolvers/SelectedClassResolverFromClasspathTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/resolvers/SelectedClassResolverFromClasspathTest.java @@ -68,11 +68,11 @@ public boolean matches(URI argument) { }); } - private ImmutableList packages(String... subPackages) { + private ImmutableList packages(String... subpackages) { ImmutableList.Builder result = ImmutableList.builder(); - for (String pkg : subPackages) { + for (String pkg : subpackages) { result.add(getClass().getPackage().getName() + ".testclasses." + pkg); } return result.build(); } -} \ No newline at end of file +} diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/callimport/ExternalSubTypeConstructorCall.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/callimport/ExternalSubtypeConstructorCall.java similarity index 87% rename from archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/callimport/ExternalSubTypeConstructorCall.java rename to archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/callimport/ExternalSubtypeConstructorCall.java index ede53b5ce7..3a55b09b22 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/callimport/ExternalSubTypeConstructorCall.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/callimport/ExternalSubtypeConstructorCall.java @@ -4,7 +4,7 @@ import com.tngtech.archunit.core.importer.testexamples.complexexternal.ChildClass; -public class ExternalSubTypeConstructorCall { +public class ExternalSubtypeConstructorCall { void call() { new ChildClass(); } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/OtherSubClass.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/OtherSubclass.java similarity index 78% rename from archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/OtherSubClass.java rename to archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/OtherSubclass.java index 269a8b1874..c0a43a70d2 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/OtherSubClass.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/OtherSubclass.java @@ -1,6 +1,6 @@ package com.tngtech.archunit.core.importer.testexamples.classhierarchyimport; -public class OtherSubClass extends BaseClass implements ParentInterface { +public class OtherSubclass extends BaseClass implements ParentInterface { private int foo = 5; void soSthOtherSub() { diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SomeCollection.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SomeCollection.java index 8424ce8ed4..c313b97a8e 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SomeCollection.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SomeCollection.java @@ -1,4 +1,4 @@ package com.tngtech.archunit.core.importer.testexamples.classhierarchyimport; -public abstract class SomeCollection implements CollectionInterface, OtherInterface, SubInterface { +public abstract class SomeCollection implements CollectionInterface, OtherInterface, Subinterface { } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubSubSubClass.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubSubSubclass.java similarity index 59% rename from archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubSubSubClass.java rename to archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubSubSubclass.java index 1ac7fb59e5..c8bbda94c8 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubSubSubClass.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubSubSubclass.java @@ -1,4 +1,4 @@ package com.tngtech.archunit.core.importer.testexamples.classhierarchyimport; -public class SubSubSubSubClass extends SubSubSubClass { +public class SubSubSubSubclass extends SubSubSubclass { } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubSubClass.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubSubclass.java similarity index 61% rename from archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubSubClass.java rename to archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubSubclass.java index 902991f6a4..b8425a3330 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubSubClass.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubSubclass.java @@ -1,4 +1,4 @@ package com.tngtech.archunit.core.importer.testexamples.classhierarchyimport; -public class SubSubSubClass extends SubSubClass { +public class SubSubSubclass extends SubSubclass { } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubClass.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubclass.java similarity index 77% rename from archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubClass.java rename to archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubclass.java index 6415fbbd1a..6dae2a57fe 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubClass.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubSubclass.java @@ -1,6 +1,6 @@ package com.tngtech.archunit.core.importer.testexamples.classhierarchyimport; -public class SubSubClass extends SubClass implements SubInterface, YetAnotherInterface { +public class SubSubclass extends Subclass implements Subinterface, YetAnotherInterface { private String printMe = "NoOp"; void doSomethingSubSub() { diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubClass.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/Subclass.java similarity index 59% rename from archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubClass.java rename to archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/Subclass.java index 13a3ccfc4f..1366f123bc 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubClass.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/Subclass.java @@ -1,22 +1,22 @@ package com.tngtech.archunit.core.importer.testexamples.classhierarchyimport; -public class SubClass extends BaseClass implements SubInterface { +public class Subclass extends BaseClass implements Subinterface { static { - System.out.println(SubClass.class.getSimpleName() + " initializing"); + System.out.println(Subclass.class.getSimpleName() + " initializing"); } private int intField; - public SubClass() { + public Subclass() { intField = 1; } - public SubClass(String someField) { - super(someField + "by" + SubClass.class.getSimpleName()); + public Subclass(String someField) { + super(someField + "by" + Subclass.class.getSimpleName()); intField = 1; } - public SubClass(String someField, int addition) { + public Subclass(String someField, int addition) { this(someField + addition); this.intField = addition; } @@ -26,7 +26,7 @@ public String getSomeField() { return super.getSomeField(); } - public void subClassSay() { + public void subclassSay() { System.out.println(getSomeField()); } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubInterface.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/Subinterface.java similarity index 59% rename from archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubInterface.java rename to archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/Subinterface.java index 93a0e23b13..0f3208f6f4 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/SubInterface.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchyimport/Subinterface.java @@ -1,4 +1,4 @@ package com.tngtech.archunit.core.importer.testexamples.classhierarchyimport; -public interface SubInterface extends ParentInterface { +public interface Subinterface extends ParentInterface { } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/AccessToSuperAndSubClassField.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/AccessToSuperAndSubClassField.java deleted file mode 100644 index 8d44a095b2..0000000000 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/AccessToSuperAndSubClassField.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.tngtech.archunit.core.importer.testexamples.hierarchicalfieldaccess; - -public class AccessToSuperAndSubClassField { - String accessSuperClassField() { - return new SubClassWithAccessedField().field; - } - - int accessSubClassField() { - return new SubClassWithAccessedField().maskedField; - } -} diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/AccessToSuperAndSubclassField.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/AccessToSuperAndSubclassField.java new file mode 100644 index 0000000000..fa34bcf32b --- /dev/null +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/AccessToSuperAndSubclassField.java @@ -0,0 +1,11 @@ +package com.tngtech.archunit.core.importer.testexamples.hierarchicalfieldaccess; + +public class AccessToSuperAndSubclassField { + String accessSuperclassField() { + return new SubclassWithAccessedField().field; + } + + int accessSubclassField() { + return new SubclassWithAccessedField().maskedField; + } +} diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/SubClassWithAccessedField.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/SubclassWithAccessedField.java similarity index 64% rename from archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/SubClassWithAccessedField.java rename to archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/SubclassWithAccessedField.java index 358899dcf4..2bef681d1d 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/SubClassWithAccessedField.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/SubclassWithAccessedField.java @@ -1,5 +1,5 @@ package com.tngtech.archunit.core.importer.testexamples.hierarchicalfieldaccess; -public class SubClassWithAccessedField extends SuperClassWithAccessedField { +public class SubclassWithAccessedField extends SuperclassWithAccessedField { int maskedField; } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/SuperClassWithAccessedField.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/SuperclassWithAccessedField.java similarity index 74% rename from archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/SuperClassWithAccessedField.java rename to archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/SuperclassWithAccessedField.java index efa9c7cd90..d7a68ab991 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/SuperClassWithAccessedField.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalfieldaccess/SuperclassWithAccessedField.java @@ -1,6 +1,6 @@ package com.tngtech.archunit.core.importer.testexamples.hierarchicalfieldaccess; -public class SuperClassWithAccessedField { +public class SuperclassWithAccessedField { String field; int maskedField; } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/CallOfSuperAndSubClassMethod.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/CallOfSuperAndSubClassMethod.java deleted file mode 100644 index cf12591354..0000000000 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/CallOfSuperAndSubClassMethod.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.tngtech.archunit.core.importer.testexamples.hierarchicalmethodcall; - -public class CallOfSuperAndSubClassMethod { - public static final String callSuperClassMethod = "callSuperClassMethod"; - public static final int callSuperClassLineNumber = 10; - public static final String callSubClassMethod = "callSubClassMethod"; - public static final int callSubClassLineNumber = 14; - - String callSuperClassMethod() { - return new SubClassWithCalledMethod().method(); - } - - int callSubClassMethod() { - return new SubClassWithCalledMethod().maskedMethod(); - } -} diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/CallOfSuperAndSubclassMethod.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/CallOfSuperAndSubclassMethod.java new file mode 100644 index 0000000000..7eb063b0e1 --- /dev/null +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/CallOfSuperAndSubclassMethod.java @@ -0,0 +1,16 @@ +package com.tngtech.archunit.core.importer.testexamples.hierarchicalmethodcall; + +public class CallOfSuperAndSubclassMethod { + public static final String callSuperclassMethod = "callSuperclassMethod"; + public static final int callSuperclassLineNumber = 10; + public static final String callSubclassMethod = "callSubclassMethod"; + public static final int callSubclassLineNumber = 14; + + String callSuperclassMethod() { + return new SubclassWithCalledMethod().method(); + } + + int callSubclassMethod() { + return new SubclassWithCalledMethod().maskedMethod(); + } +} diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/SubClassWithCalledMethod.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/SubclassWithCalledMethod.java similarity index 77% rename from archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/SubClassWithCalledMethod.java rename to archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/SubclassWithCalledMethod.java index c04864e32c..8622fc7b62 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/SubClassWithCalledMethod.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/SubclassWithCalledMethod.java @@ -1,6 +1,6 @@ package com.tngtech.archunit.core.importer.testexamples.hierarchicalmethodcall; -public class SubClassWithCalledMethod extends SuperClassWithCalledMethod { +public class SubclassWithCalledMethod extends SuperclassWithCalledMethod { public static final String maskedMethod = "maskedMethod"; @Override diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/SuperClassWithCalledMethod.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/SuperclassWithCalledMethod.java similarity index 84% rename from archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/SuperClassWithCalledMethod.java rename to archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/SuperclassWithCalledMethod.java index 060c0cd52f..7b6b48941d 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/SuperClassWithCalledMethod.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/hierarchicalmethodcall/SuperclassWithCalledMethod.java @@ -1,6 +1,6 @@ package com.tngtech.archunit.core.importer.testexamples.hierarchicalmethodcall; -public class SuperClassWithCalledMethod { +public class SuperclassWithCalledMethod { public static final String method = "method"; String method() { diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/integration/ClassCDependingOnClassB_SuperClassOfX.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/integration/ClassCDependingOnClassB_SuperclassOfX.java similarity index 78% rename from archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/integration/ClassCDependingOnClassB_SuperClassOfX.java rename to archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/integration/ClassCDependingOnClassB_SuperclassOfX.java index 94e7455c65..ee4e2526e2 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/integration/ClassCDependingOnClassB_SuperClassOfX.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/integration/ClassCDependingOnClassB_SuperclassOfX.java @@ -1,10 +1,10 @@ package com.tngtech.archunit.core.importer.testexamples.integration; -public class ClassCDependingOnClassB_SuperClassOfX { +public class ClassCDependingOnClassB_SuperclassOfX { private ClassBDependingOnClassA classB; private int cState; - public ClassCDependingOnClassB_SuperClassOfX(int a, int b, int c) { + public ClassCDependingOnClassB_SuperclassOfX(int a, int b, int c) { this.cState = a + b + c; } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/integration/ClassXDependingOnClassesABCD.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/integration/ClassXDependingOnClassesABCD.java index d819a99ebb..0034f35b42 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/integration/ClassXDependingOnClassesABCD.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/integration/ClassXDependingOnClassesABCD.java @@ -1,6 +1,6 @@ package com.tngtech.archunit.core.importer.testexamples.integration; -public class ClassXDependingOnClassesABCD extends ClassCDependingOnClassB_SuperClassOfX implements InterfaceOfClassX { +public class ClassXDependingOnClassesABCD extends ClassCDependingOnClassB_SuperclassOfX implements InterfaceOfClassX { private ClassA classA; private ClassBDependingOnClassA classB; diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/outsideofclasspath/MiddleClass.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/outsideofclasspath/MiddleClass.java index ef61fd2ed6..9e9bdcea1d 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/outsideofclasspath/MiddleClass.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/outsideofclasspath/MiddleClass.java @@ -1,6 +1,6 @@ package com.tngtech.archunit.core.importer.testexamples.outsideofclasspath; -public class MiddleClass extends MissingSuperClass { +public class MiddleClass extends MissingSuperclass { public String someField; private MissingDependency missingDependency = new MissingDependency(); diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/outsideofclasspath/MissingSuperClass.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/outsideofclasspath/MissingSuperclass.java similarity index 70% rename from archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/outsideofclasspath/MissingSuperClass.java rename to archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/outsideofclasspath/MissingSuperclass.java index 5183e04b74..6d1fb1ad0e 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/outsideofclasspath/MissingSuperClass.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/outsideofclasspath/MissingSuperclass.java @@ -1,9 +1,9 @@ package com.tngtech.archunit.core.importer.testexamples.outsideofclasspath; -public class MissingSuperClass { +public class MissingSuperclass { public String parentField; - public MissingSuperClass() { + public MissingSuperclass() { } public void overrideMe() { diff --git a/archunit/src/test/java/com/tngtech/archunit/lang/ConditionEventsTest.java b/archunit/src/test/java/com/tngtech/archunit/lang/ConditionEventsTest.java index b7ae5f37e6..cb3a4c981b 100644 --- a/archunit/src/test/java/com/tngtech/archunit/lang/ConditionEventsTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/lang/ConditionEventsTest.java @@ -39,9 +39,9 @@ public void handleViolations_reports_only_violations_referring_to_the_correct_ty ConditionEvents events = events( SimpleConditionEvent.satisfied(new CorrectType("do not handle"), "I'm not violated"), SimpleConditionEvent.violated(new WrongType(), "I'm violated, but wrong type"), - SimpleConditionEvent.violated(new WrongSuperType(), "I'm violated, but wrong type"), + SimpleConditionEvent.violated(new WrongSupertype(), "I'm violated, but wrong type"), SimpleConditionEvent.violated(new CorrectType("handle type"), "I'm violated and correct type"), - SimpleConditionEvent.violated(new CorrectSubType("handle sub type"), "I'm violated and correct sub type")); + SimpleConditionEvent.violated(new CorrectSubtype("handle sub type"), "I'm violated and correct sub type")); final Set handledFailures = new HashSet<>(); events.handleViolations(new ObjectToStringAndMessageJoiningTestHandler(handledFailures)); @@ -145,13 +145,13 @@ private static ConditionEvents events(ConditionEvent... events) { return result; } - private static class CorrectSubType extends CorrectType { - CorrectSubType(String message) { + private static class CorrectSubtype extends CorrectType { + CorrectSubtype(String message) { super(message); } } - static class CorrectType extends WrongSuperType { + static class CorrectType extends WrongSupertype { String message; CorrectType(String message) { @@ -167,7 +167,7 @@ public String toString() { private static class WrongType { } - private static class WrongSuperType { + private static class WrongSupertype { } } diff --git a/archunit/src/test/java/com/tngtech/archunit/lang/conditions/ArchConditionsTest.java b/archunit/src/test/java/com/tngtech/archunit/lang/conditions/ArchConditionsTest.java index d3df43ef62..79f96971f2 100644 --- a/archunit/src/test/java/com/tngtech/archunit/lang/conditions/ArchConditionsTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/lang/conditions/ArchConditionsTest.java @@ -50,11 +50,11 @@ public void never_call_method_where_target_owner_is_assignable_to() { JavaMethodCall callTodoNotCallMe = simulateCall.from(callingClass.getMethod("call"), 0).to(doNotCallMe); ArchCondition condition = never(callMethodWhere(target(name("doNotCallMe")) - .and(target(owner(assignableTo(SomeSuperClass.class)))))); + .and(target(owner(assignableTo(SomeSuperclass.class)))))); assertThat(condition).checking(callingClass) .containViolations(callTodoNotCallMe.getDescription()); - condition = never(callMethodWhere(target(name("doNotCallMe")).and(target(owner(type(SomeSuperClass.class)))))); + condition = never(callMethodWhere(target(name("doNotCallMe")).and(target(owner(type(SomeSuperclass.class)))))); assertThat(condition).checking(callingClass) .containNoViolation(); } @@ -145,7 +145,7 @@ void call() { } } - private static class SomeClass extends SomeSuperClass { + private static class SomeClass extends SomeSuperclass { void doNotCallMe() { } @@ -153,6 +153,6 @@ void callMe() { } } - private static class SomeSuperClass { + private static class SomeSuperclass { } -} \ No newline at end of file +} diff --git a/archunit/src/test/java/com/tngtech/archunit/library/ArchitecturesTest.java b/archunit/src/test/java/com/tngtech/archunit/library/ArchitecturesTest.java index 9fe271d8aa..883410c50d 100644 --- a/archunit/src/test/java/com/tngtech/archunit/library/ArchitecturesTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/library/ArchitecturesTest.java @@ -24,7 +24,7 @@ import com.tngtech.archunit.library.testclasses.onionarchitecture.domain.service.DomainServiceLayerClass; import com.tngtech.archunit.library.testclasses.second.three.any.SecondThreeAnyClass; import com.tngtech.archunit.library.testclasses.some.pkg.SomePkgClass; -import com.tngtech.archunit.library.testclasses.some.pkg.sub.SomePkgSubClass; +import com.tngtech.archunit.library.testclasses.some.pkg.sub.SomePkgSubclass; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.DataProviders; @@ -201,10 +201,10 @@ public void layered_architecture_gathers_all_layer_violations(LayeredArchitectur assertPatternMatches(result.getFailureReport().getDetails(), ImmutableSet.of( - expectedAccessViolationPattern(FirstAnyPkgClass.class, "call", SomePkgSubClass.class, "callMe"), + expectedAccessViolationPattern(FirstAnyPkgClass.class, "call", SomePkgSubclass.class, "callMe"), expectedAccessViolationPattern(SecondThreeAnyClass.class, "call", SomePkgClass.class, "callMe"), expectedAccessViolationPattern(FirstThreeAnyClass.class, "call", FirstAnyPkgClass.class, "callMe"), - fieldTypePattern(FirstAnyPkgClass.class, "illegalTarget", SomePkgSubClass.class), + fieldTypePattern(FirstAnyPkgClass.class, "illegalTarget", SomePkgSubclass.class), fieldTypePattern(FirstThreeAnyClass.class, "illegalTarget", FirstAnyPkgClass.class), fieldTypePattern(SecondThreeAnyClass.class, "illegalTarget", SomePkgClass.class))); } @@ -217,13 +217,13 @@ public static Object[][] toIgnore() { return DataProviders.testForEach( new RuleWithIgnore( - layeredArchitecture.ignoreDependency(FirstAnyPkgClass.class, SomePkgSubClass.class), + layeredArchitecture.ignoreDependency(FirstAnyPkgClass.class, SomePkgSubclass.class), "rule with ignore specified as class objects"), new RuleWithIgnore( - layeredArchitecture.ignoreDependency(FirstAnyPkgClass.class.getName(), SomePkgSubClass.class.getName()), + layeredArchitecture.ignoreDependency(FirstAnyPkgClass.class.getName(), SomePkgSubclass.class.getName()), "rule with ignore specified as class names"), new RuleWithIgnore( - layeredArchitecture.ignoreDependency(name(FirstAnyPkgClass.class.getName()), name(SomePkgSubClass.class.getName())), + layeredArchitecture.ignoreDependency(name(FirstAnyPkgClass.class.getName()), name(SomePkgSubclass.class.getName())), "rule with ignore specified as predicates")); } @@ -231,14 +231,14 @@ public static Object[][] toIgnore() { @UseDataProvider("toIgnore") public void layered_architecture_ignores_specified_violations(RuleWithIgnore layeredArchitectureWithIgnore) { JavaClasses classes = new ClassFileImporter().importClasses( - FirstAnyPkgClass.class, SomePkgSubClass.class, + FirstAnyPkgClass.class, SomePkgSubclass.class, SecondThreeAnyClass.class, SomePkgClass.class); EvaluationResult result = layeredArchitectureWithIgnore.rule.evaluate(classes); assertThat(singleLine(result)) .doesNotMatch(String.format(".*%s[^%s]*%s.*", - quote(FirstAnyPkgClass.class.getName()), NEW_LINE_REPLACE, quote(SomePkgSubClass.class.getName()))) + quote(FirstAnyPkgClass.class.getName()), NEW_LINE_REPLACE, quote(SomePkgSubclass.class.getName()))) .matches(String.format(".*%s[^%s]*%s.*", quote(SecondThreeAnyClass.class.getName()), NEW_LINE_REPLACE, quote(SomePkgClass.class.getName()))); } @@ -246,13 +246,13 @@ public void layered_architecture_ignores_specified_violations(RuleWithIgnore lay @Test public void layered_architecture_combines_multiple_ignores() { JavaClasses classes = new ClassFileImporter().importClasses( - FirstAnyPkgClass.class, SomePkgSubClass.class, + FirstAnyPkgClass.class, SomePkgSubclass.class, SecondThreeAnyClass.class, SomePkgClass.class); LayeredArchitecture layeredArchitecture = layeredArchitecture() .layer("One").definedBy(absolute("some.pkg..")) .whereLayer("One").mayNotBeAccessedByAnyLayer() - .ignoreDependency(FirstAnyPkgClass.class, SomePkgSubClass.class); + .ignoreDependency(FirstAnyPkgClass.class, SomePkgSubclass.class); assertThat(layeredArchitecture.evaluate(classes).hasViolation()).as("result has violation").isTrue(); diff --git a/archunit/src/test/java/com/tngtech/archunit/library/dependencies/SliceTest.java b/archunit/src/test/java/com/tngtech/archunit/library/dependencies/SliceTest.java index e8ce98e89f..13b953b45d 100644 --- a/archunit/src/test/java/com/tngtech/archunit/library/dependencies/SliceTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/library/dependencies/SliceTest.java @@ -7,7 +7,7 @@ import com.tngtech.archunit.library.testclasses.first.three.any.FirstThreeAnyClass; import com.tngtech.archunit.library.testclasses.second.any.pkg.SecondAnyClass; import com.tngtech.archunit.library.testclasses.second.three.any.SecondThreeAnyClass; -import com.tngtech.archunit.library.testclasses.some.pkg.sub.SomePkgSubClass; +import com.tngtech.archunit.library.testclasses.some.pkg.sub.SomePkgSubclass; import org.junit.Test; import static com.tngtech.archunit.testutil.Assertions.assertThatDependencies; @@ -21,7 +21,7 @@ public void dependencies_from_self() { assertThatDependencies(slice.getDependenciesFromSelf()).containOnly( from(FirstAnyPkgClass.class).to(Object.class) - .from(FirstAnyPkgClass.class).to(SomePkgSubClass.class) + .from(FirstAnyPkgClass.class).to(SomePkgSubclass.class) .from(FirstAnyPkgClass.class).to(SecondThreeAnyClass.class) .from(ClassOnlyDependentOnOwnPackageAndObject.class).to(Object.class) .from(FirstThreeAnyClass.class).to(Object.class) @@ -35,7 +35,7 @@ public void dependencies_to_self() { assertThatDependencies(slice.getDependenciesToSelf()).containOnly( from(SecondAnyClass.class).to(FirstAnyPkgClass.class) - .from(SomePkgSubClass.class).to(FirstAnyPkgClass.class) + .from(SomePkgSubclass.class).to(FirstAnyPkgClass.class) ); } @@ -52,4 +52,4 @@ private Slices slicesOfTestClasses() { JavaClasses testClasses = new ClassFileImporter().importPackages("com.tngtech.archunit.library.testclasses"); return Slices.matching("..testclasses.(*)..").transform(testClasses); } -} \ No newline at end of file +} diff --git a/archunit/src/test/java/com/tngtech/archunit/library/dependencies/SlicesShouldTest.java b/archunit/src/test/java/com/tngtech/archunit/library/dependencies/SlicesShouldTest.java index 47d1c9abf3..a97611ed9e 100644 --- a/archunit/src/test/java/com/tngtech/archunit/library/dependencies/SlicesShouldTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/library/dependencies/SlicesShouldTest.java @@ -13,7 +13,7 @@ import com.tngtech.archunit.library.testclasses.second.any.pkg.SecondAnyClass; import com.tngtech.archunit.library.testclasses.second.three.any.SecondThreeAnyClass; import com.tngtech.archunit.library.testclasses.some.pkg.SomePkgClass; -import com.tngtech.archunit.library.testclasses.some.pkg.sub.SomePkgSubClass; +import com.tngtech.archunit.library.testclasses.some.pkg.sub.SomePkgSubclass; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; @@ -45,35 +45,35 @@ public void slice_rules_should_ignore_configured_dependencies(SliceRule rule) { assertViolations(classes, rule) .contain(FirstThreeAnyClass.class, SecondThreeAnyClass.class) - .contain(FirstAnyPkgClass.class, SomePkgSubClass.class) + .contain(FirstAnyPkgClass.class, SomePkgSubclass.class) .contain(SecondAnyClass.class, FirstAnyPkgClass.class) .contain(SecondThreeAnyClass.class, SomePkgClass.class); rule = rule.ignoreDependency(classIn(".*\\.first\\.three\\..*"), DescribedPredicate.alwaysTrue()); assertViolations(classes, rule) .doNotContain(FirstThreeAnyClass.class, SecondThreeAnyClass.class) - .contain(FirstAnyPkgClass.class, SomePkgSubClass.class) + .contain(FirstAnyPkgClass.class, SomePkgSubclass.class) .contain(SecondAnyClass.class, FirstAnyPkgClass.class) .contain(SecondThreeAnyClass.class, SomePkgClass.class); - rule = rule.ignoreDependency(FirstAnyPkgClass.class.getName(), SomePkgSubClass.class.getName()); + rule = rule.ignoreDependency(FirstAnyPkgClass.class.getName(), SomePkgSubclass.class.getName()); assertViolations(classes, rule) .doNotContain(FirstThreeAnyClass.class, SecondThreeAnyClass.class) - .doNotContain(FirstAnyPkgClass.class, SomePkgSubClass.class) + .doNotContain(FirstAnyPkgClass.class, SomePkgSubclass.class) .contain(SecondAnyClass.class, FirstAnyPkgClass.class) .contain(SecondThreeAnyClass.class, SomePkgClass.class); rule = rule.ignoreDependency(SecondAnyClass.class, FirstAnyPkgClass.class); assertViolations(classes, rule) .doNotContain(FirstThreeAnyClass.class, SecondThreeAnyClass.class) - .doNotContain(FirstAnyPkgClass.class, SomePkgSubClass.class) + .doNotContain(FirstAnyPkgClass.class, SomePkgSubclass.class) .doNotContain(SecondAnyClass.class, FirstAnyPkgClass.class) .contain(SecondThreeAnyClass.class, SomePkgClass.class); rule = rule.ignoreDependency(DescribedPredicate.alwaysTrue(), classIn(".*\\.some\\.pkg\\..*")); assertViolations(classes, rule) .doNotContain(FirstThreeAnyClass.class, SecondThreeAnyClass.class) - .doNotContain(FirstAnyPkgClass.class, SomePkgSubClass.class) + .doNotContain(FirstAnyPkgClass.class, SomePkgSubclass.class) .doNotContain(SecondAnyClass.class, FirstAnyPkgClass.class) .doNotContain(SecondThreeAnyClass.class, SomePkgClass.class); } diff --git a/archunit/src/test/java/com/tngtech/archunit/library/testclasses/first/any/pkg/FirstAnyPkgClass.java b/archunit/src/test/java/com/tngtech/archunit/library/testclasses/first/any/pkg/FirstAnyPkgClass.java index 5d13837a14..12681fac02 100644 --- a/archunit/src/test/java/com/tngtech/archunit/library/testclasses/first/any/pkg/FirstAnyPkgClass.java +++ b/archunit/src/test/java/com/tngtech/archunit/library/testclasses/first/any/pkg/FirstAnyPkgClass.java @@ -1,11 +1,11 @@ package com.tngtech.archunit.library.testclasses.first.any.pkg; import com.tngtech.archunit.library.testclasses.second.three.any.SecondThreeAnyClass; -import com.tngtech.archunit.library.testclasses.some.pkg.sub.SomePkgSubClass; +import com.tngtech.archunit.library.testclasses.some.pkg.sub.SomePkgSubclass; @SuppressWarnings("WeakerAccess") public class FirstAnyPkgClass { - SomePkgSubClass illegalTarget; + SomePkgSubclass illegalTarget; SecondThreeAnyClass legalTarget; // The importer will never find the bytecode of FirstAnyPkgClass[].class, thus it will always be a stub in the same package FirstAnyPkgClass[] evilArrayToCauseOriginallyUnimportedClass; diff --git a/archunit/src/test/java/com/tngtech/archunit/library/testclasses/some/pkg/SomePkgClass.java b/archunit/src/test/java/com/tngtech/archunit/library/testclasses/some/pkg/SomePkgClass.java index 7d91117812..21d270298e 100644 --- a/archunit/src/test/java/com/tngtech/archunit/library/testclasses/some/pkg/SomePkgClass.java +++ b/archunit/src/test/java/com/tngtech/archunit/library/testclasses/some/pkg/SomePkgClass.java @@ -1,9 +1,9 @@ package com.tngtech.archunit.library.testclasses.some.pkg; -import com.tngtech.archunit.library.testclasses.some.pkg.sub.SomePkgSubClass; +import com.tngtech.archunit.library.testclasses.some.pkg.sub.SomePkgSubclass; public class SomePkgClass { - SomePkgSubClass legalTarget; + SomePkgSubclass legalTarget; void call() { legalTarget.callMe(); diff --git a/archunit/src/test/java/com/tngtech/archunit/library/testclasses/some/pkg/sub/SomePkgSubClass.java b/archunit/src/test/java/com/tngtech/archunit/library/testclasses/some/pkg/sub/SomePkgSubclass.java similarity index 89% rename from archunit/src/test/java/com/tngtech/archunit/library/testclasses/some/pkg/sub/SomePkgSubClass.java rename to archunit/src/test/java/com/tngtech/archunit/library/testclasses/some/pkg/sub/SomePkgSubclass.java index 70b2efec96..27964632f7 100644 --- a/archunit/src/test/java/com/tngtech/archunit/library/testclasses/some/pkg/sub/SomePkgSubClass.java +++ b/archunit/src/test/java/com/tngtech/archunit/library/testclasses/some/pkg/sub/SomePkgSubclass.java @@ -2,7 +2,7 @@ import com.tngtech.archunit.library.testclasses.first.any.pkg.FirstAnyPkgClass; -public class SomePkgSubClass { +public class SomePkgSubclass { FirstAnyPkgClass legalTarget; void call() { diff --git a/archunit/src/test/java/com/tngtech/archunit/testutil/ArchConfigurationRule.java b/archunit/src/test/java/com/tngtech/archunit/testutil/ArchConfigurationRule.java index c283edae33..ccc67c7f29 100644 --- a/archunit/src/test/java/com/tngtech/archunit/testutil/ArchConfigurationRule.java +++ b/archunit/src/test/java/com/tngtech/archunit/testutil/ArchConfigurationRule.java @@ -1,11 +1,24 @@ package com.tngtech.archunit.testutil; +import java.util.concurrent.Callable; + import com.tngtech.archunit.ArchConfiguration; import org.junit.rules.ExternalResource; public class ArchConfigurationRule extends ExternalResource { private boolean resolveMissingDependenciesFromClassPath = ArchConfiguration.get().resolveMissingDependenciesFromClassPath(); + public static T resetConfigurationAround(Callable callable) { + ArchConfiguration.get().reset(); + try { + return callable.call(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + ArchConfiguration.get().reset(); + } + } + public ArchConfigurationRule resolveAdditionalDependenciesFromClassPath(boolean enabled) { resolveMissingDependenciesFromClassPath = enabled; return this; diff --git a/archunit/src/test/java/com/tngtech/archunit/testutil/Assertions.java b/archunit/src/test/java/com/tngtech/archunit/testutil/Assertions.java index a27b573443..adee9f10c9 100644 --- a/archunit/src/test/java/com/tngtech/archunit/testutil/Assertions.java +++ b/archunit/src/test/java/com/tngtech/archunit/testutil/Assertions.java @@ -7,6 +7,7 @@ import java.util.Set; import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.tngtech.archunit.base.DescribedPredicate; import com.tngtech.archunit.base.Optional; @@ -104,6 +105,14 @@ public static JavaTypesAssertion assertThatTypes(Iterable ja return new JavaTypesAssertion(javaTypes); } + public static JavaTypesAssertion assertThatTypeErasuresOf(Iterable javaTypes) { + ImmutableList.Builder erasures = ImmutableList.builder(); + for (JavaType javaType : javaTypes) { + erasures.add(javaType.toErasure()); + } + return new JavaTypesAssertion(erasures.build()); + } + public static JavaPackagesAssertion assertThatPackages(Iterable javaPackages) { return new JavaPackagesAssertion(javaPackages); } diff --git a/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/DependenciesAssertion.java b/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/DependenciesAssertion.java index 629e972830..09eaf10c32 100644 --- a/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/DependenciesAssertion.java +++ b/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/DependenciesAssertion.java @@ -10,6 +10,7 @@ import com.google.common.base.Predicate; import com.google.common.collect.FluentIterable; import com.google.common.collect.Iterables; +import com.tngtech.archunit.base.HasDescription; import com.tngtech.archunit.core.domain.Dependency; import org.assertj.core.api.AbstractIterableAssert; @@ -111,6 +112,7 @@ public static ExpectedDependenciesCreator from(Class origin) { public static class ExpectedDependenciesCreator { private final ExpectedDependencies expectedDependencies; + private Optional descriptionTemplate = Optional.absent(); private final Class origin; private ExpectedDependenciesCreator(ExpectedDependencies expectedDependencies, Class origin) { @@ -118,8 +120,16 @@ private ExpectedDependenciesCreator(ExpectedDependencies expectedDependencies, C this.origin = origin; } - public ExpectedDependencies to(Class target) { - return expectedDependencies.add(origin, target); + public ExpectedDependenciesCreator withExpectedDescriptionTemplate(String descriptionTemplate) { + this.descriptionTemplate = Optional.of(descriptionTemplate); + return this; + } + + public ExpectedDependencies to(Class... targets) { + for (Class target : targets) { + expectedDependencies.add(origin, target, descriptionTemplate); + } + return expectedDependencies; } } @@ -138,9 +148,12 @@ public ExpectedDependenciesCreator from(Class origin) { return new ExpectedDependenciesCreator(this, origin); } - ExpectedDependencies add(Class origin, Class target) { - expectedDependencies.add(new ExpectedDependency(origin, target)); - return this; + void add(Class origin, Class target, Optional descriptionTemplate) { + ExpectedDependency expectedDependency = new ExpectedDependency(origin, target); + if (descriptionTemplate.isPresent()) { + expectedDependency.descriptionContaining(descriptionTemplate.get().replace("#target", target.getName())); + } + expectedDependencies.add(expectedDependency); } public ExpectedDependencies withDescriptionContaining(String descriptionTemplate, Object... args) { @@ -172,7 +185,7 @@ boolean matches(Dependency dependency) { && (!locationPart.isPresent() || dependency.getDescription().endsWith(locationPart.get())); } - public void descriptionContaining(String descriptionTemplate, Object[] args) { + public void descriptionContaining(String descriptionTemplate, Object... args) { String descriptionPart = String.format(descriptionTemplate, args); descriptionPattern = Optional.of(Pattern.compile(".*" + quote(descriptionPart) + ".*")); } @@ -190,7 +203,7 @@ public String toString() { } } - private static class ExpectedDependenciesMatchResult { + private class ExpectedDependenciesMatchResult { private final Iterable missingDependencies; private final Iterable unexpectedDependencies; @@ -201,8 +214,17 @@ private ExpectedDependenciesMatchResult(Iterable missingDepe void assertNoMissingDependencies() { if (!Iterables.isEmpty(missingDependencies)) { - throw new AssertionError("Could not find expected dependencies:" + lineSeparator() + Joiner.on(lineSeparator()).join(missingDependencies)); + throw new AssertionError("Could not find expected dependencies:" + lineSeparator() + Joiner.on(lineSeparator()).join(missingDependencies) + + lineSeparator() + "within: " + lineSeparator() + Joiner.on(lineSeparator()).join(descriptionsOf(actual))); + } + } + + private List descriptionsOf(Iterable haveDescriptions) { + List result = new ArrayList<>(); + for (HasDescription hasDescription : haveDescriptions) { + result.add(hasDescription.getDescription()); } + return result; } public void assertAllDependenciesMatched() { diff --git a/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/DescriptionContext.java b/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/DescriptionContext.java new file mode 100644 index 0000000000..ed578350fd --- /dev/null +++ b/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/DescriptionContext.java @@ -0,0 +1,81 @@ +package com.tngtech.archunit.testutil.assertion; + +import com.google.common.base.Joiner; +import com.google.common.collect.FluentIterable; + +import static com.google.common.collect.Iterables.cycle; +import static com.google.common.collect.Iterables.limit; + +class DescriptionContext { + private static final String MARKER = "##MARKER##"; + private static final String PLACEHOLDER = "_"; + + private final String context; + private final String description; + private final String currentElement; + private final String joinString; + + DescriptionContext(String context) { + this(context + MARKER, "assertion", "", ", "); + } + + private DescriptionContext(String context, String description, String currentElement, String joinString) { + this.context = context; + this.description = description; + this.currentElement = currentElement; + this.joinString = joinString; + } + + public DescriptionContext describe(String part) { + return new DescriptionContext(context.replace(MARKER, part + MARKER), description, part, joinString); + } + + public DescriptionContext describeUpperBounds() { + String newContext = context.replace(MARKER, " extends " + MARKER); + return new DescriptionContext(newContext, description, currentElement, " & "); + } + + public DescriptionContext describeLowerBounds() { + String newContext = context.replace(MARKER, " super " + MARKER); + return new DescriptionContext(newContext, description, currentElement, " & "); + } + + public DescriptionContext describeElements(int number) { + String elementsPlaceHolder = number > 0 ? joinedPlaceHolders(number) : "[]"; + return new DescriptionContext(context.replace(MARKER, elementsPlaceHolder), description, currentElement, joinString); + } + + public DescriptionContext describeElement(int index, int totalSize) { + int maxIndex = totalSize - 1; + String prefix = index > 0 ? joinedPlaceHolders(index) + joinString : ""; + String suffix = index < maxIndex ? joinString + joinedPlaceHolders(maxIndex - index) : ""; + String newContext = context.replace(MARKER, prefix + MARKER + suffix); + String newCurrentElement = this.currentElement + "[" + index + "]"; + return new DescriptionContext(newContext, description, newCurrentElement, joinString); + } + + private String joinedPlaceHolders(int number) { + return FluentIterable.from(limit(cycle(PLACEHOLDER), number)).join(Joiner.on(joinString)); + } + + public DescriptionContext step(String description) { + return new DescriptionContext(context, description, currentElement, joinString); + } + + public DescriptionContext metaInfo() { + String newContext = context.replace(MARKER, "{" + MARKER + "}"); + return new DescriptionContext(newContext, description, currentElement, joinString); + } + + public DescriptionContext describeTypeParameters() { + String newContext = context.replace(MARKER, "<" + MARKER + ">"); + String newJoinString = ", "; + return new DescriptionContext(newContext, description, currentElement, newJoinString); + } + + @Override + public String toString() { + String currentElementInfix = currentElement.isEmpty() ? "" : "[" + currentElement + "]"; + return "\"" + description + "\"" + currentElementInfix + " -> " + context.replace(MARKER, ""); + } +} diff --git a/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/ExpectedConcreteType.java b/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/ExpectedConcreteType.java new file mode 100644 index 0000000000..7c5de8fa1e --- /dev/null +++ b/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/ExpectedConcreteType.java @@ -0,0 +1,255 @@ +package com.tngtech.archunit.testutil.assertion; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.tngtech.archunit.core.domain.JavaClass; +import com.tngtech.archunit.core.domain.JavaGenericArrayType; +import com.tngtech.archunit.core.domain.JavaParameterizedType; +import com.tngtech.archunit.core.domain.JavaType; +import com.tngtech.archunit.core.domain.JavaTypeVariable; +import com.tngtech.archunit.core.domain.JavaWildcardType; +import org.junit.Assert; + +import static com.tngtech.archunit.core.domain.Formatters.ensureSimpleName; +import static com.tngtech.archunit.testutil.Assertions.assertThatType; +import static com.tngtech.archunit.testutil.Assertions.assertThatTypes; +import static java.util.Collections.emptyList; +import static org.assertj.core.api.Assertions.assertThat; + +public interface ExpectedConcreteType { + void assertMatchWith(JavaType actual, DescriptionContext context); + + class ExpectedConcreteClass implements ExpectedConcreteType { + private final Class clazz; + + private ExpectedConcreteClass(Class clazz) { + this.clazz = clazz; + } + + @Override + public void assertMatchWith(JavaType actual, DescriptionContext context) { + DescriptionContext newContext = context.describe(ensureSimpleName(actual.getName())); + assertThat(actual).as(newContext.toString()).isInstanceOf(JavaClass.class); + assertThatType(actual).as(newContext.toString()).matches(clazz); + } + + static ExpectedConcreteType[] wrap(Class[] classes) { + ExpectedConcreteType[] result = new ExpectedConcreteType[classes.length]; + for (int i = 0; i < classes.length; i++) { + result[i] = new ExpectedConcreteClass(classes[i]); + } + return result; + } + + public static ExpectedConcreteClass concreteClass(Class expectedType) { + return new ExpectedConcreteClass(expectedType); + } + } + + class ExpectedConcreteParameterizedType implements ExpectedConcreteType { + private final Type type; + private final List typeParameters = new ArrayList<>(); + + private ExpectedConcreteParameterizedType(Type type) { + this.type = type; + } + + public static ExpectedConcreteParameterizedType parameterizedType(Class expectedType) { + return new ExpectedConcreteParameterizedType(expectedType); + } + + public ExpectedConcreteType withTypeArguments(Class... type) { + return withTypeArguments(ExpectedConcreteClass.wrap(type)); + } + + public ExpectedConcreteType withTypeArguments(ExpectedConcreteType... type) { + typeParameters.addAll(ImmutableList.copyOf(type)); + return this; + } + + public ExpectedConcreteType withWildcardTypeParameter() { + return withTypeArguments(new ExpectedConcreteWildcardType()); + } + + public ExpectedConcreteType withWildcardTypeParameterWithUpperBound(Class bound) { + return withWildcardTypeParameterWithUpperBound(new ExpectedConcreteClass(bound)); + } + + public ExpectedConcreteType withWildcardTypeParameterWithUpperBound(ExpectedConcreteType bound) { + return withTypeArguments(ExpectedConcreteWildcardType.wildcardType().withUpperBound(bound)); + } + + public ExpectedConcreteType withWildcardTypeParameterWithLowerBound(Class bound) { + return withWildcardTypeParameterWithLowerBound(new ExpectedConcreteClass(bound)); + } + + public ExpectedConcreteType withWildcardTypeParameterWithLowerBound(ExpectedConcreteType bound) { + return withTypeArguments(ExpectedConcreteWildcardType.wildcardType().withLowerBound(bound)); + } + + public ExpectedConcreteType withWildcardTypeParameters(ExpectedConcreteWildcardType... wildcardTypes) { + return withTypeArguments(wildcardTypes); + } + + @Override + public void assertMatchWith(JavaType actual, DescriptionContext context) { + DescriptionContext newContext = context.describe(ensureSimpleName(actual.getName())); + assertThatType(actual.toErasure()).as(newContext.toString()).matches(type); + assertTypeParametersMatch(actual, newContext); + } + + private void assertTypeParametersMatch(JavaType actual, DescriptionContext context) { + DescriptionContext parameterContext = context.step("type parameters").describeTypeParameters(); + if (!(actual instanceof JavaParameterizedType)) { + Assert.fail(String.format( + "%s: Actual type is not parameterized, but expected to be parameterized with type parameters %s", parameterContext, typeParameters)); + } + List actualTypeParameters = ((JavaParameterizedType) actual).getActualTypeArguments(); + assertThatTypes(actualTypeParameters).matchExactly(typeParameters, parameterContext); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + type + formatTypeParameters() + '}'; + } + + private String formatTypeParameters() { + return !typeParameters.isEmpty() ? "<" + Joiner.on(", ").join(typeParameters) + ">" : ""; + } + } + + class ExpectedConcreteWildcardType implements ExpectedConcreteType { + private final List upperBounds = new ArrayList<>(); + private final List lowerBounds = new ArrayList<>(); + + private ExpectedConcreteWildcardType() { + } + + @Override + public void assertMatchWith(JavaType actual, DescriptionContext context) { + context = context.describe(actual.getName()); + + assertThat(actual).as(context.toString()).isInstanceOf(JavaWildcardType.class); + JavaWildcardType wildcardType = (JavaWildcardType) actual; + assertThat(wildcardType.getName()).as(context.toString()).isEqualTo("?"); + + assertUpperBoundsMatch(wildcardType, context); + assertLowerBoundMatch(wildcardType, context); + } + + private void assertUpperBoundsMatch(JavaWildcardType actual, DescriptionContext context) { + context = context.step("upper bounds"); + assertThat(actual.getUpperBounds()).as(context.toString()).hasSameSizeAs(upperBounds); + context = context.describeUpperBounds(); + assertBoundsMatch(actual.getUpperBounds(), upperBounds, context); + } + + private void assertLowerBoundMatch(JavaWildcardType actual, DescriptionContext context) { + context = context.step("lower bounds"); + assertThat(actual.getLowerBounds()).as(context.toString()).hasSameSizeAs(lowerBounds); + context = context.describeLowerBounds(); + assertBoundsMatch(actual.getLowerBounds(), lowerBounds, context); + } + + private void assertBoundsMatch(List actualBounds, List expectedBounds, DescriptionContext context) { + for (int i = 0; i < expectedBounds.size(); i++) { + expectedBounds.get(i).assertMatchWith(actualBounds.get(i), context); + } + } + + public ExpectedConcreteWildcardType withUpperBound(Class bound) { + return withUpperBound(new ExpectedConcreteClass(bound)); + } + + public ExpectedConcreteWildcardType withUpperBound(ExpectedConcreteType bound) { + upperBounds.add(bound); + return this; + } + + public ExpectedConcreteWildcardType withLowerBound(Class bound) { + return withLowerBound(new ExpectedConcreteClass(bound)); + } + + public ExpectedConcreteWildcardType withLowerBound(ExpectedConcreteType bound) { + lowerBounds.add(bound); + return this; + } + + public static ExpectedConcreteWildcardType wildcardType() { + return new ExpectedConcreteWildcardType(); + } + } + + class ExpectedConcreteTypeVariable implements ExpectedConcreteType { + private final String name; + private List upperBounds; + + private ExpectedConcreteTypeVariable(String name) { + this.name = name; + } + + public ExpectedConcreteTypeVariable withUpperBounds(Class... bounds) { + return withUpperBounds(ExpectedConcreteClass.wrap(bounds)); + } + + public ExpectedConcreteTypeVariable withUpperBounds(ExpectedConcreteType... bounds) { + upperBounds = ImmutableList.copyOf(bounds); + return this; + } + + public ExpectedConcreteTypeVariable withoutUpperBounds() { + upperBounds = emptyList(); + return this; + } + + @Override + public void assertMatchWith(JavaType actual, DescriptionContext context) { + assertThat(actual).as(context.step("JavaType").toString()).isInstanceOf(JavaTypeVariable.class); + JavaTypeVariable actualTypeVariable = (JavaTypeVariable) actual; + assertThat(actualTypeVariable.getName()).as(context.step("type variable name").toString()).isEqualTo(name); + + if (upperBounds != null) { + DescriptionContext newContext = context.describe(actual.getName()).step("bounds").metaInfo().describeUpperBounds(); + assertThatTypes(actualTypeVariable.getUpperBounds()).matchExactly(upperBounds, newContext); + } + } + + public static ExpectedConcreteTypeVariable typeVariable(String name) { + return new ExpectedConcreteTypeVariable(name); + } + } + + class ExpectedConcreteTypeVariableArray implements ExpectedConcreteType { + private final String name; + private ExpectedConcreteType componentType; + + private ExpectedConcreteTypeVariableArray(String name) { + this.name = name; + } + + public ExpectedConcreteTypeVariableArray withComponentType(ExpectedConcreteType componentType) { + this.componentType = componentType; + return this; + } + + @Override + public void assertMatchWith(JavaType actual, DescriptionContext context) { + assertThat(actual).as(context.step("JavaType").toString()).isInstanceOf(JavaGenericArrayType.class); + JavaGenericArrayType actualArrayType = (JavaGenericArrayType) actual; + assertThat(actualArrayType.getName()).as(context.step("type variable name").toString()).isEqualTo(name); + + if (componentType != null) { + DescriptionContext newContext = context.describe(actual.getName()).step("component type").metaInfo(); + componentType.assertMatchWith(actualArrayType.getComponentType(), newContext); + } + } + + public static ExpectedConcreteTypeVariableArray typeVariableArray(String typeVariableArrayString) { + return new ExpectedConcreteTypeVariableArray(typeVariableArrayString); + } + } +} diff --git a/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/JavaTypeAssertion.java b/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/JavaTypeAssertion.java index 2bd4a81ec7..fb292407fe 100644 --- a/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/JavaTypeAssertion.java +++ b/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/JavaTypeAssertion.java @@ -9,9 +9,10 @@ import com.google.common.collect.FluentIterable; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaModifier; +import com.tngtech.archunit.core.domain.JavaParameterizedType; import com.tngtech.archunit.core.domain.JavaType; import com.tngtech.archunit.core.domain.JavaTypeVariable; -import com.tngtech.archunit.testutil.assertion.JavaTypeVariableAssertion.ExpectedConcreteType; +import com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteClass; import org.assertj.core.api.AbstractObjectAssert; import org.objectweb.asm.Type; @@ -88,8 +89,30 @@ public JavaTypeVariableOfClassAssertion hasOnlyTypeParameter(String name) { return hasTypeParameter(name); } + public JavaTypeAssertion hasErasure(Class rawType) { + new JavaTypeAssertion(actual.toErasure()).as("Erasure of %s", actual.getName()).matches(rawType); + return this; + } + + public void hasActualTypeArguments(Class... typeArguments) { + hasActualTypeArguments(ExpectedConcreteClass.wrap(typeArguments)); + } + + public void hasActualTypeArguments(ExpectedConcreteType... typeArguments) { + assertThat(actual).isInstanceOf(JavaParameterizedType.class); + JavaParameterizedType parameterizedType = (JavaParameterizedType) this.actual; + + List actualTypeArguments = parameterizedType.getActualTypeArguments(); + DescriptionContext context = new DescriptionContext(actual.getName()).describeTypeParameters().step("actual type arguments"); + assertThat(actualTypeArguments).as(context.toString()).hasSameSizeAs(typeArguments); + for (int i = 0; i < actualTypeArguments.size(); i++) { + typeArguments[i].assertMatchWith(actualTypeArguments.get(i), context.describeElement(i, actualTypeArguments.size())); + } + } + private JavaClass actualClass() { - return actual instanceof JavaClass ? (JavaClass) actual : actual.toErasure(); + assertThat(actual).as(describeAssertion(actual.getName())).isInstanceOf(JavaClass.class); + return (JavaClass) actual; } private String ensureArrayName(String name) { diff --git a/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/JavaTypeVariableAssertion.java b/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/JavaTypeVariableAssertion.java index 820227e0c4..8ef0d8e9fe 100644 --- a/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/JavaTypeVariableAssertion.java +++ b/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/JavaTypeVariableAssertion.java @@ -1,27 +1,8 @@ package com.tngtech.archunit.testutil.assertion; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; - -import com.google.common.base.Joiner; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ImmutableList; -import com.tngtech.archunit.core.domain.JavaGenericArrayType; -import com.tngtech.archunit.core.domain.JavaParameterizedType; -import com.tngtech.archunit.core.domain.JavaType; import com.tngtech.archunit.core.domain.JavaTypeVariable; -import com.tngtech.archunit.core.domain.JavaWildcardType; +import com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteClass; import org.assertj.core.api.AbstractObjectAssert; -import org.junit.Assert; - -import static com.google.common.collect.Iterables.cycle; -import static com.google.common.collect.Iterables.limit; -import static com.tngtech.archunit.core.domain.Formatters.ensureSimpleName; -import static com.tngtech.archunit.testutil.Assertions.assertThat; -import static com.tngtech.archunit.testutil.Assertions.assertThatType; -import static com.tngtech.archunit.testutil.assertion.JavaTypeVariableAssertion.ExpectedConcreteWildcardType.wildcardType; -import static java.util.Collections.emptyList; public class JavaTypeVariableAssertion extends AbstractObjectAssert> { public JavaTypeVariableAssertion(JavaTypeVariable actual) { @@ -29,307 +10,11 @@ public JavaTypeVariableAssertion(JavaTypeVariable actual) { } public void hasBoundsMatching(Class... bounds) { - hasBoundsMatching(ExpectedConcreteParameterizedType.wrap(bounds)); + hasBoundsMatching(ExpectedConcreteClass.wrap(bounds)); } public void hasBoundsMatching(ExpectedConcreteType... bounds) { DescriptionContext context = new DescriptionContext(actual.getName()).step("bounds").describeUpperBounds(); - assertConcreteTypesMatch(context, actual.getBounds(), ImmutableList.copyOf(bounds)); - } - - private static void assertConcreteTypesMatch(DescriptionContext context, List actual, List expected) { - assertThat(actual).as(context.describeElements(actual.size()).toString()).hasSize(expected.size()); - for (int i = 0; i < actual.size(); i++) { - DescriptionContext elementContext = context.describeElement(i, actual.size()); - expected.get(i).assertMatchWith(actual.get(i), elementContext); - } - } - - public static ExpectedConcreteParameterizedType parameterizedType(Class expectedType) { - return new ExpectedConcreteParameterizedType(expectedType); - } - - public interface ExpectedConcreteType { - void assertMatchWith(JavaType actual, DescriptionContext context); - } - - public static class ExpectedConcreteParameterizedType implements ExpectedConcreteType { - private Type type; - private final List typeParameters = new ArrayList<>(); - - private ExpectedConcreteParameterizedType(Type type) { - this.type = type; - } - - public ExpectedConcreteType withTypeArguments(Type... type) { - return withTypeArguments(ExpectedConcreteParameterizedType.wrap(type)); - } - - public ExpectedConcreteType withTypeArguments(ExpectedConcreteType... type) { - typeParameters.addAll(ImmutableList.copyOf(type)); - return this; - } - - public ExpectedConcreteType withWildcardTypeParameter() { - return withTypeArguments(new ExpectedConcreteWildcardType()); - } - - public ExpectedConcreteType withWildcardTypeParameterWithUpperBound(Class bound) { - return withWildcardTypeParameterWithUpperBound(new ExpectedConcreteParameterizedType(bound)); - } - - public ExpectedConcreteType withWildcardTypeParameterWithUpperBound(ExpectedConcreteType bound) { - return withTypeArguments(wildcardType().withUpperBound(bound)); - } - - public ExpectedConcreteType withWildcardTypeParameterWithLowerBound(Class bound) { - return withWildcardTypeParameterWithLowerBound(new ExpectedConcreteParameterizedType(bound)); - } - - public ExpectedConcreteType withWildcardTypeParameterWithLowerBound(ExpectedConcreteType bound) { - return withTypeArguments(wildcardType().withLowerBound(bound)); - } - - public ExpectedConcreteType withWildcardTypeParameters(ExpectedConcreteWildcardType... wildcardTypes) { - return withTypeArguments(wildcardTypes); - } - - @Override - public void assertMatchWith(JavaType actual, DescriptionContext context) { - DescriptionContext newContext = context.describe(ensureSimpleName(actual.getName())); - assertThatType(actual).as(newContext.toString()).matches(type); - assertTypeParametersMatch(actual, newContext); - } - - private void assertTypeParametersMatch(JavaType actual, DescriptionContext context) { - DescriptionContext parameterContext = context.step("type parameters").describeTypeParameters(); - if (!typeParameters.isEmpty() && !(actual instanceof JavaParameterizedType)) { - Assert.fail(String.format("%s: Not parameterized, but expected to have type parameters %s", parameterContext, typeParameters)); - } - List actualTypeParameters = ((JavaParameterizedType) actual).getActualTypeArguments(); - assertConcreteTypesMatch(parameterContext, actualTypeParameters, typeParameters); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "{" + type + formatTypeParameters() + '}'; - } - - private String formatTypeParameters() { - return !typeParameters.isEmpty() ? "<" + Joiner.on(", ").join(typeParameters) + ">" : ""; - } - - static ExpectedConcreteType[] wrap(Type... types) { - ExpectedConcreteType[] result = new ExpectedConcreteType[types.length]; - for (int i = 0; i < types.length; i++) { - result[i] = new ExpectedConcreteParameterizedType(types[i]); - } - return result; - } - } - - public static class ExpectedConcreteWildcardType implements ExpectedConcreteType { - private final List upperBounds = new ArrayList<>(); - private final List lowerBounds = new ArrayList<>(); - - private ExpectedConcreteWildcardType() { - } - - @Override - public void assertMatchWith(JavaType actual, DescriptionContext context) { - context = context.describe(actual.getName()); - - assertThat(actual).as(context.toString()).isInstanceOf(JavaWildcardType.class); - JavaWildcardType wildcardType = (JavaWildcardType) actual; - assertThat(wildcardType.getName()).as(context.toString()).isEqualTo("?"); - - assertUpperBoundsMatch(wildcardType, context); - assertLowerBoundMatch(wildcardType, context); - } - - private void assertUpperBoundsMatch(JavaWildcardType actual, DescriptionContext context) { - context = context.step("upper bounds"); - assertThat(actual.getUpperBounds()).as(context.toString()).hasSameSizeAs(upperBounds); - context = context.describeUpperBounds(); - assertBoundsMatch(actual.getUpperBounds(), upperBounds, context); - } - - private void assertLowerBoundMatch(JavaWildcardType actual, DescriptionContext context) { - context = context.step("lower bounds"); - assertThat(actual.getLowerBounds()).as(context.toString()).hasSameSizeAs(lowerBounds); - context = context.describeLowerBounds(); - assertBoundsMatch(actual.getLowerBounds(), lowerBounds, context); - } - - private void assertBoundsMatch(List actualBounds, List expectedBounds, DescriptionContext context) { - for (int i = 0; i < expectedBounds.size(); i++) { - expectedBounds.get(i).assertMatchWith(actualBounds.get(i), context); - } - } - - public ExpectedConcreteWildcardType withUpperBound(Class bound) { - return withUpperBound(new ExpectedConcreteParameterizedType(bound)); - } - - public ExpectedConcreteWildcardType withUpperBound(ExpectedConcreteType bound) { - upperBounds.add(bound); - return this; - } - - public ExpectedConcreteWildcardType withLowerBound(Class bound) { - return withLowerBound(new ExpectedConcreteParameterizedType(bound)); - } - - public ExpectedConcreteWildcardType withLowerBound(ExpectedConcreteType bound) { - lowerBounds.add(bound); - return this; - } - - public static ExpectedConcreteWildcardType wildcardType() { - return new ExpectedConcreteWildcardType(); - } - } - - public static class ExpectedConcreteTypeVariable implements ExpectedConcreteType { - private final String name; - private List upperBounds; - - private ExpectedConcreteTypeVariable(String name) { - this.name = name; - } - - public ExpectedConcreteTypeVariable withUpperBounds(Class... bounds) { - return withUpperBounds(ExpectedConcreteParameterizedType.wrap(bounds)); - } - - public ExpectedConcreteTypeVariable withUpperBounds(ExpectedConcreteType... bounds) { - upperBounds = ImmutableList.copyOf(bounds); - return this; - } - - public ExpectedConcreteTypeVariable withoutUpperBounds() { - upperBounds = emptyList(); - return this; - } - - @Override - public void assertMatchWith(JavaType actual, DescriptionContext context) { - assertThat(actual).as(context.step("JavaType").toString()).isInstanceOf(JavaTypeVariable.class); - JavaTypeVariable actualTypeVariable = (JavaTypeVariable) actual; - assertThat(actualTypeVariable.getName()).as(context.step("type variable name").toString()).isEqualTo(name); - - if (upperBounds != null) { - DescriptionContext newContext = context.describe(actual.getName()).step("bounds").metaInfo().describeUpperBounds(); - assertConcreteTypesMatch(newContext, actualTypeVariable.getUpperBounds(), upperBounds); - } - } - - public static ExpectedConcreteTypeVariable typeVariable(String name) { - return new ExpectedConcreteTypeVariable(name); - } - } - - public static class ExpectedConcreteTypeVariableArray implements ExpectedConcreteType { - private final String name; - private ExpectedConcreteType componentType; - - private ExpectedConcreteTypeVariableArray(String name) { - this.name = name; - } - - public ExpectedConcreteTypeVariableArray withComponentType(ExpectedConcreteType componentType) { - this.componentType = componentType; - return this; - } - - @Override - public void assertMatchWith(JavaType actual, DescriptionContext context) { - assertThat(actual).as(context.step("JavaType").toString()).isInstanceOf(JavaGenericArrayType.class); - JavaGenericArrayType actualArrayType = (JavaGenericArrayType) actual; - assertThat(actualArrayType.getName()).as(context.step("type variable name").toString()).isEqualTo(name); - - if (componentType != null) { - DescriptionContext newContext = context.describe(actual.getName()).step("component type").metaInfo(); - componentType.assertMatchWith(actualArrayType.getComponentType(), newContext); - } - } - - public static ExpectedConcreteTypeVariableArray typeVariableArray(String typeVariableArrayString) { - return new ExpectedConcreteTypeVariableArray(typeVariableArrayString); - } - } - - private static class DescriptionContext { - private static final String MARKER = "##MARKER##"; - private static final String PLACEHOLDER = "_"; - - private final String context; - private final String description; - private final String currentElement; - private final String joinString; - - DescriptionContext(String context) { - this(context + MARKER, "assertion", "", ", "); - } - - private DescriptionContext(String context, String description, String currentElement, String joinString) { - this.context = context; - this.description = description; - this.currentElement = currentElement; - this.joinString = joinString; - } - - public DescriptionContext describe(String part) { - return new DescriptionContext(context.replace(MARKER, part + MARKER), description, part, joinString); - } - - public DescriptionContext describeUpperBounds() { - String newContext = context.replace(MARKER, " extends " + MARKER); - return new DescriptionContext(newContext, description, currentElement, " & "); - } - - public DescriptionContext describeLowerBounds() { - String newContext = context.replace(MARKER, " super " + MARKER); - return new DescriptionContext(newContext, description, currentElement, " & "); - } - - public DescriptionContext describeElements(int number) { - String elementsPlaceHolder = number > 0 ? joinedPlaceHolders(number) : "[]"; - return new DescriptionContext(context.replace(MARKER, elementsPlaceHolder), description, currentElement, joinString); - } - - public DescriptionContext describeElement(int index, int totalSize) { - int maxIndex = totalSize - 1; - String prefix = index > 0 ? joinedPlaceHolders(index) + joinString : ""; - String suffix = index < maxIndex ? joinString + joinedPlaceHolders(maxIndex - index) : ""; - String newContext = context.replace(MARKER, prefix + MARKER + suffix); - String newCurrentElement = this.currentElement + "[" + index + "]"; - return new DescriptionContext(newContext, description, newCurrentElement, joinString); - } - - private String joinedPlaceHolders(int number) { - return FluentIterable.from(limit(cycle(PLACEHOLDER), number)).join(Joiner.on(joinString)); - } - - public DescriptionContext step(String description) { - return new DescriptionContext(context, description, currentElement, joinString); - } - - public DescriptionContext metaInfo() { - String newContext = context.replace(MARKER, "{" + MARKER + "}"); - return new DescriptionContext(newContext, description, currentElement, joinString); - } - - public DescriptionContext describeTypeParameters() { - String newContext = context.replace(MARKER, "<" + MARKER + ">"); - String newJoinString = ", "; - return new DescriptionContext(newContext, description, currentElement, newJoinString); - } - - @Override - public String toString() { - String currentElementInfix = currentElement.isEmpty() ? "" : "[" + currentElement + "]"; - return "\"" + description + "\"" + currentElementInfix + " -> " + context.replace(MARKER, ""); - } + new JavaTypesAssertion(actual.getBounds()).matchExactly(bounds, context); } } diff --git a/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/JavaTypesAssertion.java b/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/JavaTypesAssertion.java index c0c108eb83..3d8784aab1 100644 --- a/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/JavaTypesAssertion.java +++ b/archunit/src/test/java/com/tngtech/archunit/testutil/assertion/JavaTypesAssertion.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Set; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.tngtech.archunit.core.domain.JavaClass; @@ -43,6 +44,18 @@ public void matchInAnyOrder(Class... classes) { matchInAnyOrder(ImmutableSet.copyOf(classes)); } + public void matchExactly(ExpectedConcreteType[] expected, DescriptionContext context) { + matchExactly(ImmutableList.copyOf(expected), context); + } + + public void matchExactly(List expected, DescriptionContext context) { + assertThat(actual).as(context.describeElements(actual.length).toString()).hasSize(expected.size()); + for (int i = 0; i < actual.length; i++) { + DescriptionContext elementContext = context.describeElement(i, actual.length); + expected.get(i).assertMatchWith(actual[i], elementContext); + } + } + public void matchExactly(Class... classes) { assertThat(TestUtils.namesOf(actual)).as("classes").containsExactlyElementsOf(namesOf(classes)); matchInAnyOrder(classes); diff --git a/docs/userguide/006_The_Core_API.adoc b/docs/userguide/006_The_Core_API.adoc index d63509733b..9b48fadafd 100644 --- a/docs/userguide/006_The_Core_API.adoc +++ b/docs/userguide/006_The_Core_API.adoc @@ -170,11 +170,11 @@ class ClassAccessing { } class ClassBeingAccessed -class SuperClassBeingAccessed { +class SuperclassBeingAccessed { Object accessedField } -SuperClassBeingAccessed <|-- ClassBeingAccessed +SuperclassBeingAccessed <|-- ClassBeingAccessed ClassAccessing o-- ClassBeingAccessed ---- @@ -226,7 +226,7 @@ Two things might seem strange at the first look. First, why can a target resolve to zero matching members? The reason is that the set of classes that was imported does not need to have all classes involved within this resolution process. -Consider the above example, if `SuperClassBeingAccessed` would not be imported, ArchUnit would +Consider the above example, if `SuperclassBeingAccessed` would not be imported, ArchUnit would have no way of knowing where the actual targeted field resides. Thus in this case the resolution would return zero elements. diff --git a/docs/userguide/007_The_Lang_API.adoc b/docs/userguide/007_The_Lang_API.adoc index 2102b13b2e..a8870d7993 100644 --- a/docs/userguide/007_The_Lang_API.adoc +++ b/docs/userguide/007_The_Lang_API.adoc @@ -210,7 +210,7 @@ HasName.Predicates.name("").and(JavaClass.Predicates.type(Serializable.class)) JavaClass.Predicates.type(Serializable.class).and(HasName.Predicates.name("")) // Does compile, because the compiler now sees name(..) as a predicate for JavaClass -DescribedPredicate name = HasName.Predicates.name("").forSubType(); +DescribedPredicate name = HasName.Predicates.name("").forSubtype(); name.and(JavaClass.Predicates.type(Serializable.class)); ---- @@ -361,4 +361,4 @@ It is possible to add comments to ignore patterns by prefixing the line with a ' ---- # There are many known violations where LegacyService is involved; we'll ignore them all .*some\.pkg\.LegacyService.* ----- \ No newline at end of file +----