From 4d1d4a54fc26df28cf47880b37d3eadafc49624b Mon Sep 17 00:00:00 2001 From: Tobias Stamann Date: Wed, 15 May 2024 15:01:41 +0200 Subject: [PATCH] Added additional constraints and tests --- constraints/README.md | 63 +++++ .../aptk/constraints/TargetElement.java | 9 + ... WithAnnotationAttributeTargetOfType.java} | 2 +- .../aptk/constraints/WithDoubleInBounds.java | 25 ++ .../aptk/constraints/WithFloatInBounds.java | 25 ++ .../aptk/constraints/WithIntegerInBounds.java | 25 ++ .../aptk/constraints/WithLongInBounds.java | 25 ++ ...EmptyArray.java => WithNonEmptyArray.java} | 6 +- ...ptyString.java => WithNonEmptyString.java} | 6 +- ...MustMatch.java => WithStringMatching.java} | 6 +- ...va => WithTargetElementAnnotatedWith.java} | 15 +- ...TargetElementAnnotatedWithRepeatable.java} | 6 +- ...ava => WithTargetElementAssignableTo.java} | 13 +- .../{On.java => WithTargetOfKind.java} | 9 +- ...getMustBeAnnotatedWithsConstraintImpl.java | 36 --- ...nAttributeTargetOfTypeConstraintImpl.java} | 8 +- .../WithDoubleInBoundsConstraintImpl.java | 99 ++++++++ .../WithFloatInBoundsConstraintImpl.java | 99 ++++++++ .../WithIntegerInBoundsConstraintImpl.java | 99 ++++++++ .../WithLongInBoundsConstraintImpl.java | 99 ++++++++ ...a => WithNonEmptyArrayConstraintImpl.java} | 6 +- ... => WithNonEmptyStringConstraintImpl.java} | 4 +- ... => WithStringMatchingConstraintImpl.java} | 6 +- ...etElementAnnotatedWithConstraintImpl.java} | 12 +- ...AnnotatedWithRepeatableConstraintImpl.java | 30 +++ ...getElementAssignableToConstraintImpl.java} | 6 +- ...va => WithTargetOfKindConstraintImpl.java} | 18 +- .../aptk/constraints/package-info.java | 16 +- .../constraints/BasicConstraintsTest.java | 3 + .../JustOnInterfacesAnnotation.java | 2 +- .../JustOnStringAttributeAndIntegers.java | 2 +- .../NonEmptyArrayConstraintsTest.java | 2 +- .../WithDoubleInBoundsConstraintsTest.java | 228 ++++++++++++++++++ .../WithFloatInBoundsConstraintsTest.java | 228 ++++++++++++++++++ .../WithIntegerInBoundsConstraintsTest.java | 228 ++++++++++++++++++ .../WithLongInBoundsConstraintsTest.java | 228 ++++++++++++++++++ ...=> WithNonEmptyStringConstraintsTest.java} | 6 +- ...=> WithStringMatchingConstraintsTest.java} | 4 +- ...tElementAnnotatedWithConstraintsTest.java} | 10 +- ...etElementAssignableToConstraintsTest.java} | 8 +- .../constraints/processor/TestAnnotation.java | 4 +- .../testcase/happyPath/HappyPath.java | 6 - .../InvalidUsage.java | 6 - .../manualConstraintsHappyPath/HappyPath.java | 4 +- mvnw.cmd | 8 +- 45 files changed, 1602 insertions(+), 148 deletions(-) create mode 100644 constraints/README.md create mode 100644 constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/TargetElement.java rename constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/{OnAnnotationAttributeOfType.java => WithAnnotationAttributeTargetOfType.java} (93%) create mode 100644 constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithDoubleInBounds.java create mode 100644 constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithFloatInBounds.java create mode 100644 constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithIntegerInBounds.java create mode 100644 constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithLongInBounds.java rename constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/{NonEmptyArray.java => WithNonEmptyArray.java} (62%) rename constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/{NonEmptyString.java => WithNonEmptyString.java} (55%) rename constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/{StringMustMatch.java => WithStringMatching.java} (57%) rename constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/{TargetMustBeAnnotatedWith.java => WithTargetElementAnnotatedWith.java} (56%) rename constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/{TargetMustBeAnnotatedWiths.java => WithTargetElementAnnotatedWithRepeatable.java} (67%) rename constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/{TargetMustBeAssignableTo.java => WithTargetElementAssignableTo.java} (52%) rename constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/{On.java => WithTargetOfKind.java} (86%) delete mode 100644 constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAnnotatedWithsConstraintImpl.java rename constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/{OnAnnotationAttributeOfTypeConstraintImpl.java => WithAnnotationAttributeTargetOfTypeConstraintImpl.java} (96%) create mode 100644 constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithDoubleInBoundsConstraintImpl.java create mode 100644 constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithFloatInBoundsConstraintImpl.java create mode 100644 constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithIntegerInBoundsConstraintImpl.java create mode 100644 constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithLongInBoundsConstraintImpl.java rename constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/{NonEmptyArrayConstraintImpl.java => WithNonEmptyArrayConstraintImpl.java} (90%) rename constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/{NonEmptyStringConstraintImpl.java => WithNonEmptyStringConstraintImpl.java} (95%) rename constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/{StringMustMatchConstraintImpl.java => WithStringMatchingConstraintImpl.java} (93%) rename constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/{TargetMustBeAnnotatedWithConstraintImpl.java => WithTargetElementAnnotatedWithConstraintImpl.java} (88%) create mode 100644 constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAnnotatedWithRepeatableConstraintImpl.java rename constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/{TargetMustBeAssignableToConstraintImpl.java => WithTargetElementAssignableToConstraintImpl.java} (94%) rename constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/{OnConstraintImpl.java => WithTargetOfKindConstraintImpl.java} (93%) create mode 100644 constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithDoubleInBoundsConstraintsTest.java create mode 100644 constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithFloatInBoundsConstraintsTest.java create mode 100644 constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithIntegerInBoundsConstraintsTest.java create mode 100644 constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithLongInBoundsConstraintsTest.java rename constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/{NonEmptyStringConstraintsTest.java => WithNonEmptyStringConstraintsTest.java} (96%) rename constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/{StringMustMatchConstraintsTest.java => WithStringMatchingConstraintsTest.java} (93%) rename constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/{TargetMustBeAnnotatedWithConstraintsTest.java => WithTargetElementAnnotatedWithConstraintsTest.java} (86%) rename constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/{TargetMustBeAssignableToConstraintsTest.java => WithTargetElementAssignableToConstraintsTest.java} (92%) diff --git a/constraints/README.md b/constraints/README.md new file mode 100644 index 00000000..c9c3452c --- /dev/null +++ b/constraints/README.md @@ -0,0 +1,63 @@ +# WARNING - THIS FEATURE IS STILL IN EXPERIMENTAL AND MIGHT BE REMOVED + +# Constraints on Annotations +A lot of the commonly used tools in Java development are somehow based on annotations. +In most cases there are kind of constraints about how those annotations must be used. + +One example is the Target annotation provided by Java itself which is used to allow annotations to be placed on certain kind of Elements. +But there are other kinds of possible constraints like that annotated types must implement a specific interface or that a String based annotation attribute must not be empty. +Such kind of constraints often just mentioned in the Javadoc and errors might only be triggered at runtime - this is the case for most tools which doesn't have an annotation processor involved. + +If there is an annotation processor present, it will be possible to check those constraints during the processing and trigger compiler messages in the compilation process. +This can be warnings or even errors that make the compilation fail. + +This subproject of APTK tries to ease applying and documenting of annotation constraints by providing general purpose constraint annotations which can be placed on annotation types or attributes. + +Just think about the Java's _Target_ annotation again. It allows restraining the usage on types by declaring: + +```java + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface TestAnnotation { + +} +``` + +Unfortunately the _ElementType.TYPE_ is kind of ambiguous and represents classes, interfaces, enums and records. +This tool provides the _On_ constraint annotation which can be used to further restrict the usage of annotations. +So if annotations must only be applicable on interfaces rather than on classes, enums and records. + +```java + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import io.toolisticon.aptk.constraints.WithTargetOfKind; +import io.toolisticon.aptk.constraints.WithTargetOfKind.TargetKind; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@On(Location.INTERFACE) +public @interface TestAnnotation { + +} +``` + +The library provides a standalone annotation processor that scans all annotations if they are annotated with any kind of constraint annotation. Additionally, it's possible to embed the validation into an existing processor. + +A constraint annotation is just an annotation that is annotated with the _Constraint_ meta annotation. It must be either be placeable on annotations or annotation attributes. +It's also possible to use constraint annotations on other constraint annotations. + +The constraint implementation will be bound to the constraint annotation via the _AnnotationConstraintSpi_ SPI. By doing this it's relatively easy to provide custom constraints. + +In some cases it's useful to manually implement constraints for some annotations. This can be done by implementing the _ManualConstraintSpi_ SPI. + +If we take a look at the _On_ annotation we will see that it is implenting both SPIs. One for applying constraints on other annotations and the other to make sure that it itself is used correctly. + diff --git a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/TargetElement.java b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/TargetElement.java new file mode 100644 index 00000000..bb4cd1b5 --- /dev/null +++ b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/TargetElement.java @@ -0,0 +1,9 @@ +package io.toolisticon.aptk.constraints; + +public enum TargetElement { + + ANNOTATED_ELEMENT, + PARENT_TYPE_ELEMENT, + TOP_LEVEL_TYPE_ELEMENT + +} diff --git a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/OnAnnotationAttributeOfType.java b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithAnnotationAttributeTargetOfType.java similarity index 93% rename from constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/OnAnnotationAttributeOfType.java rename to constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithAnnotationAttributeTargetOfType.java index c78d7b74..84ef3db9 100644 --- a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/OnAnnotationAttributeOfType.java +++ b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithAnnotationAttributeTargetOfType.java @@ -8,7 +8,7 @@ @Constraint @Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.RUNTIME) -public @interface OnAnnotationAttributeOfType { +public @interface WithAnnotationAttributeTargetOfType { enum AttributeType { FLOAT, diff --git a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithDoubleInBounds.java b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithDoubleInBounds.java new file mode 100644 index 00000000..5738cbd1 --- /dev/null +++ b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithDoubleInBounds.java @@ -0,0 +1,25 @@ +package io.toolisticon.aptk.constraints; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Constraint +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@WithTargetOfKind(WithTargetOfKind.TargetKind.ANNOTATION_ATTRIBUTE) +@WithAnnotationAttributeTargetOfType({WithAnnotationAttributeTargetOfType.AttributeType.DOUBLE, WithAnnotationAttributeTargetOfType.AttributeType.DOUBLE_ARRAY}) +public @interface WithDoubleInBounds { + + double lowerBound() default Double.MIN_VALUE; + + boolean inclusiveLowerBound() default true; + + double upperBound() default Double.MAX_VALUE; + + boolean inclusiveUpperBound() default true; + +} diff --git a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithFloatInBounds.java b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithFloatInBounds.java new file mode 100644 index 00000000..e6982ff2 --- /dev/null +++ b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithFloatInBounds.java @@ -0,0 +1,25 @@ +package io.toolisticon.aptk.constraints; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Constraint +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@WithTargetOfKind(WithTargetOfKind.TargetKind.ANNOTATION_ATTRIBUTE) +@WithAnnotationAttributeTargetOfType({WithAnnotationAttributeTargetOfType.AttributeType.FLOAT, WithAnnotationAttributeTargetOfType.AttributeType.FLOAT_ARRAY}) +public @interface WithFloatInBounds { + + float lowerBound() default Float.MIN_VALUE; + + boolean inclusiveLowerBound() default true; + + float upperBound() default Float.MAX_VALUE; + + boolean inclusiveUpperBound() default true; + +} diff --git a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithIntegerInBounds.java b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithIntegerInBounds.java new file mode 100644 index 00000000..c9d5cdea --- /dev/null +++ b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithIntegerInBounds.java @@ -0,0 +1,25 @@ +package io.toolisticon.aptk.constraints; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Constraint +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@WithTargetOfKind(WithTargetOfKind.TargetKind.ANNOTATION_ATTRIBUTE) +@WithAnnotationAttributeTargetOfType({WithAnnotationAttributeTargetOfType.AttributeType.INTEGER, WithAnnotationAttributeTargetOfType.AttributeType.INTEGER_ARRAY}) +public @interface WithIntegerInBounds { + + int lowerBound() default Integer.MIN_VALUE; + + boolean inclusiveLowerBound() default true; + + int upperBound() default Integer.MAX_VALUE; + + boolean inclusiveUpperBound() default true; + +} diff --git a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithLongInBounds.java b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithLongInBounds.java new file mode 100644 index 00000000..50fec146 --- /dev/null +++ b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithLongInBounds.java @@ -0,0 +1,25 @@ +package io.toolisticon.aptk.constraints; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Constraint +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@WithTargetOfKind(WithTargetOfKind.TargetKind.ANNOTATION_ATTRIBUTE) +@WithAnnotationAttributeTargetOfType({WithAnnotationAttributeTargetOfType.AttributeType.LONG, WithAnnotationAttributeTargetOfType.AttributeType.LONG_ARRAY}) +public @interface WithLongInBounds { + + long lowerBound() default Long.MIN_VALUE; + + boolean inclusiveLowerBound() default true; + + long upperBound() default Long.MAX_VALUE; + + boolean inclusiveUpperBound() default true; + +} diff --git a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/NonEmptyArray.java b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithNonEmptyArray.java similarity index 62% rename from constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/NonEmptyArray.java rename to constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithNonEmptyArray.java index 4299d02d..64c8688e 100644 --- a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/NonEmptyArray.java +++ b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithNonEmptyArray.java @@ -10,7 +10,7 @@ @Constraint @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) -@On(On.Location.ANNOTATION_ATTRIBUTE) -@OnAnnotationAttributeOfType({OnAnnotationAttributeOfType.AttributeType.ARRAY}) -public @interface NonEmptyArray { +@WithTargetOfKind(WithTargetOfKind.TargetKind.ANNOTATION_ATTRIBUTE) +@WithAnnotationAttributeTargetOfType({WithAnnotationAttributeTargetOfType.AttributeType.ARRAY}) +public @interface WithNonEmptyArray { } diff --git a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/NonEmptyString.java b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithNonEmptyString.java similarity index 55% rename from constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/NonEmptyString.java rename to constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithNonEmptyString.java index 697326f6..284edd1b 100644 --- a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/NonEmptyString.java +++ b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithNonEmptyString.java @@ -10,7 +10,7 @@ @Constraint @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) -@On(On.Location.ANNOTATION_ATTRIBUTE) -@OnAnnotationAttributeOfType({OnAnnotationAttributeOfType.AttributeType.STRING, OnAnnotationAttributeOfType.AttributeType.STRING_ARRAY}) -public @interface NonEmptyString { +@WithTargetOfKind(WithTargetOfKind.TargetKind.ANNOTATION_ATTRIBUTE) +@WithAnnotationAttributeTargetOfType({WithAnnotationAttributeTargetOfType.AttributeType.STRING, WithAnnotationAttributeTargetOfType.AttributeType.STRING_ARRAY}) +public @interface WithNonEmptyString { } diff --git a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/StringMustMatch.java b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithStringMatching.java similarity index 57% rename from constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/StringMustMatch.java rename to constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithStringMatching.java index db28f082..5daa0888 100644 --- a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/StringMustMatch.java +++ b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithStringMatching.java @@ -11,9 +11,9 @@ @Constraint @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) -@On(On.Location.ANNOTATION_ATTRIBUTE) -@OnAnnotationAttributeOfType({OnAnnotationAttributeOfType.AttributeType.STRING, OnAnnotationAttributeOfType.AttributeType.STRING_ARRAY}) -public @interface StringMustMatch { +@WithTargetOfKind(WithTargetOfKind.TargetKind.ANNOTATION_ATTRIBUTE) +@WithAnnotationAttributeTargetOfType({WithAnnotationAttributeTargetOfType.AttributeType.STRING, WithAnnotationAttributeTargetOfType.AttributeType.STRING_ARRAY}) +public @interface WithStringMatching { String value(); diff --git a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAnnotatedWith.java b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAnnotatedWith.java similarity index 56% rename from constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAnnotatedWith.java rename to constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAnnotatedWith.java index 810fde42..eaaa987d 100644 --- a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAnnotatedWith.java +++ b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAnnotatedWith.java @@ -13,19 +13,12 @@ @Constraint @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) -@On(On.Location.ANNOTATION) -@Repeatable(TargetMustBeAnnotatedWiths.class) -public @interface TargetMustBeAnnotatedWith { - - enum TargetElement { - ANNOTATED_ELEMENT, - PARENT_TYPE_ELEMENT, - TOP_LEVEL_TYPE_ELEMENT - } - +@WithTargetOfKind(WithTargetOfKind.TargetKind.ANNOTATION) +@Repeatable(WithTargetElementAnnotatedWithRepeatable.class) +public @interface WithTargetElementAnnotatedWith { Class value(); - TargetMustBeAnnotatedWith.TargetElement target() default TargetMustBeAnnotatedWith.TargetElement.ANNOTATED_ELEMENT; + TargetElement targetElement() default TargetElement.ANNOTATED_ELEMENT; } diff --git a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAnnotatedWiths.java b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAnnotatedWithRepeatable.java similarity index 67% rename from constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAnnotatedWiths.java rename to constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAnnotatedWithRepeatable.java index cc8d8014..e09b002b 100644 --- a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAnnotatedWiths.java +++ b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAnnotatedWithRepeatable.java @@ -11,7 +11,7 @@ @Constraint @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) -@On(On.Location.ANNOTATION) -public @interface TargetMustBeAnnotatedWiths { - TargetMustBeAnnotatedWith[] value(); +@WithTargetOfKind(WithTargetOfKind.TargetKind.ANNOTATION) +public @interface WithTargetElementAnnotatedWithRepeatable { + WithTargetElementAnnotatedWith[] value(); } diff --git a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAssignableTo.java b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAssignableTo.java similarity index 52% rename from constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAssignableTo.java rename to constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAssignableTo.java index 3f0d1e5a..9bb9f92a 100644 --- a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAssignableTo.java +++ b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAssignableTo.java @@ -2,7 +2,6 @@ import java.lang.annotation.Documented; import java.lang.annotation.ElementType; -import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -11,15 +10,9 @@ @Constraint @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) -@On(On.Location.ANNOTATION_ATTRIBUTE) -@OnAnnotationAttributeOfType({OnAnnotationAttributeOfType.AttributeType.CLASS, OnAnnotationAttributeOfType.AttributeType.CLASS_ARRAY}) -public @interface TargetMustBeAssignableTo { - - enum TargetElement { - ANNOTATED_ELEMENT, - PARENT_TYPE_ELEMENT, - TOP_LEVEL_TYPE_ELEMENT - } +@WithTargetOfKind(WithTargetOfKind.TargetKind.ANNOTATION_ATTRIBUTE) +@WithAnnotationAttributeTargetOfType({WithAnnotationAttributeTargetOfType.AttributeType.CLASS, WithAnnotationAttributeTargetOfType.AttributeType.CLASS_ARRAY}) +public @interface WithTargetElementAssignableTo { TargetElement targetElement() default TargetElement.ANNOTATED_ELEMENT; diff --git a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/On.java b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithTargetOfKind.java similarity index 86% rename from constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/On.java rename to constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithTargetOfKind.java index 768591c3..002a1611 100644 --- a/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/On.java +++ b/constraints/constraint-api/src/main/java/io/toolisticon/aptk/constraints/WithTargetOfKind.java @@ -16,17 +16,18 @@ */ @Documented @Constraint -@Target(ElementType.ANNOTATION_TYPE) +@Target({ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) -public @interface On { +public @interface WithTargetOfKind { - enum Location { + enum TargetKind { PACKAGE, ANNOTATION_ATTRIBUTE, ANNOTATION, CLASS, INTERFACE, ENUM, + RECORD, METHOD, CONSTRUCTOR, PARAMETER, @@ -36,6 +37,6 @@ enum Location { } - Location[] value(); + TargetKind[] value(); } diff --git a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAnnotatedWithsConstraintImpl.java b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAnnotatedWithsConstraintImpl.java deleted file mode 100644 index 5e087bdd..00000000 --- a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAnnotatedWithsConstraintImpl.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.toolisticon.aptk.constraints; - -import io.toolisticon.aptk.compilermessage.api.DeclareCompilerMessage; -import io.toolisticon.aptk.tools.wrapper.ElementWrapper; -import io.toolisticon.aptk.tools.wrapper.TypeElementWrapper; -import io.toolisticon.spiap.api.SpiService; - -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.Element; -import java.lang.annotation.Annotation; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -@SpiService(value = AnnotationConstraintSpi.class) -public class TargetMustBeAnnotatedWithsConstraintImpl implements AnnotationConstraintSpi { - - @Override - public Class getSupportedAnnotation() { - return TargetMustBeAnnotatedWiths.class; - } - - @Override - public boolean checkConstraints(Element annotatedElement, AnnotationMirror annotationMirrorToCheck, AnnotationMirror constraintAnnotationMirror, String attributeNameToBeCheckedByConstraint) { - - TargetMustBeAnnotatedWithsWrapper constraintWrapper = TargetMustBeAnnotatedWithsWrapper.wrap(annotatedElement, constraintAnnotationMirror); - - boolean result = true; - - for (TargetMustBeAnnotatedWithWrapper constraint : constraintWrapper.value()) { - result = result & new TargetMustBeAnnotatedWithConstraintImpl().checkConstraints(annotatedElement, annotationMirrorToCheck, constraint._annotationMirror(),attributeNameToBeCheckedByConstraint); - } - - return result; - } -} \ No newline at end of file diff --git a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/OnAnnotationAttributeOfTypeConstraintImpl.java b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithAnnotationAttributeTargetOfTypeConstraintImpl.java similarity index 96% rename from constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/OnAnnotationAttributeOfTypeConstraintImpl.java rename to constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithAnnotationAttributeTargetOfTypeConstraintImpl.java index 8778065c..7162ec09 100644 --- a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/OnAnnotationAttributeOfTypeConstraintImpl.java +++ b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithAnnotationAttributeTargetOfTypeConstraintImpl.java @@ -13,11 +13,11 @@ @SpiService(value = AnnotationConstraintSpi.class) @DeclareCompilerMessage(code = "ONATTRIBUTETYPE_001", enumValueName = "ONATTRIBUTETYPE_ERROR_WRONG_USAGE", message = "'${0}' Constraint violated: Annotation ${1} must be placed on annotation attribute of kind ${2}", processorClass = BasicConstraints.class) -public class OnAnnotationAttributeOfTypeConstraintImpl implements AnnotationConstraintSpi { +public class WithAnnotationAttributeTargetOfTypeConstraintImpl implements AnnotationConstraintSpi { @Override public Class getSupportedAnnotation() { - return OnAnnotationAttributeOfType.class; + return WithAnnotationAttributeTargetOfType.class; } @@ -37,11 +37,11 @@ public boolean checkConstraints(Element annotatedElement, AnnotationMirror annot ExecutableElementWrapper attributeElementWrapper = ExecutableElementWrapper.wrap((ExecutableElement) annotatedElement); // Now check if annotation - OnAnnotationAttributeOfTypeWrapper onAnnotationOfType = OnAnnotationAttributeOfTypeWrapper.wrap(constraintAnnotationMirror); + WithAnnotationAttributeTargetOfTypeWrapper onAnnotationOfType = WithAnnotationAttributeTargetOfTypeWrapper.wrap(constraintAnnotationMirror); boolean foundMatchingElementType = false; loop: - for (OnAnnotationAttributeOfType.AttributeType targetAttributeType : onAnnotationOfType.value()) { + for (WithAnnotationAttributeTargetOfType.AttributeType targetAttributeType : onAnnotationOfType.value()) { switch (targetAttributeType) { case ARRAY: { diff --git a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithDoubleInBoundsConstraintImpl.java b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithDoubleInBoundsConstraintImpl.java new file mode 100644 index 00000000..92cdb5bc --- /dev/null +++ b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithDoubleInBoundsConstraintImpl.java @@ -0,0 +1,99 @@ +package io.toolisticon.aptk.constraints; + +import io.toolisticon.aptk.compilermessage.api.DeclareCompilerMessage; +import io.toolisticon.aptk.tools.wrapper.AnnotationMirrorWrapper; +import io.toolisticon.aptk.tools.wrapper.AnnotationValueWrapper; +import io.toolisticon.aptk.tools.wrapper.ElementWrapper; +import io.toolisticon.spiap.api.SpiService; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import java.lang.annotation.Annotation; +import java.util.Optional; + +@SpiService(value = AnnotationConstraintSpi.class) +public class WithDoubleInBoundsConstraintImpl implements AnnotationConstraintSpi { + + @Override + public Class getSupportedAnnotation() { + return WithDoubleInBounds.class; + } + + + @DeclareCompilerMessage(code = "WITHDOUBLEBOUNDS_001", enumValueName = "WITHDOUBLEBOUNDS_ERROR_WRONG_USAGE", message = "'${0}' Constraint violated: Annotation attribute ${1} is out of bounds (${2})", processorClass = BasicConstraints.class) + @Override + public boolean checkConstraints(Element annotatedElement, AnnotationMirror annotationMirrorToCheck, AnnotationMirror constraintAnnotationMirror, String attributeNameToBeCheckedByConstraint) { + + ElementWrapper wrappedElement = ElementWrapper.wrap(annotatedElement); + WithDoubleInBoundsWrapper constraintWrapper = WithDoubleInBoundsWrapper.wrap(constraintAnnotationMirror); + + + Optional attribute = AnnotationMirrorWrapper.wrap(annotationMirrorToCheck).getAttribute(attributeNameToBeCheckedByConstraint); + + + if (attribute.isPresent()) { + + if (attribute.get().isArray()) { + + for (AnnotationValueWrapper annotationValueWrapper : attribute.get().getArrayValue()) { + if (!checkBounds(annotationValueWrapper, constraintWrapper)) { + wrappedElement.compilerMessage(annotationMirrorToCheck,attribute.get().unwrap()).asError().write(BasicConstraintsCompilerMessages.WITHDOUBLEBOUNDS_ERROR_WRONG_USAGE, UtilityFunctions.getSimpleName(constraintAnnotationMirror), attributeNameToBeCheckedByConstraint, createConstraintString(constraintWrapper)); + return false; + } + + } + + } else { + if (!checkBounds(attribute.get(), constraintWrapper)) { + wrappedElement.compilerMessage(annotationMirrorToCheck,attribute.get().unwrap()).asError().write(BasicConstraintsCompilerMessages.WITHDOUBLEBOUNDS_ERROR_WRONG_USAGE, UtilityFunctions.getSimpleName(constraintAnnotationMirror), attributeNameToBeCheckedByConstraint, createConstraintString(constraintWrapper)); + return false; + } + } + + + } + + + return true; + } + + private boolean checkBounds(AnnotationValueWrapper annotationValueWrapper, WithDoubleInBoundsWrapper constraintWrapper) { + + if (annotationValueWrapper.isDouble()) { + + Double value = annotationValueWrapper.getDoubleValue(); + + // check lower bound + if (!constraintWrapper.lowerBoundIsDefaultValue() + && ( + (constraintWrapper.inclusiveLowerBound() && value < constraintWrapper.lowerBound()) + || (!constraintWrapper.inclusiveLowerBound() && value <= constraintWrapper.lowerBound()) + )) { + return false; + } + + // check upper bound + if (!constraintWrapper.upperBoundIsDefaultValue() + && ( + (constraintWrapper.inclusiveUpperBound() && value > constraintWrapper.upperBound()) + || (!constraintWrapper.inclusiveUpperBound() && value >= constraintWrapper.upperBound()) + )) { + return false; + } + + + } + + return true; + + } + + private String createConstraintString(WithDoubleInBoundsWrapper constraintWrapper) { + + return (!constraintWrapper.lowerBoundIsDefaultValue() ? constraintWrapper.lowerBound() + " <" + (constraintWrapper.inclusiveLowerBound() ? "" : "=") + " " : "") + + " value " + + (!constraintWrapper.upperBoundIsDefaultValue() ? "<" + (constraintWrapper.inclusiveUpperBound() ? "" : "=") + " " + constraintWrapper.upperBound(): ""); + + + } +} diff --git a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithFloatInBoundsConstraintImpl.java b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithFloatInBoundsConstraintImpl.java new file mode 100644 index 00000000..409983f9 --- /dev/null +++ b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithFloatInBoundsConstraintImpl.java @@ -0,0 +1,99 @@ +package io.toolisticon.aptk.constraints; + +import io.toolisticon.aptk.compilermessage.api.DeclareCompilerMessage; +import io.toolisticon.aptk.tools.wrapper.AnnotationMirrorWrapper; +import io.toolisticon.aptk.tools.wrapper.AnnotationValueWrapper; +import io.toolisticon.aptk.tools.wrapper.ElementWrapper; +import io.toolisticon.spiap.api.SpiService; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import java.lang.annotation.Annotation; +import java.util.Optional; + +@SpiService(value = AnnotationConstraintSpi.class) +public class WithFloatInBoundsConstraintImpl implements AnnotationConstraintSpi { + + @Override + public Class getSupportedAnnotation() { + return WithFloatInBounds.class; + } + + + @DeclareCompilerMessage(code = "WITHFLOATBOUNDS_001", enumValueName = "WITHFLOATBOUNDS_ERROR_WRONG_USAGE", message = "'${0}' Constraint violated: Annotation attribute ${1} is out of bounds (${2})", processorClass = BasicConstraints.class) + @Override + public boolean checkConstraints(Element annotatedElement, AnnotationMirror annotationMirrorToCheck, AnnotationMirror constraintAnnotationMirror, String attributeNameToBeCheckedByConstraint) { + + ElementWrapper wrappedElement = ElementWrapper.wrap(annotatedElement); + WithFloatInBoundsWrapper constraintWrapper = WithFloatInBoundsWrapper.wrap(constraintAnnotationMirror); + + + Optional attribute = AnnotationMirrorWrapper.wrap(annotationMirrorToCheck).getAttribute(attributeNameToBeCheckedByConstraint); + + + if (attribute.isPresent()) { + + if (attribute.get().isArray()) { + + for (AnnotationValueWrapper annotationValueWrapper : attribute.get().getArrayValue()) { + if (!checkBounds(annotationValueWrapper, constraintWrapper)) { + wrappedElement.compilerMessage(annotationMirrorToCheck,attribute.get().unwrap()).asError().write(BasicConstraintsCompilerMessages.WITHFLOATBOUNDS_ERROR_WRONG_USAGE, UtilityFunctions.getSimpleName(constraintAnnotationMirror), attributeNameToBeCheckedByConstraint, createConstraintString(constraintWrapper)); + return false; + } + + } + + } else { + if (!checkBounds(attribute.get(), constraintWrapper)) { + wrappedElement.compilerMessage(annotationMirrorToCheck,attribute.get().unwrap()).asError().write(BasicConstraintsCompilerMessages.WITHFLOATBOUNDS_ERROR_WRONG_USAGE, UtilityFunctions.getSimpleName(constraintAnnotationMirror), attributeNameToBeCheckedByConstraint, createConstraintString(constraintWrapper)); + return false; + } + } + + + } + + + return true; + } + + private boolean checkBounds(AnnotationValueWrapper annotationValueWrapper, WithFloatInBoundsWrapper constraintWrapper) { + + if (annotationValueWrapper.isFloat()) { + + Float value = annotationValueWrapper.getFloatValue(); + + // check lower bound + if (!constraintWrapper.lowerBoundIsDefaultValue() + && ( + (constraintWrapper.inclusiveLowerBound() && value < constraintWrapper.lowerBound()) + || (!constraintWrapper.inclusiveLowerBound() && value <= constraintWrapper.lowerBound()) + )) { + return false; + } + + // check upper bound + if (!constraintWrapper.upperBoundIsDefaultValue() + && ( + (constraintWrapper.inclusiveUpperBound() && value > constraintWrapper.upperBound()) + || (!constraintWrapper.inclusiveUpperBound() && value >= constraintWrapper.upperBound()) + )) { + return false; + } + + + } + + return true; + + } + + private String createConstraintString(WithFloatInBoundsWrapper constraintWrapper) { + + return (!constraintWrapper.lowerBoundIsDefaultValue() ? constraintWrapper.lowerBound() + " <" + (constraintWrapper.inclusiveLowerBound() ? "" : "=") + " " : "") + + " value " + + (!constraintWrapper.upperBoundIsDefaultValue() ? "<" + (constraintWrapper.inclusiveUpperBound() ? "" : "=") + " " + constraintWrapper.upperBound(): ""); + + + } +} diff --git a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithIntegerInBoundsConstraintImpl.java b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithIntegerInBoundsConstraintImpl.java new file mode 100644 index 00000000..b44cea64 --- /dev/null +++ b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithIntegerInBoundsConstraintImpl.java @@ -0,0 +1,99 @@ +package io.toolisticon.aptk.constraints; + +import io.toolisticon.aptk.compilermessage.api.DeclareCompilerMessage; +import io.toolisticon.aptk.tools.wrapper.AnnotationMirrorWrapper; +import io.toolisticon.aptk.tools.wrapper.AnnotationValueWrapper; +import io.toolisticon.aptk.tools.wrapper.ElementWrapper; +import io.toolisticon.spiap.api.SpiService; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import java.lang.annotation.Annotation; +import java.util.Optional; + +@SpiService(value = AnnotationConstraintSpi.class) +public class WithIntegerInBoundsConstraintImpl implements AnnotationConstraintSpi { + + @Override + public Class getSupportedAnnotation() { + return WithIntegerInBounds.class; + } + + + @DeclareCompilerMessage(code = "WITHINTEGERBOUNDS_001", enumValueName = "WITHINTEGERBOUNDS_ERROR_WRONG_USAGE", message = "'${0}' Constraint violated: Annotation attribute ${1} is out of bounds (${2})", processorClass = BasicConstraints.class) + @Override + public boolean checkConstraints(Element annotatedElement, AnnotationMirror annotationMirrorToCheck, AnnotationMirror constraintAnnotationMirror, String attributeNameToBeCheckedByConstraint) { + + ElementWrapper wrappedElement = ElementWrapper.wrap(annotatedElement); + WithIntegerInBoundsWrapper constraintWrapper = WithIntegerInBoundsWrapper.wrap(constraintAnnotationMirror); + + + Optional attribute = AnnotationMirrorWrapper.wrap(annotationMirrorToCheck).getAttribute(attributeNameToBeCheckedByConstraint); + + + if (attribute.isPresent()) { + + if (attribute.get().isArray()) { + + for (AnnotationValueWrapper annotationValueWrapper : attribute.get().getArrayValue()) { + if (!checkBounds(annotationValueWrapper, constraintWrapper)) { + wrappedElement.compilerMessage(annotationMirrorToCheck,attribute.get().unwrap()).asError().write(BasicConstraintsCompilerMessages.WITHINTEGERBOUNDS_ERROR_WRONG_USAGE, UtilityFunctions.getSimpleName(constraintAnnotationMirror), attributeNameToBeCheckedByConstraint, createConstraintString(constraintWrapper)); + return false; + } + + } + + } else { + if (!checkBounds(attribute.get(), constraintWrapper)) { + wrappedElement.compilerMessage(annotationMirrorToCheck,attribute.get().unwrap()).asError().write(BasicConstraintsCompilerMessages.WITHINTEGERBOUNDS_ERROR_WRONG_USAGE, UtilityFunctions.getSimpleName(constraintAnnotationMirror), attributeNameToBeCheckedByConstraint, createConstraintString(constraintWrapper)); + return false; + } + } + + + } + + + return true; + } + + private boolean checkBounds(AnnotationValueWrapper annotationValueWrapper, WithIntegerInBoundsWrapper constraintWrapper) { + + if (annotationValueWrapper.isInteger()) { + + Integer value = annotationValueWrapper.getIntegerValue(); + + // check lower bound + if (!constraintWrapper.lowerBoundIsDefaultValue() + && ( + (constraintWrapper.inclusiveLowerBound() && value < constraintWrapper.lowerBound()) + || (!constraintWrapper.inclusiveLowerBound() && value <= constraintWrapper.lowerBound()) + )) { + return false; + } + + // check upper bound + if (!constraintWrapper.upperBoundIsDefaultValue() + && ( + (constraintWrapper.inclusiveUpperBound() && value > constraintWrapper.upperBound()) + || (!constraintWrapper.inclusiveUpperBound() && value >= constraintWrapper.upperBound()) + )) { + return false; + } + + + } + + return true; + + } + + private String createConstraintString(WithIntegerInBoundsWrapper constraintWrapper) { + + return (!constraintWrapper.lowerBoundIsDefaultValue() ? constraintWrapper.lowerBound() + " <" + (constraintWrapper.inclusiveLowerBound() ? "" : "=") + " " : "") + + " value " + + (!constraintWrapper.upperBoundIsDefaultValue() ? "<" + (constraintWrapper.inclusiveUpperBound() ? "" : "=") + " " + constraintWrapper.upperBound(): ""); + + + } +} diff --git a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithLongInBoundsConstraintImpl.java b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithLongInBoundsConstraintImpl.java new file mode 100644 index 00000000..a78d318c --- /dev/null +++ b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithLongInBoundsConstraintImpl.java @@ -0,0 +1,99 @@ +package io.toolisticon.aptk.constraints; + +import io.toolisticon.aptk.compilermessage.api.DeclareCompilerMessage; +import io.toolisticon.aptk.tools.wrapper.AnnotationMirrorWrapper; +import io.toolisticon.aptk.tools.wrapper.AnnotationValueWrapper; +import io.toolisticon.aptk.tools.wrapper.ElementWrapper; +import io.toolisticon.spiap.api.SpiService; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import java.lang.annotation.Annotation; +import java.util.Optional; + +@SpiService(value = AnnotationConstraintSpi.class) +public class WithLongInBoundsConstraintImpl implements AnnotationConstraintSpi { + + @Override + public Class getSupportedAnnotation() { + return WithLongInBounds.class; + } + + + @DeclareCompilerMessage(code = "WITHLONGBOUNDS_001", enumValueName = "WITHLONGBOUNDS_ERROR_WRONG_USAGE", message = "'${0}' Constraint violated: Annotation attribute ${1} is out of bounds (${2})", processorClass = BasicConstraints.class) + @Override + public boolean checkConstraints(Element annotatedElement, AnnotationMirror annotationMirrorToCheck, AnnotationMirror constraintAnnotationMirror, String attributeNameToBeCheckedByConstraint) { + + ElementWrapper wrappedElement = ElementWrapper.wrap(annotatedElement); + WithLongInBoundsWrapper constraintWrapper = WithLongInBoundsWrapper.wrap(constraintAnnotationMirror); + + + Optional attribute = AnnotationMirrorWrapper.wrap(annotationMirrorToCheck).getAttribute(attributeNameToBeCheckedByConstraint); + + + if (attribute.isPresent()) { + + if (attribute.get().isArray()) { + + for (AnnotationValueWrapper annotationValueWrapper : attribute.get().getArrayValue()) { + if (!checkBounds(annotationValueWrapper, constraintWrapper)) { + wrappedElement.compilerMessage(annotationMirrorToCheck,attribute.get().unwrap()).asError().write(BasicConstraintsCompilerMessages.WITHLONGBOUNDS_ERROR_WRONG_USAGE, UtilityFunctions.getSimpleName(constraintAnnotationMirror), attributeNameToBeCheckedByConstraint, createConstraintString(constraintWrapper)); + return false; + } + + } + + } else { + if (!checkBounds(attribute.get(), constraintWrapper)) { + wrappedElement.compilerMessage(annotationMirrorToCheck,attribute.get().unwrap()).asError().write(BasicConstraintsCompilerMessages.WITHLONGBOUNDS_ERROR_WRONG_USAGE, UtilityFunctions.getSimpleName(constraintAnnotationMirror), attributeNameToBeCheckedByConstraint, createConstraintString(constraintWrapper)); + return false; + } + } + + + } + + + return true; + } + + private boolean checkBounds(AnnotationValueWrapper annotationValueWrapper, WithLongInBoundsWrapper constraintWrapper) { + + if (annotationValueWrapper.isLong()) { + + Long value = annotationValueWrapper.getLongValue(); + + // check lower bound + if (!constraintWrapper.lowerBoundIsDefaultValue() + && ( + (constraintWrapper.inclusiveLowerBound() && value < constraintWrapper.lowerBound()) + || (!constraintWrapper.inclusiveLowerBound() && value <= constraintWrapper.lowerBound()) + )) { + return false; + } + + // check upper bound + if (!constraintWrapper.upperBoundIsDefaultValue() + && ( + (constraintWrapper.inclusiveUpperBound() && value > constraintWrapper.upperBound()) + || (!constraintWrapper.inclusiveUpperBound() && value >= constraintWrapper.upperBound()) + )) { + return false; + } + + + } + + return true; + + } + + private String createConstraintString(WithLongInBoundsWrapper constraintWrapper) { + + return (!constraintWrapper.lowerBoundIsDefaultValue() ? constraintWrapper.lowerBound() + " <" + (constraintWrapper.inclusiveLowerBound() ? "" : "=") + " " : "") + + " value " + + (!constraintWrapper.upperBoundIsDefaultValue() ? "<" + (constraintWrapper.inclusiveUpperBound() ? "" : "=") + " " + constraintWrapper.upperBound(): ""); + + + } +} diff --git a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/NonEmptyArrayConstraintImpl.java b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithNonEmptyArrayConstraintImpl.java similarity index 90% rename from constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/NonEmptyArrayConstraintImpl.java rename to constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithNonEmptyArrayConstraintImpl.java index 2abcd24f..42e69113 100644 --- a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/NonEmptyArrayConstraintImpl.java +++ b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithNonEmptyArrayConstraintImpl.java @@ -10,15 +10,13 @@ import javax.lang.model.element.Element; import java.lang.annotation.Annotation; import java.util.Optional; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; @SpiService(value = AnnotationConstraintSpi.class) -public class NonEmptyArrayConstraintImpl implements AnnotationConstraintSpi { +public class WithNonEmptyArrayConstraintImpl implements AnnotationConstraintSpi { @Override public Class getSupportedAnnotation() { - return NonEmptyArray.class; + return WithNonEmptyArray.class; } diff --git a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/NonEmptyStringConstraintImpl.java b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithNonEmptyStringConstraintImpl.java similarity index 95% rename from constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/NonEmptyStringConstraintImpl.java rename to constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithNonEmptyStringConstraintImpl.java index c550b1a7..50374f4b 100644 --- a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/NonEmptyStringConstraintImpl.java +++ b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithNonEmptyStringConstraintImpl.java @@ -12,11 +12,11 @@ import java.util.Optional; @SpiService(value = AnnotationConstraintSpi.class) -public class NonEmptyStringConstraintImpl implements AnnotationConstraintSpi { +public class WithNonEmptyStringConstraintImpl implements AnnotationConstraintSpi { @Override public Class getSupportedAnnotation() { - return NonEmptyString.class; + return WithNonEmptyString.class; } diff --git a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/StringMustMatchConstraintImpl.java b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithStringMatchingConstraintImpl.java similarity index 93% rename from constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/StringMustMatchConstraintImpl.java rename to constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithStringMatchingConstraintImpl.java index 42875f67..4c96e45e 100644 --- a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/StringMustMatchConstraintImpl.java +++ b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithStringMatchingConstraintImpl.java @@ -16,11 +16,11 @@ @SpiService(value = AnnotationConstraintSpi.class) @DeclareCompilerMessage(code = "STRINGMUSTMATCH_001", enumValueName = "STRINGMUSTMATCH_ERROR_WRONG_USAGE", message = "'${0}' Constraint violated: Annotation ${1} attribute ${2} value(s) must match regular expression: ${3}", processorClass = BasicConstraints.class) @DeclareCompilerMessage(code = "STRINGMUSTMATCH_002", enumValueName = "STRINGMUSTMATCH_ERROR_INVALID_PATTERN", message = "'${0}' Invalid Pattern '${1}' can't be compiled!", processorClass = BasicConstraints.class) -public class StringMustMatchConstraintImpl implements AnnotationConstraintSpi { +public class WithStringMatchingConstraintImpl implements AnnotationConstraintSpi { @Override public Class getSupportedAnnotation() { - return StringMustMatch.class; + return WithStringMatching.class; } @@ -28,7 +28,7 @@ public Class getSupportedAnnotation() { public boolean checkConstraints(Element annotatedElement, AnnotationMirror annotationMirrorToCheck, AnnotationMirror constraintAnnotationMirror, String attributeNameToBeCheckedByConstraint) { ElementWrapper wrappedElement = ElementWrapper.wrap(annotatedElement); - StringMustMatchWrapper constraintWrapper = StringMustMatchWrapper.wrap(constraintAnnotationMirror); + WithStringMatchingWrapper constraintWrapper = WithStringMatchingWrapper.wrap(constraintAnnotationMirror); Optional attribute = AnnotationMirrorWrapper.wrap(annotationMirrorToCheck).getAttribute(attributeNameToBeCheckedByConstraint); if (attribute.isPresent()) { diff --git a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAnnotatedWithConstraintImpl.java b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAnnotatedWithConstraintImpl.java similarity index 88% rename from constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAnnotatedWithConstraintImpl.java rename to constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAnnotatedWithConstraintImpl.java index 30cbda45..44f210a2 100644 --- a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAnnotatedWithConstraintImpl.java +++ b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAnnotatedWithConstraintImpl.java @@ -13,11 +13,11 @@ import java.util.stream.Collectors; @SpiService(value = AnnotationConstraintSpi.class) -public class TargetMustBeAnnotatedWithConstraintImpl implements AnnotationConstraintSpi { +public class WithTargetElementAnnotatedWithConstraintImpl implements AnnotationConstraintSpi { @Override public Class getSupportedAnnotation() { - return TargetMustBeAnnotatedWith.class; + return WithTargetElementAnnotatedWith.class; } @@ -28,11 +28,11 @@ public Class getSupportedAnnotation() { public boolean checkConstraints(Element annotatedElement, AnnotationMirror annotationMirrorToCheck, AnnotationMirror constraintAnnotationMirror, String attributeNameToBeCheckedByConstraint) { ElementWrapper wrappedElement = ElementWrapper.wrap(annotatedElement); - TargetMustBeAnnotatedWithWrapper constraintWrapper = TargetMustBeAnnotatedWithWrapper.wrap(annotatedElement, constraintAnnotationMirror); + WithTargetElementAnnotatedWithWrapper constraintWrapper = WithTargetElementAnnotatedWithWrapper.wrap(annotatedElement, constraintAnnotationMirror); ElementWrapper elementToCheck = null; - switch (constraintWrapper.target()) { + switch (constraintWrapper.targetElement()) { case ANNOTATED_ELEMENT: { elementToCheck = ElementWrapper.wrap(annotatedElement); @@ -50,7 +50,7 @@ public boolean checkConstraints(Element annotatedElement, AnnotationMirror annot if (result.size() == 1) { elementToCheck = result.get(0); } else { - wrappedElement.compilerMessage(annotationMirrorToCheck).asError().write(BasicConstraintsCompilerMessages.TARGETMUSTBEANNOTATEDWITH_ERROR_WRONG_USAGE, UtilityFunctions.getSimpleName(constraintAnnotationMirror), constraintWrapper.target()); + wrappedElement.compilerMessage(annotationMirrorToCheck).asError().write(BasicConstraintsCompilerMessages.TARGETMUSTBEANNOTATEDWITH_ERROR_WRONG_USAGE, UtilityFunctions.getSimpleName(constraintAnnotationMirror), constraintWrapper.targetElement()); } break; @@ -64,7 +64,7 @@ public boolean checkConstraints(Element annotatedElement, AnnotationMirror annot } if (elementToCheck == null) { - wrappedElement.compilerMessage(annotationMirrorToCheck).asError().write(BasicConstraintsCompilerMessages.TARGETMUSTBEANNOTATEDWITH_ERROR_WRONG_USAGE, UtilityFunctions.getSimpleName(constraintAnnotationMirror), constraintWrapper.target()); + wrappedElement.compilerMessage(annotationMirrorToCheck).asError().write(BasicConstraintsCompilerMessages.TARGETMUSTBEANNOTATEDWITH_ERROR_WRONG_USAGE, UtilityFunctions.getSimpleName(constraintAnnotationMirror), constraintWrapper.targetElement()); } break; diff --git a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAnnotatedWithRepeatableConstraintImpl.java b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAnnotatedWithRepeatableConstraintImpl.java new file mode 100644 index 00000000..42e589a2 --- /dev/null +++ b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAnnotatedWithRepeatableConstraintImpl.java @@ -0,0 +1,30 @@ +package io.toolisticon.aptk.constraints; + +import io.toolisticon.spiap.api.SpiService; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import java.lang.annotation.Annotation; + +@SpiService(value = AnnotationConstraintSpi.class) +public class WithTargetElementAnnotatedWithRepeatableConstraintImpl implements AnnotationConstraintSpi { + + @Override + public Class getSupportedAnnotation() { + return WithTargetElementAnnotatedWithRepeatable.class; + } + + @Override + public boolean checkConstraints(Element annotatedElement, AnnotationMirror annotationMirrorToCheck, AnnotationMirror constraintAnnotationMirror, String attributeNameToBeCheckedByConstraint) { + + WithTargetElementAnnotatedWithRepeatableWrapper constraintWrapper = WithTargetElementAnnotatedWithRepeatableWrapper.wrap(annotatedElement, constraintAnnotationMirror); + + boolean result = true; + + for (WithTargetElementAnnotatedWithWrapper constraint : constraintWrapper.value()) { + result = result & new WithTargetElementAnnotatedWithConstraintImpl().checkConstraints(annotatedElement, annotationMirrorToCheck, constraint._annotationMirror(),attributeNameToBeCheckedByConstraint); + } + + return result; + } +} \ No newline at end of file diff --git a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAssignableToConstraintImpl.java b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAssignableToConstraintImpl.java similarity index 94% rename from constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAssignableToConstraintImpl.java rename to constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAssignableToConstraintImpl.java index 3c597a13..c63e046c 100644 --- a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/TargetMustBeAssignableToConstraintImpl.java +++ b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithTargetElementAssignableToConstraintImpl.java @@ -15,11 +15,11 @@ import java.util.stream.Collectors; @SpiService(value = AnnotationConstraintSpi.class) -public class TargetMustBeAssignableToConstraintImpl implements AnnotationConstraintSpi { +public class WithTargetElementAssignableToConstraintImpl implements AnnotationConstraintSpi { @Override public Class getSupportedAnnotation() { - return TargetMustBeAssignableTo.class; + return WithTargetElementAssignableTo.class; } @@ -30,7 +30,7 @@ public Class getSupportedAnnotation() { public boolean checkConstraints(Element annotatedElement, AnnotationMirror annotationMirrorToCheck, AnnotationMirror constraintAnnotationMirror, String attributeNameToBeCheckedByConstraint) { ElementWrapper wrappedElement = ElementWrapper.wrap(annotatedElement); - TargetMustBeAssignableToWrapper constraintWrapper = TargetMustBeAssignableToWrapper.wrap(annotatedElement, constraintAnnotationMirror); + WithTargetElementAssignableToWrapper constraintWrapper = WithTargetElementAssignableToWrapper.wrap(annotatedElement, constraintAnnotationMirror); Optional attribute = AnnotationMirrorWrapper.wrap(annotationMirrorToCheck).getAttribute(attributeNameToBeCheckedByConstraint); diff --git a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/OnConstraintImpl.java b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithTargetOfKindConstraintImpl.java similarity index 93% rename from constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/OnConstraintImpl.java rename to constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithTargetOfKindConstraintImpl.java index 962c0a33..2bb0ffee 100644 --- a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/OnConstraintImpl.java +++ b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/WithTargetOfKindConstraintImpl.java @@ -22,11 +22,11 @@ @DeclareCompilerMessage(code = "ON_002", enumValueName = "ON_ERROR_TARGET_ANNOTATION_NOT_FOUND", message = "No target annotation has been found. Please either remove On Constraint annotation or add matching Target annotation.", processorClass = BasicConstraints.class) @DeclareCompilerMessage(code = "ON_003", enumValueName = "ON_ERROR_WRONG_USAGE", message = "'${0}' Constraint violated: Annotation ${1} must be placed on either ${2}", processorClass = BasicConstraints.class) -public class OnConstraintImpl implements AnnotationConstraintSpi, ManualConstraintSpi { +public class WithTargetOfKindConstraintImpl implements AnnotationConstraintSpi, ManualConstraintSpi { @Override public Class getSupportedAnnotation() { - return On.class; + return WithTargetOfKind.class; } @Override @@ -40,17 +40,17 @@ public void checkManualConstraints(Element annotatedElement, AnnotationMirror co // Now check if annotation value matches Target target = annotatedElement.getAnnotation(Target.class); - OnWrapper onAnnotation = OnWrapper.wrap(constraintAnnotationMirror); + WithTargetOfKindWrapper onAnnotation = WithTargetOfKindWrapper.wrap(constraintAnnotationMirror); if (target != null) { - for (On.Location location : onAnnotation.value()) { + for (WithTargetOfKind.TargetKind targetKind : onAnnotation.value()) { boolean foundLocation = false; for (ElementType elementType : target.value()) { - switch (location) { + switch (targetKind) { case CLASS: case INTERFACE: case ENUM: { @@ -108,7 +108,7 @@ public void checkManualConstraints(Element annotatedElement, AnnotationMirror co } if (!foundLocation) { - MessagerUtils.error(annotatedElement, constraintAnnotationMirror, BasicConstraintsCompilerMessages.ON_ERROR_MATCHING_TARGET_NOT_FOUND, location.toString()); + MessagerUtils.error(annotatedElement, constraintAnnotationMirror, BasicConstraintsCompilerMessages.ON_ERROR_MATCHING_TARGET_NOT_FOUND, targetKind.toString()); } } @@ -127,12 +127,12 @@ public boolean checkConstraints(Element annotatedElement, AnnotationMirror annot // Now check if annotation - OnWrapper onAnnotation = OnWrapper.wrap(constraintAnnotationMirror); + WithTargetOfKindWrapper onAnnotation = WithTargetOfKindWrapper.wrap(constraintAnnotationMirror); boolean foundMatchingElementType = false; - for (On.Location location : onAnnotation.value()) { + for (WithTargetOfKind.TargetKind targetKind : onAnnotation.value()) { - switch (location) { + switch (targetKind) { case ANNOTATION_ATTRIBUTE: { // must be placed on method of annotation type if (ElementUtils.CheckKindOfElement.isMethod(annotatedElement) && ElementUtils.CheckKindOfElement.isAnnotation(annotatedElement.getEnclosingElement())) { diff --git a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/package-info.java b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/package-info.java index 01c2e964..40e170e1 100644 --- a/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/package-info.java +++ b/constraints/constraint-impl/src/main/java/io/toolisticon/aptk/constraints/package-info.java @@ -3,12 +3,16 @@ */ @AnnotationWrapper( value = { - On.class, - OnAnnotationAttributeOfType.class, - StringMustMatch.class, - TargetMustBeAnnotatedWith.class, - TargetMustBeAnnotatedWiths.class, - TargetMustBeAssignableTo.class + WithTargetOfKind.class, + WithAnnotationAttributeTargetOfType.class, + WithStringMatching.class, + WithTargetElementAnnotatedWith.class, + WithTargetElementAnnotatedWithRepeatable.class, + WithTargetElementAssignableTo.class, + WithIntegerInBounds.class, + WithLongInBounds.class, + WithFloatInBounds.class, + WithDoubleInBounds.class }) package io.toolisticon.aptk.constraints; diff --git a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/BasicConstraintsTest.java b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/BasicConstraintsTest.java index 75e66df9..2eb8d8f4 100644 --- a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/BasicConstraintsTest.java +++ b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/BasicConstraintsTest.java @@ -2,12 +2,15 @@ import io.toolisticon.aptk.common.ToolingProvider; import io.toolisticon.aptk.tools.MessagerUtils; +import io.toolisticon.aptk.tools.TypeMirrorWrapper; import io.toolisticon.cute.Cute; import io.toolisticon.cute.PassIn; import org.hamcrest.MatcherAssert; import org.junit.Before; import org.junit.Test; +import javax.lang.model.element.TypeElement; + public class BasicConstraintsTest { @Before diff --git a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/JustOnInterfacesAnnotation.java b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/JustOnInterfacesAnnotation.java index 63c91c79..83b31ca3 100644 --- a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/JustOnInterfacesAnnotation.java +++ b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/JustOnInterfacesAnnotation.java @@ -7,7 +7,7 @@ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) -@On(On.Location.INTERFACE) +@WithTargetOfKind(WithTargetOfKind.TargetKind.INTERFACE) public @interface JustOnInterfacesAnnotation { } diff --git a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/JustOnStringAttributeAndIntegers.java b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/JustOnStringAttributeAndIntegers.java index a87a8f13..4ee19fb7 100644 --- a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/JustOnStringAttributeAndIntegers.java +++ b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/JustOnStringAttributeAndIntegers.java @@ -1,6 +1,6 @@ package io.toolisticon.aptk.constraints; -@OnAnnotationAttributeOfType({OnAnnotationAttributeOfType.AttributeType.STRING, OnAnnotationAttributeOfType.AttributeType.INTEGER}) +@WithAnnotationAttributeTargetOfType({WithAnnotationAttributeTargetOfType.AttributeType.STRING, WithAnnotationAttributeTargetOfType.AttributeType.INTEGER}) public @interface JustOnStringAttributeAndIntegers { } diff --git a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/NonEmptyArrayConstraintsTest.java b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/NonEmptyArrayConstraintsTest.java index c3f1f479..0e7b63fb 100644 --- a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/NonEmptyArrayConstraintsTest.java +++ b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/NonEmptyArrayConstraintsTest.java @@ -18,7 +18,7 @@ public void init() { @interface ArrayMustBeNonEmpty { - @NonEmptyArray String[] value(); + @WithNonEmptyArray String[] value(); } diff --git a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithDoubleInBoundsConstraintsTest.java b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithDoubleInBoundsConstraintsTest.java new file mode 100644 index 00000000..66e68c9f --- /dev/null +++ b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithDoubleInBoundsConstraintsTest.java @@ -0,0 +1,228 @@ +package io.toolisticon.aptk.constraints; + +import io.toolisticon.aptk.common.ToolingProvider; +import io.toolisticon.aptk.tools.MessagerUtils; +import io.toolisticon.cute.Cute; +import io.toolisticon.cute.PassIn; +import org.hamcrest.MatcherAssert; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit test for {@link WithDoubleInBoundsConstraintImpl}. + */ +public class WithDoubleInBoundsConstraintsTest { + + @Before + public void init() { + MessagerUtils.setPrintMessageCodes(true); + } + + @interface WithInclusiveUpperAndLowerBound { + + @WithDoubleInBounds(lowerBound = 5.0, upperBound = 10.0) double value(); + + } + + @PassIn + @WithInclusiveUpperAndLowerBound(8.0) + static class WithInclusiveUpperAndLowerBound_mid_Match { + + } + + @PassIn + @WithInclusiveUpperAndLowerBound(5.0) + static class WithInclusiveUpperAndLowerBound_low_Match { + + } + + @PassIn + @WithInclusiveUpperAndLowerBound(10.0) + static class WithInclusiveUpperAndLowerBound_up_Match { + + } + + @Test + public void inclusive_mid_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithInclusiveUpperAndLowerBound_mid_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @Test + public void inclusive_low_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithInclusiveUpperAndLowerBound_low_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @Test + public void inclusive_up_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithInclusiveUpperAndLowerBound_up_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @interface WithExclusiveUpperAndLowerBound { + + @WithDoubleInBounds(lowerBound = 5.0, inclusiveLowerBound = false, upperBound = 10.0, inclusiveUpperBound = false) double value(); + + } + + @PassIn + @WithExclusiveUpperAndLowerBound(8.0) + static class WithExclusiveUpperAndLowerBound_mid_Match { + + } + + @PassIn + @WithExclusiveUpperAndLowerBound(5.0) + static class WithExclusiveUpperAndLowerBound_low_Match { + + } + + @PassIn + @WithExclusiveUpperAndLowerBound(10.0) + static class WithExclusiveUpperAndLowerBound_up_Match { + + } + + @Test + public void exclusive_mid_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithExclusiveUpperAndLowerBound_mid_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @Test + public void exclusive_low_failingValidation() { + Cute.unitTest().when().passInElement().fromClass(WithExclusiveUpperAndLowerBound_low_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect false", !BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationFails() + .andThat().compilerMessage().ofKindError().contains(BasicConstraintsCompilerMessages.WITHDOUBLEBOUNDS_ERROR_WRONG_USAGE.getCode()) + .executeTest(); + } + + @Test + public void exclusive_up_failingValidation() { + Cute.unitTest().when().passInElement().fromClass(WithExclusiveUpperAndLowerBound_up_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect false", !BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationFails() + .andThat().compilerMessage().ofKindError().contains(BasicConstraintsCompilerMessages.WITHDOUBLEBOUNDS_ERROR_WRONG_USAGE.getCode()) + .executeTest(); + } + + @interface ArrayWithExclusiveUpperAndLowerBound { + + @WithDoubleInBounds(lowerBound = 5.0, inclusiveLowerBound = false, upperBound = 10.0, inclusiveUpperBound = false) double[] value(); + + } + + @PassIn + @ArrayWithExclusiveUpperAndLowerBound({8.0,7.0,6.0}) + static class ArrayWithExclusiveUpperAndLowerBound_mid_Match { + + } + + @PassIn + @ArrayWithExclusiveUpperAndLowerBound({5.0,9.0,8.0}) + static class ArrayWithExclusiveUpperAndLowerBound_low_Match { + + } + + @PassIn + @ArrayWithExclusiveUpperAndLowerBound({10.0,7.0,8.0}) + static class ArrayWithExclusiveUpperAndLowerBound_up_Match { + + } + + @Test + public void array_exclusive_mid_happyPath() { + Cute.unitTest().when().passInElement().fromClass(ArrayWithExclusiveUpperAndLowerBound_mid_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @Test + public void array_exclusive_low_failingValidation() { + Cute.unitTest().when().passInElement().fromClass(ArrayWithExclusiveUpperAndLowerBound_low_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect false", !BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationFails() + .andThat().compilerMessage().ofKindError().contains(BasicConstraintsCompilerMessages.WITHDOUBLEBOUNDS_ERROR_WRONG_USAGE.getCode()) + .executeTest(); + } + + @Test + public void array_exclusive_up_failingValidation() { + Cute.unitTest().when().passInElement().fromClass(ArrayWithExclusiveUpperAndLowerBound_up_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect false", !BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationFails() + .andThat().compilerMessage().ofKindError().contains(BasicConstraintsCompilerMessages.WITHDOUBLEBOUNDS_ERROR_WRONG_USAGE.getCode()) + .executeTest(); + } + + +} diff --git a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithFloatInBoundsConstraintsTest.java b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithFloatInBoundsConstraintsTest.java new file mode 100644 index 00000000..5baf43c7 --- /dev/null +++ b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithFloatInBoundsConstraintsTest.java @@ -0,0 +1,228 @@ +package io.toolisticon.aptk.constraints; + +import io.toolisticon.aptk.common.ToolingProvider; +import io.toolisticon.aptk.tools.MessagerUtils; +import io.toolisticon.cute.Cute; +import io.toolisticon.cute.PassIn; +import org.hamcrest.MatcherAssert; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit test for {@link WithFloatInBoundsConstraintImpl}. + */ +public class WithFloatInBoundsConstraintsTest { + + @Before + public void init() { + MessagerUtils.setPrintMessageCodes(true); + } + + @interface WithInclusiveUpperAndLowerBound { + + @WithFloatInBounds(lowerBound = 5.0f, upperBound = 10.0f) float value(); + + } + + @PassIn + @WithInclusiveUpperAndLowerBound(8.0f) + static class WithInclusiveUpperAndLowerBound_mid_Match { + + } + + @PassIn + @WithInclusiveUpperAndLowerBound(5.0f) + static class WithInclusiveUpperAndLowerBound_low_Match { + + } + + @PassIn + @WithInclusiveUpperAndLowerBound(10.0f) + static class WithInclusiveUpperAndLowerBound_up_Match { + + } + + @Test + public void inclusive_mid_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithInclusiveUpperAndLowerBound_mid_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @Test + public void inclusive_low_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithInclusiveUpperAndLowerBound_low_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @Test + public void inclusive_up_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithInclusiveUpperAndLowerBound_up_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @interface WithExclusiveUpperAndLowerBound { + + @WithFloatInBounds(lowerBound = 5.0f, inclusiveLowerBound = false, upperBound = 10.0f, inclusiveUpperBound = false) float value(); + + } + + @PassIn + @WithExclusiveUpperAndLowerBound(8.0f) + static class WithExclusiveUpperAndLowerBound_mid_Match { + + } + + @PassIn + @WithExclusiveUpperAndLowerBound(5.0f) + static class WithExclusiveUpperAndLowerBound_low_Match { + + } + + @PassIn + @WithExclusiveUpperAndLowerBound(10.0f) + static class WithExclusiveUpperAndLowerBound_up_Match { + + } + + @Test + public void exclusive_mid_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithExclusiveUpperAndLowerBound_mid_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @Test + public void exclusive_low_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithExclusiveUpperAndLowerBound_low_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect false", !BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationFails() + .andThat().compilerMessage().ofKindError().contains(BasicConstraintsCompilerMessages.WITHFLOATBOUNDS_ERROR_WRONG_USAGE.getCode()) + .executeTest(); + } + + @Test + public void exclusive_up_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithExclusiveUpperAndLowerBound_up_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect false", !BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationFails() + .andThat().compilerMessage().ofKindError().contains(BasicConstraintsCompilerMessages.WITHFLOATBOUNDS_ERROR_WRONG_USAGE.getCode()) + .executeTest(); + } + + @interface ArrayWithExclusiveUpperAndLowerBound { + + @WithFloatInBounds(lowerBound = 5.0f, inclusiveLowerBound = false, upperBound = 10.0f, inclusiveUpperBound = false) float[] value(); + + } + + @PassIn + @ArrayWithExclusiveUpperAndLowerBound({8.0f,7.0f,6.0f}) + static class ArrayWithExclusiveUpperAndLowerBound_mid_Match { + + } + + @PassIn + @ArrayWithExclusiveUpperAndLowerBound({5.0f,9.0f,8.0f}) + static class ArrayWithExclusiveUpperAndLowerBound_low_Match { + + } + + @PassIn + @ArrayWithExclusiveUpperAndLowerBound({10.0f,7.0f,8.0f}) + static class ArrayWithExclusiveUpperAndLowerBound_up_Match { + + } + + @Test + public void array_exclusive_mid_happyPath() { + Cute.unitTest().when().passInElement().fromClass(ArrayWithExclusiveUpperAndLowerBound_mid_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @Test + public void array_exclusive_low_happyPath() { + Cute.unitTest().when().passInElement().fromClass(ArrayWithExclusiveUpperAndLowerBound_low_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect false", !BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationFails() + .andThat().compilerMessage().ofKindError().contains(BasicConstraintsCompilerMessages.WITHFLOATBOUNDS_ERROR_WRONG_USAGE.getCode()) + .executeTest(); + } + + @Test + public void array_exclusive_up_happyPath() { + Cute.unitTest().when().passInElement().fromClass(ArrayWithExclusiveUpperAndLowerBound_up_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect false", !BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationFails() + .andThat().compilerMessage().ofKindError().contains(BasicConstraintsCompilerMessages.WITHFLOATBOUNDS_ERROR_WRONG_USAGE.getCode()) + .executeTest(); + } + + +} diff --git a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithIntegerInBoundsConstraintsTest.java b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithIntegerInBoundsConstraintsTest.java new file mode 100644 index 00000000..4b3488f6 --- /dev/null +++ b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithIntegerInBoundsConstraintsTest.java @@ -0,0 +1,228 @@ +package io.toolisticon.aptk.constraints; + +import io.toolisticon.aptk.common.ToolingProvider; +import io.toolisticon.aptk.tools.MessagerUtils; +import io.toolisticon.cute.Cute; +import io.toolisticon.cute.PassIn; +import org.hamcrest.MatcherAssert; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit test for {@link WithIntegerInBoundsConstraintImpl}. + */ +public class WithIntegerInBoundsConstraintsTest { + + @Before + public void init() { + MessagerUtils.setPrintMessageCodes(true); + } + + @interface WithInclusiveUpperAndLowerBound { + + @WithIntegerInBounds(lowerBound = 5, upperBound = 10) int value(); + + } + + @PassIn + @WithInclusiveUpperAndLowerBound(8) + static class WithInclusiveUpperAndLowerBound_mid_Match { + + } + + @PassIn + @WithInclusiveUpperAndLowerBound(5) + static class WithInclusiveUpperAndLowerBound_low_Match { + + } + + @PassIn + @WithInclusiveUpperAndLowerBound(10) + static class WithInclusiveUpperAndLowerBound_up_Match { + + } + + @Test + public void inclusive_mid_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithInclusiveUpperAndLowerBound_mid_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @Test + public void inclusive_low_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithInclusiveUpperAndLowerBound_low_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @Test + public void inclusive_up_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithInclusiveUpperAndLowerBound_up_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @interface WithExclusiveUpperAndLowerBound { + + @WithIntegerInBounds(lowerBound = 5, inclusiveLowerBound = false, upperBound = 10, inclusiveUpperBound = false) int value(); + + } + + @PassIn + @WithExclusiveUpperAndLowerBound(8) + static class WithExclusiveUpperAndLowerBound_mid_Match { + + } + + @PassIn + @WithExclusiveUpperAndLowerBound(5) + static class WithExclusiveUpperAndLowerBound_low_Match { + + } + + @PassIn + @WithExclusiveUpperAndLowerBound(10) + static class WithExclusiveUpperAndLowerBound_up_Match { + + } + + @Test + public void exclusive_mid_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithExclusiveUpperAndLowerBound_mid_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @Test + public void exclusive_low_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithExclusiveUpperAndLowerBound_low_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect false", !BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationFails() + .andThat().compilerMessage().ofKindError().contains(BasicConstraintsCompilerMessages.WITHINTEGERBOUNDS_ERROR_WRONG_USAGE.getCode()) + .executeTest(); + } + + @Test + public void exclusive_up_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithExclusiveUpperAndLowerBound_up_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect false", !BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationFails() + .andThat().compilerMessage().ofKindError().contains(BasicConstraintsCompilerMessages.WITHINTEGERBOUNDS_ERROR_WRONG_USAGE.getCode()) + .executeTest(); + } + + @interface ArrayWithExclusiveUpperAndLowerBound { + + @WithIntegerInBounds(lowerBound = 5, inclusiveLowerBound = false, upperBound = 10, inclusiveUpperBound = false) int[] value(); + + } + + @PassIn + @ArrayWithExclusiveUpperAndLowerBound({8,7,6}) + static class ArrayWithExclusiveUpperAndLowerBound_mid_Match { + + } + + @PassIn + @ArrayWithExclusiveUpperAndLowerBound({5,9,8}) + static class ArrayWithExclusiveUpperAndLowerBound_low_Match { + + } + + @PassIn + @ArrayWithExclusiveUpperAndLowerBound({10,7,8}) + static class ArrayWithExclusiveUpperAndLowerBound_up_Match { + + } + + @Test + public void array_exclusive_mid_happyPath() { + Cute.unitTest().when().passInElement().fromClass(ArrayWithExclusiveUpperAndLowerBound_mid_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @Test + public void array_exclusive_low_happyPath() { + Cute.unitTest().when().passInElement().fromClass(ArrayWithExclusiveUpperAndLowerBound_low_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect false", !BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationFails() + .andThat().compilerMessage().ofKindError().contains(BasicConstraintsCompilerMessages.WITHINTEGERBOUNDS_ERROR_WRONG_USAGE.getCode()) + .executeTest(); + } + + @Test + public void array_exclusive_up_happyPath() { + Cute.unitTest().when().passInElement().fromClass(ArrayWithExclusiveUpperAndLowerBound_up_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect false", !BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationFails() + .andThat().compilerMessage().ofKindError().contains(BasicConstraintsCompilerMessages.WITHINTEGERBOUNDS_ERROR_WRONG_USAGE.getCode()) + .executeTest(); + } + + +} diff --git a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithLongInBoundsConstraintsTest.java b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithLongInBoundsConstraintsTest.java new file mode 100644 index 00000000..cbf09ff4 --- /dev/null +++ b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithLongInBoundsConstraintsTest.java @@ -0,0 +1,228 @@ +package io.toolisticon.aptk.constraints; + +import io.toolisticon.aptk.common.ToolingProvider; +import io.toolisticon.aptk.tools.MessagerUtils; +import io.toolisticon.cute.Cute; +import io.toolisticon.cute.PassIn; +import org.hamcrest.MatcherAssert; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit test for {@link WithLongInBoundsConstraintImpl}. + */ +public class WithLongInBoundsConstraintsTest { + + @Before + public void init() { + MessagerUtils.setPrintMessageCodes(true); + } + + @interface WithInclusiveUpperAndLowerBound { + + @WithLongInBounds(lowerBound = 5, upperBound = 10) long value(); + + } + + @PassIn + @WithInclusiveUpperAndLowerBound(8) + static class WithInclusiveUpperAndLowerBound_mid_Match { + + } + + @PassIn + @WithInclusiveUpperAndLowerBound(5) + static class WithInclusiveUpperAndLowerBound_low_Match { + + } + + @PassIn + @WithInclusiveUpperAndLowerBound(10) + static class WithInclusiveUpperAndLowerBound_up_Match { + + } + + @Test + public void inclusive_mid_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithInclusiveUpperAndLowerBound_mid_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @Test + public void inclusive_low_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithInclusiveUpperAndLowerBound_low_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @Test + public void inclusive_up_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithInclusiveUpperAndLowerBound_up_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @interface WithExclusiveUpperAndLowerBound { + + @WithLongInBounds(lowerBound = 5, inclusiveLowerBound = false, upperBound = 10, inclusiveUpperBound = false) long value(); + + } + + @PassIn + @WithExclusiveUpperAndLowerBound(8) + static class WithExclusiveUpperAndLowerBound_mid_Match { + + } + + @PassIn + @WithExclusiveUpperAndLowerBound(5) + static class WithExclusiveUpperAndLowerBound_low_Match { + + } + + @PassIn + @WithExclusiveUpperAndLowerBound(10) + static class WithExclusiveUpperAndLowerBound_up_Match { + + } + + @Test + public void exclusive_mid_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithExclusiveUpperAndLowerBound_mid_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @Test + public void exclusive_low_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithExclusiveUpperAndLowerBound_low_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect false", !BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationFails() + .andThat().compilerMessage().ofKindError().contains(BasicConstraintsCompilerMessages.WITHLONGBOUNDS_ERROR_WRONG_USAGE.getCode()) + .executeTest(); + } + + @Test + public void exclusive_up_happyPath() { + Cute.unitTest().when().passInElement().fromClass(WithExclusiveUpperAndLowerBound_up_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect false", !BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationFails() + .andThat().compilerMessage().ofKindError().contains(BasicConstraintsCompilerMessages.WITHLONGBOUNDS_ERROR_WRONG_USAGE.getCode()) + .executeTest(); + } + + @interface ArrayWithExclusiveUpperAndLowerBound { + + @WithLongInBounds(lowerBound = 5, inclusiveLowerBound = false, upperBound = 10, inclusiveUpperBound = false) long[] value(); + + } + + @PassIn + @ArrayWithExclusiveUpperAndLowerBound({8,7,6}) + static class ArrayWithExclusiveUpperAndLowerBound_mid_Match { + + } + + @PassIn + @ArrayWithExclusiveUpperAndLowerBound({5,9,8}) + static class ArrayWithExclusiveUpperAndLowerBound_low_Match { + + } + + @PassIn + @ArrayWithExclusiveUpperAndLowerBound({10,7,8}) + static class ArrayWithExclusiveUpperAndLowerBound_up_Match { + + } + + @Test + public void array_exclusive_mid_happyPath() { + Cute.unitTest().when().passInElement().fromClass(ArrayWithExclusiveUpperAndLowerBound_mid_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect true", BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + } + + @Test + public void array_exclusive_low_happyPath() { + Cute.unitTest().when().passInElement().fromClass(ArrayWithExclusiveUpperAndLowerBound_low_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect false", !BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationFails() + .andThat().compilerMessage().ofKindError().contains(BasicConstraintsCompilerMessages.WITHLONGBOUNDS_ERROR_WRONG_USAGE.getCode()) + .executeTest(); + } + + @Test + public void array_exclusive_up_happyPath() { + Cute.unitTest().when().passInElement().fromClass(ArrayWithExclusiveUpperAndLowerBound_up_Match.class).intoUnitTest((procEnv, e) -> { + + ToolingProvider.setTooling(procEnv); + try { + MatcherAssert.assertThat("Expect false", !BasicConstraints.checkConstraints(e)); + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationFails() + .andThat().compilerMessage().ofKindError().contains(BasicConstraintsCompilerMessages.WITHLONGBOUNDS_ERROR_WRONG_USAGE.getCode()) + .executeTest(); + } + + +} diff --git a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/NonEmptyStringConstraintsTest.java b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithNonEmptyStringConstraintsTest.java similarity index 96% rename from constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/NonEmptyStringConstraintsTest.java rename to constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithNonEmptyStringConstraintsTest.java index 659e97fb..a9295a00 100644 --- a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/NonEmptyStringConstraintsTest.java +++ b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithNonEmptyStringConstraintsTest.java @@ -8,7 +8,7 @@ import org.junit.Before; import org.junit.Test; -public class NonEmptyStringConstraintsTest { +public class WithNonEmptyStringConstraintsTest { @Before public void init() { @@ -18,7 +18,7 @@ public void init() { @interface StringMustBeNonEmpty { - @NonEmptyString String value(); + @WithNonEmptyString String value(); } @@ -66,7 +66,7 @@ public void nonEmptyString_failure() { @interface StringMustBeNonEmpty_InArray { - @NonEmptyString String[] value(); + @WithNonEmptyString String[] value(); } diff --git a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/StringMustMatchConstraintsTest.java b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithStringMatchingConstraintsTest.java similarity index 93% rename from constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/StringMustMatchConstraintsTest.java rename to constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithStringMatchingConstraintsTest.java index 46a9a685..e6baf89f 100644 --- a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/StringMustMatchConstraintsTest.java +++ b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithStringMatchingConstraintsTest.java @@ -10,7 +10,7 @@ import java.io.Serializable; -public class StringMustMatchConstraintsTest implements Serializable { +public class WithStringMatchingConstraintsTest implements Serializable { @Before public void init() { @@ -20,7 +20,7 @@ public void init() { @interface TestStringMustMatch { - @StringMustMatch("A.+Z") String value(); + @WithStringMatching("A.+Z") String value(); } diff --git a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/TargetMustBeAnnotatedWithConstraintsTest.java b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithTargetElementAnnotatedWithConstraintsTest.java similarity index 86% rename from constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/TargetMustBeAnnotatedWithConstraintsTest.java rename to constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithTargetElementAnnotatedWithConstraintsTest.java index e4d206cb..17eeede6 100644 --- a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/TargetMustBeAnnotatedWithConstraintsTest.java +++ b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithTargetElementAnnotatedWithConstraintsTest.java @@ -11,8 +11,8 @@ import java.io.Serializable; -@TargetMustBeAnnotatedWithConstraintsTest.AnotherTopLevelAccompanyingAnnotation -public class TargetMustBeAnnotatedWithConstraintsTest implements Serializable{ +@WithTargetElementAnnotatedWithConstraintsTest.AnotherTopLevelAccompanyingAnnotation +public class WithTargetElementAnnotatedWithConstraintsTest implements Serializable{ @Before public void init() { @@ -24,7 +24,7 @@ public void init() { } - @TargetMustBeAnnotatedWith(value = AccompanyingAnnotation.class) + @WithTargetElementAnnotatedWith(value = AccompanyingAnnotation.class) @interface AnnotatedElementMustBeAnnotatedWith { } @@ -80,8 +80,8 @@ public void annotatedElementMustBeAssignable_failure() { } - @TargetMustBeAnnotatedWith(value = AccompanyingAnnotation.class) - @TargetMustBeAnnotatedWith(value = AnotherTopLevelAccompanyingAnnotation.class, target = TargetMustBeAnnotatedWith.TargetElement.TOP_LEVEL_TYPE_ELEMENT) + @WithTargetElementAnnotatedWith(value = AccompanyingAnnotation.class) + @WithTargetElementAnnotatedWith(value = AnotherTopLevelAccompanyingAnnotation.class, targetElement = TargetElement.TOP_LEVEL_TYPE_ELEMENT) @interface RepeatableMustBeAnnotatedWith { } diff --git a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/TargetMustBeAssignableToConstraintsTest.java b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithTargetElementAssignableToConstraintsTest.java similarity index 92% rename from constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/TargetMustBeAssignableToConstraintsTest.java rename to constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithTargetElementAssignableToConstraintsTest.java index 6cec6a67..ff552566 100644 --- a/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/TargetMustBeAssignableToConstraintsTest.java +++ b/constraints/constraint-impl/src/test/java/io/toolisticon/aptk/constraints/WithTargetElementAssignableToConstraintsTest.java @@ -10,7 +10,7 @@ import java.io.Serializable; -public class TargetMustBeAssignableToConstraintsTest implements Serializable{ +public class WithTargetElementAssignableToConstraintsTest implements Serializable{ @Before public void init() { @@ -20,7 +20,7 @@ public void init() { @interface AnnotatedElementMustBeAssignableTo { - @TargetMustBeAssignableTo(targetElement = TargetMustBeAssignableTo.TargetElement.ANNOTATED_ELEMENT) Class value(); + @WithTargetElementAssignableTo(targetElement = TargetElement.ANNOTATED_ELEMENT) Class value(); } @@ -76,7 +76,7 @@ public void annotatedElementMustBeAssignable_failure() { @interface TopLevelTypeElementMustBeAssignableTo { - @TargetMustBeAssignableTo(targetElement = TargetMustBeAssignableTo.TargetElement.TOP_LEVEL_TYPE_ELEMENT) Class value(); + @WithTargetElementAssignableTo(targetElement = TargetElement.TOP_LEVEL_TYPE_ELEMENT) Class value(); } @@ -125,7 +125,7 @@ public void topLevelTypeElementMustBeAssignable_failure() { @interface EnclosingTypeElementMustBeAssignableTo { - @TargetMustBeAssignableTo(targetElement = TargetMustBeAssignableTo.TargetElement.PARENT_TYPE_ELEMENT) Class value(); + @WithTargetElementAssignableTo(targetElement = TargetElement.PARENT_TYPE_ELEMENT) Class value(); } diff --git a/constraints/constraint-processor/src/test/java/io/toolisticon/aptk/constraints/processor/TestAnnotation.java b/constraints/constraint-processor/src/test/java/io/toolisticon/aptk/constraints/processor/TestAnnotation.java index fdf6a38b..b4f6a1c2 100644 --- a/constraints/constraint-processor/src/test/java/io/toolisticon/aptk/constraints/processor/TestAnnotation.java +++ b/constraints/constraint-processor/src/test/java/io/toolisticon/aptk/constraints/processor/TestAnnotation.java @@ -1,13 +1,13 @@ package io.toolisticon.aptk.constraints.processor; -import io.toolisticon.aptk.constraints.On; +import io.toolisticon.aptk.constraints.WithTargetOfKind; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@On(On.Location.INTERFACE) +@WithTargetOfKind(WithTargetOfKind.TargetKind.INTERFACE) @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestAnnotation { } diff --git a/constraints/constraint-processor/src/test/resources/testcase/happyPath/HappyPath.java b/constraints/constraint-processor/src/test/resources/testcase/happyPath/HappyPath.java index f6643dba..036c15a5 100644 --- a/constraints/constraint-processor/src/test/resources/testcase/happyPath/HappyPath.java +++ b/constraints/constraint-processor/src/test/resources/testcase/happyPath/HappyPath.java @@ -1,13 +1,7 @@ package io.toolisticon.aptk.tests; -import io.toolisticon.aptk.constraints.On; import io.toolisticon.aptk.constraints.processor.TestAnnotation; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - @TestAnnotation public interface HappyPath { diff --git a/constraints/constraint-processor/src/test/resources/testcase/invalidUsageOnAnnotation/InvalidUsage.java b/constraints/constraint-processor/src/test/resources/testcase/invalidUsageOnAnnotation/InvalidUsage.java index ee3c3a23..8c4b758d 100644 --- a/constraints/constraint-processor/src/test/resources/testcase/invalidUsageOnAnnotation/InvalidUsage.java +++ b/constraints/constraint-processor/src/test/resources/testcase/invalidUsageOnAnnotation/InvalidUsage.java @@ -1,14 +1,8 @@ package io.toolisticon.aptk.tests; -import io.toolisticon.aptk.constraints.On; import io.toolisticon.aptk.constraints.processor.TestAnnotation; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - @TestAnnotation public enum InvalidUsage { TEST; diff --git a/constraints/constraint-processor/src/test/resources/testcase/manualConstraintsHappyPath/HappyPath.java b/constraints/constraint-processor/src/test/resources/testcase/manualConstraintsHappyPath/HappyPath.java index 4024f9e3..771247bd 100644 --- a/constraints/constraint-processor/src/test/resources/testcase/manualConstraintsHappyPath/HappyPath.java +++ b/constraints/constraint-processor/src/test/resources/testcase/manualConstraintsHappyPath/HappyPath.java @@ -1,14 +1,14 @@ package io.toolisticon.aptk.tests; -import io.toolisticon.aptk.constraints.On; +import io.toolisticon.aptk.constraints.WithTargetOfKind; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@On(On.Location.ANNOTATION) +@WithTargetOfKind(WithTargetOfKind.TargetKind.ANNOTATION) @Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface HappyPath { diff --git a/mvnw.cmd b/mvnw.cmd index b26ab24f..91b49adc 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -21,10 +21,10 @@ @REM Maven2 Start Up Batch script @REM @REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir +@REM JAVA_HOME - targetKind of a JDK home dir @REM @REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir +@REM M2_HOME - targetKind of maven2's installed home dir @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @@ -63,7 +63,7 @@ if not "%JAVA_HOME%" == "" goto OkJHome echo. echo Error: JAVA_HOME not found in your environment. >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 +echo targetKind of your Java installation. >&2 echo. goto error @@ -74,7 +74,7 @@ echo. echo Error: JAVA_HOME is set to an invalid directory. >&2 echo JAVA_HOME = "%JAVA_HOME%" >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 +echo targetKind of your Java installation. >&2 echo. goto error