Skip to content

Commit 92fef8a

Browse files
authored
Merge pull request #164 from hankem/static-final-syntax
static/final syntax
2 parents 751bbff + f145cbd commit 92fef8a

File tree

33 files changed

+770
-175
lines changed

33 files changed

+770
-175
lines changed

archunit-example/example-junit4/src/test/java/com/tngtech/archunit/exampletest/junit4/CodingRulesTest.java

+12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.tngtech.archunit.exampletest.junit4;
22

33
import com.tngtech.archunit.core.domain.JavaClasses;
4+
import com.tngtech.archunit.core.domain.JavaModifier;
45
import com.tngtech.archunit.junit.AnalyzeClasses;
56
import com.tngtech.archunit.junit.ArchTest;
67
import com.tngtech.archunit.junit.ArchUnitRunner;
@@ -9,6 +10,9 @@
910
import org.junit.experimental.categories.Category;
1011
import org.junit.runner.RunWith;
1112

13+
import java.util.logging.Logger;
14+
15+
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.fields;
1216
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
1317
import static com.tngtech.archunit.library.GeneralCodingRules.ACCESS_STANDARD_STREAMS;
1418
import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS;
@@ -35,6 +39,14 @@ private void no_access_to_standard_streams_as_method(JavaClasses classes) {
3539
@ArchTest
3640
private final ArchRule no_java_util_logging = NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING;
3741

42+
@ArchTest
43+
private final ArchRule loggers_should_be_private_static_final =
44+
fields().that().haveRawType(Logger.class)
45+
.should().bePrivate()
46+
.andShould().beStatic()
47+
.andShould().beFinal()
48+
.because("we agreed on this convention");
49+
3850
@ArchTest
3951
private final ArchRule no_jodatime = NO_CLASSES_SHOULD_USE_JODATIME;
4052

archunit-example/example-junit5/src/test/java/com/tngtech/archunit/exampletest/junit5/CodingRulesTest.java

+12
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package com.tngtech.archunit.exampletest.junit5;
22

33
import com.tngtech.archunit.core.domain.JavaClasses;
4+
import com.tngtech.archunit.core.domain.JavaModifier;
45
import com.tngtech.archunit.junit.AnalyzeClasses;
56
import com.tngtech.archunit.junit.ArchTag;
67
import com.tngtech.archunit.junit.ArchTest;
78
import com.tngtech.archunit.lang.ArchRule;
89
import com.tngtech.archunit.lang.CompositeArchRule;
910

11+
import java.util.logging.Logger;
12+
13+
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.fields;
1014
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
1115
import static com.tngtech.archunit.library.GeneralCodingRules.ACCESS_STANDARD_STREAMS;
1216
import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS;
@@ -32,6 +36,14 @@ private void no_access_to_standard_streams_as_method(JavaClasses classes) {
3236
@ArchTest
3337
private final ArchRule no_java_util_logging = NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING;
3438

39+
@ArchTest
40+
private final ArchRule loggers_should_be_private_static_final =
41+
fields().that().haveRawType(Logger.class)
42+
.should().bePrivate()
43+
.andShould().beStatic()
44+
.andShould().beFinal()
45+
.because("we agreed on this convention");
46+
3547
@ArchTest
3648
private final ArchRule no_jodatime = NO_CLASSES_SHOULD_USE_JODATIME;
3749

archunit-example/example-plain/src/main/java/com/tngtech/archunit/example/ClassViolatingCodingRules.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import java.util.logging.Logger;
77

88
public class ClassViolatingCodingRules {
9-
private static final Logger log = Logger.getLogger("Wrong Logger"); // Violates rule not to use java.util.logging
9+
static Logger log = Logger.getLogger("Wrong Logger"); // Violates rules not to use java.util.logging & that loggers should be private static final
1010

1111
public void printToStandardStream() throws FileNotFoundException {
1212
System.out.println("I'm gonna print to the command line"); // Violates rule not to write to standard streams

archunit-example/example-plain/src/test/java/com/tngtech/archunit/exampletest/CodingRulesTest.java

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package com.tngtech.archunit.exampletest;
22

33
import com.tngtech.archunit.core.domain.JavaClasses;
4+
import com.tngtech.archunit.core.domain.JavaModifier;
45
import com.tngtech.archunit.core.importer.ClassFileImporter;
56
import com.tngtech.archunit.example.ClassViolatingCodingRules;
67
import com.tngtech.archunit.lang.CompositeArchRule;
78
import org.junit.Test;
89
import org.junit.experimental.categories.Category;
910

11+
import java.util.logging.Logger;
12+
13+
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.fields;
1014
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
1115
import static com.tngtech.archunit.library.GeneralCodingRules.ACCESS_STANDARD_STREAMS;
1216
import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS;
@@ -39,6 +43,16 @@ public void classes_should_not_use_java_util_logging() {
3943
NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING.check(classes);
4044
}
4145

46+
@Test
47+
public void loggers_should_be_private_static_final() {
48+
fields().that().haveRawType(Logger.class)
49+
.should().bePrivate()
50+
.andShould().beStatic()
51+
.andShould().beFinal()
52+
.because("we agreed on this convention")
53+
.check(classes);
54+
}
55+
4256
@Test
4357
public void classes_should_not_use_jodatime() {
4458
NO_CLASSES_SHOULD_USE_JODATIME.check(classes);
@@ -49,5 +63,4 @@ public void no_classes_should_access_standard_streams_or_throw_generic_exception
4963
CompositeArchRule.of(NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS)
5064
.and(NO_CLASSES_SHOULD_THROW_GENERIC_EXCEPTIONS).check(classes);
5165
}
52-
5366
}

archunit-integration-test/src/test/java/com/tngtech/archunit/integration/ExamplesIntegrationTest.java

+7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import com.google.common.base.Joiner;
1717
import com.google.common.collect.ImmutableSet;
18+
import com.tngtech.archunit.core.domain.JavaModifier;
1819
import com.tngtech.archunit.example.AbstractController;
1920
import com.tngtech.archunit.example.ClassViolatingCodingRules;
2021
import com.tngtech.archunit.example.ClassViolatingSessionBeanRules;
@@ -108,6 +109,7 @@
108109
import com.tngtech.archunit.testutils.CyclicErrorMatcher;
109110
import com.tngtech.archunit.testutils.ExpectedClass;
110111
import com.tngtech.archunit.testutils.ExpectedConstructor;
112+
import com.tngtech.archunit.testutils.ExpectedField;
111113
import com.tngtech.archunit.testutils.ExpectedMethod;
112114
import com.tngtech.archunit.testutils.ExpectedTestFailures;
113115
import com.tngtech.archunit.testutils.MessageAssertionChain;
@@ -196,6 +198,11 @@ Stream<DynamicTest> CodingRulesTest() {
196198
expectAccessToStandardStreams(expectFailures);
197199
expectThrownGenericExceptions(expectFailures);
198200

201+
expectFailures.ofRule("fields that have raw type java.util.logging.Logger should be private " +
202+
"and should be static and should be final, because we agreed on this convention")
203+
.by(ExpectedField.of(ClassViolatingCodingRules.class, "log").doesNotHaveModifier(JavaModifier.PRIVATE))
204+
.by(ExpectedField.of(ClassViolatingCodingRules.class, "log").doesNotHaveModifier(JavaModifier.FINAL));
205+
199206
return expectFailures.toDynamicTests();
200207
}
201208

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.tngtech.archunit.testutils;
2+
3+
import com.tngtech.archunit.core.domain.JavaModifier;
4+
5+
import static java.lang.String.format;
6+
7+
public class ExpectedField {
8+
9+
public static ExpectedField.Creator of(Class<?> owner, String fieldName) {
10+
return new ExpectedField.Creator(owner, fieldName);
11+
}
12+
13+
public static class Creator {
14+
private final Class<?> clazz;
15+
private final String fieldName;
16+
17+
private Creator(Class<?> clazz, String fieldName) {
18+
this.clazz = clazz;
19+
this.fieldName = fieldName;
20+
}
21+
22+
public ExpectedMessage doesNotHaveModifier(JavaModifier modifier) {
23+
return field("does not have modifier " + modifier);
24+
}
25+
26+
private ExpectedMessage field(String message) {
27+
String fieldDescription = format("Field <%s.%s>", clazz.getName(), fieldName);
28+
String sourceCodeLocation = format("(%s.java:0)", clazz.getSimpleName());
29+
return new ExpectedMessage(format("%s %s in %s", fieldDescription, message, sourceCodeLocation));
30+
}
31+
}
32+
}

archunit-integration-test/src/test/java/com/tngtech/archunit/testutils/ExpectedMethod.java

+10-12
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import com.tngtech.archunit.core.domain.JavaClass;
66

7+
import static java.lang.String.format;
78
import static com.tngtech.archunit.core.domain.Formatters.formatMethod;
89

910
public class ExpectedMethod {
@@ -23,24 +24,21 @@ private Creator(Class<?> clazz, String methodName, Class<?>[] params) {
2324
}
2425

2526
public ExpectedMessage toNotHaveRawReturnType(Class<?> type) {
26-
return new ExpectedMessage(String.format("Method <%s> does not have raw return type %s in (%s.java:0)",
27-
formatMethod(clazz.getName(), methodName, JavaClass.namesOf(params)),
28-
type.getName(),
29-
clazz.getSimpleName()));
27+
return method("does not have raw return type " + type.getName());
3028
}
3129

3230
public ExpectedMessage throwsException(Class<?> type) {
33-
return new ExpectedMessage(String.format("Method <%s> does declare throwable of type %s in (%s.java:0)",
34-
formatMethod(clazz.getName(), methodName, JavaClass.namesOf(params)),
35-
type.getName(),
36-
clazz.getSimpleName()));
31+
return method("does declare throwable of type " + type.getName());
3732
}
3833

3934
public ExpectedMessage beingAnnotatedWith(Class<? extends Annotation> annotationType) {
40-
return new ExpectedMessage(String.format("Method <%s> is annotated with @%s in (%s.java:0)",
41-
formatMethod(clazz.getName(), methodName, JavaClass.namesOf(params)),
42-
annotationType.getSimpleName(),
43-
clazz.getSimpleName()));
35+
return method("is annotated with @" + annotationType.getSimpleName());
36+
}
37+
38+
private ExpectedMessage method(String message) {
39+
String methodDescription = format("Method <%s>", formatMethod(clazz.getName(), methodName, JavaClass.namesOf(params)));
40+
String sourceCodeLocation = format("(%s.java:0)", clazz.getSimpleName());
41+
return new ExpectedMessage(format("%s %s in %s", methodDescription, message, sourceCodeLocation));
4442
}
4543
}
4644
}

archunit/src/main/java/com/tngtech/archunit/lang/conditions/ArchConditions.java

+26-5
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@
5252
import com.tngtech.archunit.core.domain.properties.HasAnnotations;
5353
import com.tngtech.archunit.core.domain.properties.HasModifiers;
5454
import com.tngtech.archunit.core.domain.properties.HasName;
55-
import com.tngtech.archunit.core.domain.properties.HasSourceCodeLocation;
5655
import com.tngtech.archunit.core.domain.properties.HasOwner.Functions.Get;
5756
import com.tngtech.archunit.core.domain.properties.HasOwner.Predicates.With;
57+
import com.tngtech.archunit.core.domain.properties.HasSourceCodeLocation;
5858
import com.tngtech.archunit.core.domain.properties.HasThrowsClause;
5959
import com.tngtech.archunit.lang.ArchCondition;
6060
import com.tngtech.archunit.lang.ConditionEvents;
@@ -90,6 +90,7 @@
9090
import static com.tngtech.archunit.core.domain.JavaConstructor.CONSTRUCTOR_NAME;
9191
import static com.tngtech.archunit.core.domain.JavaMember.Predicates.declaredIn;
9292
import static com.tngtech.archunit.core.domain.JavaModifier.FINAL;
93+
import static com.tngtech.archunit.core.domain.JavaModifier.STATIC;
9394
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;
9495
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.metaAnnotatedWith;
9596
import static com.tngtech.archunit.core.domain.properties.HasModifiers.Predicates.modifier;
@@ -522,22 +523,22 @@ public static <HAS_NAME extends HasName & HasDescription & HasSourceCodeLocation
522523

523524
@PublicAPI(usage = ACCESS)
524525
public static ArchCondition<JavaClass> resideInAPackage(final String packageIdentifier) {
525-
return new DoesConditionByPredicate(JavaClass.Predicates.resideInAPackage(packageIdentifier));
526+
return new DoesConditionByPredicate<>(JavaClass.Predicates.resideInAPackage(packageIdentifier));
526527
}
527528

528529
@PublicAPI(usage = ACCESS)
529530
public static ArchCondition<JavaClass> resideInAnyPackage(String... packageIdentifiers) {
530-
return new DoesConditionByPredicate(JavaClass.Predicates.resideInAnyPackage(packageIdentifiers));
531+
return new DoesConditionByPredicate<>(JavaClass.Predicates.resideInAnyPackage(packageIdentifiers));
531532
}
532533

533534
@PublicAPI(usage = ACCESS)
534535
public static ArchCondition<JavaClass> resideOutsideOfPackage(String packageIdentifier) {
535-
return new DoesConditionByPredicate(JavaClass.Predicates.resideOutsideOfPackage(packageIdentifier));
536+
return new DoesConditionByPredicate<>(JavaClass.Predicates.resideOutsideOfPackage(packageIdentifier));
536537
}
537538

538539
@PublicAPI(usage = ACCESS)
539540
public static ArchCondition<JavaClass> resideOutsideOfPackages(String... packageIdentifiers) {
540-
return new DoesConditionByPredicate(JavaClass.Predicates.resideOutsideOfPackages(packageIdentifiers));
541+
return new DoesConditionByPredicate<>(JavaClass.Predicates.resideOutsideOfPackages(packageIdentifiers));
541542
}
542543

543544
@PublicAPI(usage = ACCESS)
@@ -595,6 +596,26 @@ public static <HAS_MODIFIERS extends HasModifiers & HasDescription & HasSourceCo
595596
return not(ArchConditions.<HAS_MODIFIERS>haveModifier(JavaModifier.PRIVATE)).as("not be private");
596597
}
597598

599+
@PublicAPI(usage = ACCESS)
600+
public static <HAS_MODIFIERS extends HasModifiers & HasDescription & HasSourceCodeLocation> ArchCondition<HAS_MODIFIERS> beStatic() {
601+
return ArchConditions.<HAS_MODIFIERS>haveModifier(STATIC).as("be static");
602+
}
603+
604+
@PublicAPI(usage = ACCESS)
605+
public static <HAS_MODIFIERS extends HasModifiers & HasDescription & HasSourceCodeLocation> ArchCondition<HAS_MODIFIERS> notBeStatic() {
606+
return not(ArchConditions.<HAS_MODIFIERS>haveModifier(STATIC).as("be static"));
607+
}
608+
609+
@PublicAPI(usage = ACCESS)
610+
public static <HAS_MODIFIERS extends HasModifiers & HasDescription & HasSourceCodeLocation> ArchCondition<HAS_MODIFIERS> beFinal() {
611+
return ArchConditions.<HAS_MODIFIERS>haveModifier(FINAL).as("be final");
612+
}
613+
614+
@PublicAPI(usage = ACCESS)
615+
public static <HAS_MODIFIERS extends HasModifiers & HasDescription & HasSourceCodeLocation> ArchCondition<HAS_MODIFIERS> notBeFinal() {
616+
return not(ArchConditions.<HAS_MODIFIERS>haveModifier(FINAL).as("be final"));
617+
}
618+
598619
@PublicAPI(usage = ACCESS)
599620
public static ArchCondition<JavaClass> haveOnlyFinalFields() {
600621
return new HaveOnlyFinalFieldsCondition();

archunit/src/main/java/com/tngtech/archunit/lang/syntax/AbstractMembersShouldInternal.java

+20
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,26 @@ public SELF notBePrivate() {
119119
return addCondition(ArchConditions.notBePrivate());
120120
}
121121

122+
// only applicable to fields and methods; therefore not exposed via MembersShould
123+
public SELF beStatic() {
124+
return addCondition(ArchConditions.beStatic());
125+
}
126+
127+
// only applicable to fields and methods; therefore not exposed via MembersShould
128+
public SELF notBeStatic() {
129+
return addCondition(ArchConditions.notBeStatic());
130+
}
131+
132+
// only applicable to fields and methods; therefore not exposed via MembersShould
133+
public SELF beFinal() {
134+
return addCondition(ArchConditions.beFinal());
135+
}
136+
137+
// only applicable to fields and methods; therefore not exposed via MembersShould
138+
public SELF notBeFinal() {
139+
return addCondition(ArchConditions.notBeFinal());
140+
}
141+
122142
@Override
123143
public SELF haveModifier(JavaModifier modifier) {
124144
return addCondition(ArchConditions.haveModifier(modifier));

archunit/src/main/java/com/tngtech/archunit/lang/syntax/GivenMethodsInternal.java

+15
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,21 @@ private GivenMethodsInternal(
5757
super(factory, priority, classesTransformer, prepareCondition, relevantObjectsPredicates, overriddenDescription);
5858
}
5959

60+
@Override
61+
public MethodsThatInternal that() {
62+
return new MethodsThatInternal(this, currentPredicate());
63+
}
64+
65+
@Override
66+
public MethodsThatInternal and() {
67+
return new MethodsThatInternal(this, currentPredicate().thatANDs());
68+
}
69+
70+
@Override
71+
public MethodsThatInternal or() {
72+
return new MethodsThatInternal(this, currentPredicate().thatORs());
73+
}
74+
6075
@Override
6176
public MethodsShouldInternal should() {
6277
return new MethodsShouldInternal(finishedClassesTransformer(), priority, prepareCondition);

archunit/src/main/java/com/tngtech/archunit/lang/syntax/MembersThatInternal.java

+20
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,26 @@ public CONJUNCTION areNotPrivate() {
111111
return givenWith(SyntaxPredicates.areNotPrivate());
112112
}
113113

114+
// only applicable to fields and methods; therefore not exposed via MembersThat
115+
public CONJUNCTION areStatic() {
116+
return givenWith(SyntaxPredicates.areStatic());
117+
}
118+
119+
// only applicable to fields and methods; therefore not exposed via MembersThat
120+
public CONJUNCTION areNotStatic() {
121+
return givenWith(SyntaxPredicates.areNotStatic());
122+
}
123+
124+
// only applicable to (classes,) fields and methods; therefore not exposed via MembersThat
125+
public CONJUNCTION areFinal() {
126+
return givenWith(SyntaxPredicates.areFinal());
127+
}
128+
129+
// only applicable to (classes,) fields and methods; therefore not exposed via MembersThat
130+
public CONJUNCTION areNotFinal() {
131+
return givenWith(SyntaxPredicates.areNotFinal());
132+
}
133+
114134
@Override
115135
public CONJUNCTION haveModifier(JavaModifier modifier) {
116136
return givenWith(SyntaxPredicates.haveModifier(modifier));

archunit/src/main/java/com/tngtech/archunit/lang/syntax/MethodsShouldInternal.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,12 @@
2020
import com.tngtech.archunit.lang.ArchCondition;
2121
import com.tngtech.archunit.lang.ClassesTransformer;
2222
import com.tngtech.archunit.lang.Priority;
23+
import com.tngtech.archunit.lang.syntax.elements.MethodsShould;
24+
import com.tngtech.archunit.lang.syntax.elements.MethodsShouldConjunction;
2325

24-
class MethodsShouldInternal extends AbstractCodeUnitsShouldInternal<JavaMethod, MethodsShouldInternal> {
26+
class MethodsShouldInternal
27+
extends AbstractCodeUnitsShouldInternal<JavaMethod, MethodsShouldInternal>
28+
implements MethodsShould<MethodsShouldInternal>, MethodsShouldConjunction {
2529

2630
MethodsShouldInternal(
2731
ClassesTransformer<? extends JavaMethod> classesTransformer,

0 commit comments

Comments
 (0)