Skip to content

Commit

Permalink
Extended the syntax to offer an ArchRuleDefinition.theClass(..) and
Browse files Browse the repository at this point in the history
noClass(..) entry point to create rules based on a specific class. Also
added should().be(..) and should().notBe().

Issue: #60
Signed-off-by: Michael Sherman <msherman32@gatech.edu>
  • Loading branch information
Michael Sherman committed Jun 19, 2018
1 parent 38d1fe9 commit 37fd8af
Show file tree
Hide file tree
Showing 9 changed files with 1,719 additions and 47 deletions.
Expand Up @@ -65,7 +65,6 @@
import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.nameMatching;
import static com.tngtech.archunit.core.domain.properties.HasOwner.Predicates.With.owner;
import static com.tngtech.archunit.core.domain.properties.HasParameterTypes.Predicates.parameterTypes;
import static com.tngtech.archunit.lang.conditions.ArchPredicates.be;
import static com.tngtech.archunit.lang.conditions.ArchPredicates.have;
import static java.util.Arrays.asList;

Expand Down Expand Up @@ -276,6 +275,33 @@ private static DescribedPredicate<? super JavaFieldAccess> ownerAndNameAre(Strin
.as(ownerName + "." + fieldName);
}

public static ArchCondition<JavaClass> be(final Class<?> clazz) {
return be(clazz.getName());
}

public static ArchCondition<JavaClass> notBe(final Class<?> clazz) {
return not(be(clazz));
}

public static ArchCondition<JavaClass> be(final String className) {
return new ArchCondition<JavaClass>("be " + className) {
@Override
public void check(JavaClass javaClass, ConditionEvents events) {
boolean itemEquivalentToClazz = javaClass.getName().equals(className);
String message = String.format("class %s %s %s in %s",
javaClass.getName(),
itemEquivalentToClazz ? "is" : "is not",
className,
formatLocation(javaClass, 0));
events.add(new SimpleConditionEvent(javaClass, itemEquivalentToClazz, message));
}
};
}

public static ArchCondition<JavaClass> notBe(final String className) {
return not(be(className));
}

@PublicAPI(usage = ACCESS)
public static ArchCondition<JavaClass> haveFullyQualifiedName(final String name) {
final DescribedPredicate<HasName> haveFullyQualifiedName = have(fullyQualifiedName(name));
Expand Down Expand Up @@ -442,7 +468,7 @@ private static ArchCondition<JavaClass> residesConditionForPredicate(final Descr
@Override
public void check(JavaClass item, ConditionEvents events) {
boolean satisfied = resideInAPackage.apply(item);
String message = String.format("Class %s %s %s in %s",
String message = String.format("class %s %s %s in %s",
item.getName(),
satisfied ? "does" : "doesn't",
resideInAPackage.getDescription(),
Expand Down Expand Up @@ -578,7 +604,7 @@ public static ArchCondition<JavaClass> notBeMetaAnnotatedWith(DescribedPredicate
}

private static ArchCondition<JavaClass> createAnnotatedCondition(final DescribedPredicate<CanBeAnnotated> annotatedWith) {
return new ArchCondition<JavaClass>(be(annotatedWith).getDescription()) {
return new ArchCondition<JavaClass>(ArchPredicates.be(annotatedWith).getDescription()) {
@Override
public void check(JavaClass item, ConditionEvents events) {
boolean satisfied = annotatedWith.apply(item);
Expand Down Expand Up @@ -720,7 +746,7 @@ public static ArchCondition<JavaClass> notBeInterfaces() {
}

private static ArchCondition<JavaClass> createAssignableCondition(final DescribedPredicate<JavaClass> assignable) {
return new ArchCondition<JavaClass>(be(assignable).getDescription()) {
return new ArchCondition<JavaClass>(ArchPredicates.be(assignable).getDescription()) {
@Override
public void check(JavaClass item, ConditionEvents events) {
boolean satisfied = assignable.apply(item);
Expand Down
Expand Up @@ -17,19 +17,23 @@

import com.tngtech.archunit.PublicAPI;
import com.tngtech.archunit.base.Function;
import com.tngtech.archunit.base.Function.Functions;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.lang.AbstractClassesTransformer;
import com.tngtech.archunit.lang.ArchCondition;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.lang.ClassesTransformer;
import com.tngtech.archunit.lang.Priority;
import com.tngtech.archunit.lang.syntax.elements.GivenClass;
import com.tngtech.archunit.lang.syntax.elements.GivenClasses;
import com.tngtech.archunit.lang.syntax.elements.GivenObjects;

import static com.tngtech.archunit.PublicAPI.Usage.ACCESS;
import static com.tngtech.archunit.lang.Priority.MEDIUM;
import static com.tngtech.archunit.lang.conditions.ArchConditions.never;
import static java.util.Collections.singleton;

public final class ArchRuleDefinition {
private ArchRuleDefinition() {
Expand Down Expand Up @@ -66,6 +70,22 @@ public static GivenClasses noClasses() {
return priority(MEDIUM).noClasses();
}

public static GivenClass theClass(Class<?> clazz) {
return priority(MEDIUM).theClass(clazz);
}

public static GivenClass theClass(String className) {
return priority(MEDIUM).theClass(className);
}

public static GivenClass noClass(Class<?> clazz) {
return priority(MEDIUM).noClass(clazz);
}

public static GivenClass noClass(String className) {
return priority(MEDIUM).noClass(className);
}

public static final class Creator {
private final Priority priority;

Expand Down Expand Up @@ -110,6 +130,37 @@ public <TYPE> GivenObjects<TYPE> no(ClassesTransformer<TYPE> classesTransformer)
classesTransformer.as("no " + classesTransformer.getDescription()),
ArchRuleDefinition.<TYPE>negateCondition());
}

@PublicAPI(usage = ACCESS)
public GivenClass theClass(final Class<?> clazz) {
return theClass(clazz.getName());
}

@PublicAPI(usage = ACCESS)
public GivenClass theClass(final String className) {
ClassesTransformer<JavaClass> theClass = theClassTransformer(className);
return new GivenClassInternal(priority, theClass, Functions.<ArchCondition<JavaClass>>identity());
}

@PublicAPI(usage = ACCESS)
public GivenClass noClass(final Class<?> clazz) {
return noClass(clazz.getName());
}

@PublicAPI(usage = ACCESS)
public GivenClass noClass(final String className) {
ClassesTransformer<JavaClass> noClass = theClassTransformer(className).as("no class " + className);
return new GivenClassInternal(priority, noClass, ArchRuleDefinition.<JavaClass>negateCondition());
}

private ClassesTransformer<JavaClass> theClassTransformer(final String className) {
return new AbstractClassesTransformer<JavaClass>("the class " + className) {
@Override
public Iterable<JavaClass> doTransform(JavaClasses classes) {
return singleton(classes.get(className));
}
};
}
}

private static <T> Function<ArchCondition<T>, ArchCondition<T>> negateCondition() {
Expand Down
Expand Up @@ -59,6 +59,23 @@ private ClassesShouldInternal(ClassesTransformer<JavaClass> classesTransformer,
super(classesTransformer, priority, conditionAggregator, prepareCondition);
}

public ClassesShouldConjunction be(final Class<?> clazz) {
return copyWithNewCondition(conditionAggregator.add(ArchConditions.be(clazz)));
}

public ClassesShouldConjunction notBe(final Class<?> clazz) {
return copyWithNewCondition(conditionAggregator.add(ArchConditions.notBe(clazz)));
}

@Override
public ClassesShouldConjunction be(String className) {
return copyWithNewCondition(conditionAggregator.add(ArchConditions.be(className)));
}

public ClassesShouldConjunction notBe(String className) {
return copyWithNewCondition(conditionAggregator.add(ArchConditions.notBe(className)));
}

@Override
public ClassesShouldConjunction haveFullyQualifiedName(final String name) {
return copyWithNewCondition(conditionAggregator.add(ArchConditions.haveFullyQualifiedName(name)));
Expand Down
@@ -0,0 +1,48 @@
/*
* Copyright 2018 TNG Technology Consulting GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.tngtech.archunit.lang.syntax;

import com.tngtech.archunit.base.Function;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.lang.ArchCondition;
import com.tngtech.archunit.lang.ClassesTransformer;
import com.tngtech.archunit.lang.Priority;
import com.tngtech.archunit.lang.syntax.elements.ClassesShould;
import com.tngtech.archunit.lang.syntax.elements.ClassesShouldConjunction;
import com.tngtech.archunit.lang.syntax.elements.GivenClass;

class GivenClassInternal implements GivenClass {
private final Priority priority;
private final ClassesTransformer<JavaClass> classesTransformer;
private final Function<ArchCondition<JavaClass>, ArchCondition<JavaClass>> prepareCondition;

GivenClassInternal(Priority priority, ClassesTransformer<JavaClass> classesTransformer,
Function<ArchCondition<JavaClass>, ArchCondition<JavaClass>> prepareCondition) {
this.priority = priority;
this.classesTransformer = classesTransformer;
this.prepareCondition = prepareCondition;
}

@Override
public ClassesShould should() {
return new ClassesShouldInternal(classesTransformer, priority, prepareCondition);
}

@Override
public ClassesShouldConjunction should(ArchCondition<JavaClass> condition) {
return new ClassesShouldInternal(classesTransformer, priority, condition, prepareCondition);
}
}
Expand Up @@ -802,4 +802,38 @@ public interface ClassesShould {
*/
@PublicAPI(usage = ACCESS)
ClassesShouldConjunction notBeInterfaces();

/**
* Asserts that the rule matches exactly the given class.
*
* @param clazz the only class the should be matched
* @return A syntax element that can either be used as working rule, or to continue specifying a more complex rule
*/
@PublicAPI(usage = ACCESS)
ClassesShouldConjunction be(Class<?> clazz);

/**
* Asserts that the rule does not match the given class.
*
* @param clazz the class that should not be matched
* @return A syntax element that can either be used as working rule, or to continue specifying a more complex rule
*/
@PublicAPI(usage = ACCESS)
ClassesShouldConjunction notBe(Class<?> clazz);

/**
* Asserts that the rule matches exactly the class with the given fully qualified class name.
*
* @param className the name of the only class that should be matched.
* @return A syntax element that can either be used as working rule, or to continue specifying a more complex rule
*/
ClassesShouldConjunction be(String className);

/**
* Asserts that the rule does not match the class with the given fully qualified class name.
*
* @param className the name of the class that should not be matched.
* @return A syntax element that can either be used as working rule, or to continue specifying a more complex rule
*/
ClassesShouldConjunction notBe(String className);
}
@@ -0,0 +1,25 @@
/*
* Copyright 2018 TNG Technology Consulting GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.tngtech.archunit.lang.syntax.elements;

import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.lang.ArchCondition;

public interface GivenClass {
ClassesShould should();

ClassesShouldConjunction should(ArchCondition<JavaClass> condition);
}

0 comments on commit 37fd8af

Please sign in to comment.