Add NotNull and NotBlank parameter validation 39/79939/4
authorJim Hahn <jrh3@att.com>
Thu, 7 Mar 2019 19:53:12 +0000 (14:53 -0500)
committerJim Hahn <jrh3@att.com>
Thu, 7 Mar 2019 21:37:49 +0000 (16:37 -0500)
Modified the ParameterValidator to support new NotNull and NotBlank
annotations indicating that a field should not be null or blank.
These annotations can be made at class level or individual field level.

Moved annotations to their own subdirectory.
Added a comment to a method.
Extracted constant strings.
Moved one annotation to the subclass level.

Added support for "Min" annotation.

Propagate validation errors up from nested items.  Apply
field-level validations, even when field is a ParameterGroup.

Change-Id: Ic90df55487dc5db7b7b0be5397624d1957904a81
Issue-ID: POLICY-1542
Signed-off-by: Jim Hahn <jrh3@att.com>
common-parameters/src/main/java/org/onap/policy/common/parameters/GroupValidationResult.java
common-parameters/src/main/java/org/onap/policy/common/parameters/ParameterValidationResult.java
common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/Min.java [new file with mode: 0644]
common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/NotBlank.java [new file with mode: 0644]
common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/NotNull.java [new file with mode: 0644]
common-parameters/src/test/java/org/onap/policy/common/parameters/TestValidation.java
common-parameters/src/test/java/org/onap/policy/common/parameters/testclasses/TestParametersLGeneric.java
common-parameters/src/test/resources/expectedValidationResults/TestParametersL0_2_Invalid.txt
common-parameters/src/test/resources/expectedValidationResults/TestParametersL0_3_Invalid.txt

index 283f36b..6da36c1 100644 (file)
@@ -1,20 +1,20 @@
-/*-
+/*
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2018 Ericsson. All rights reserved.
- *  Modifications Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ *  Modifications Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- * 
+ *
  * SPDX-License-Identifier: Apache-2.0
  * ============LICENSE_END=========================================================
  */
@@ -66,7 +66,7 @@ public class GroupValidationResult implements ValidationResult {
             // Check if a validation result should be added for this declared field
             if (isIncludedField(field)) {
                 // Set a validation result for the field
-                validationResultMap.put(field.getName(), getValidationResult(field, parameterGroup));
+                validationResultMap.put(field.getName(), getSetValidationResult(field, parameterGroup));
             }
         }
 
@@ -75,13 +75,29 @@ public class GroupValidationResult implements ValidationResult {
             // Check if a validation result should be added for this declared field
             if (isIncludedField(field)) {
                 // Set a validation result for the field
-                validationResultMap.putIfAbsent(field.getName(), getValidationResult(field, parameterGroup));
+                validationResultMap.putIfAbsent(field.getName(), getSetValidationResult(field, parameterGroup));
             }
         }
     }
+
+    /**
+     * Construct a validation result for a field, updating "this" status.
+     *
+     * @param field The parameter field
+     * @param ParameterGroup The parameter group containing the field
+     * @return the validation result
+     * @throws Exception on accessing private fields
+     */
+    private ValidationResult getSetValidationResult(Field field, ParameterGroup parameterGroup) {
+        ValidationResult result = getValidationResult(field, parameterGroup);
+        setResult(result.getStatus());
+
+        return result;
+    }
+
     /**
      * Construct a validation result for a field.
-     * 
+     *
      * @param field The parameter field
      * @param ParameterGroup The parameter group containing the field
      * @return the validation result
@@ -92,6 +108,12 @@ public class GroupValidationResult implements ValidationResult {
         final Class<?> fieldType = field.getType();
         final Object fieldObject = getObjectField(parameterGroup, field);
 
+        // perform null checks
+        ParameterValidationResult result = new ParameterValidationResult(field, fieldObject);
+        if (!result.isValid()) {
+            return result;
+        }
+
         // Nested parameter groups are allowed
         if (ParameterGroup.class.isAssignableFrom(fieldType)) {
             return new GroupValidationResult((ParameterGroup) fieldObject);
@@ -106,16 +128,16 @@ public class GroupValidationResult implements ValidationResult {
         // Collections of parameter groups are not allowed
         if (Collection.class.isAssignableFrom(field.getType())) {
             checkCollection4ParameterGroups(fieldName, fieldObject);
-            return new ParameterValidationResult(field, fieldObject);
+            return result;
         }
 
         // It's a regular parameter
-        return new ParameterValidationResult(field, fieldObject);
+        return result;
     }
 
     /**
      * Get the value of a field in an object using a getter found with reflection.
-     * 
+     *
      * @param targetObject The object on which to read the field value
      * @param fieldName The name of the field
      * @return The field value
@@ -156,7 +178,7 @@ public class GroupValidationResult implements ValidationResult {
 
     /**
      * Check if this field is a map of parameter groups indexed by string keys.
-     * 
+     *
      * @param fieldName the name of the collection field.
      * @param mapObject the map object to check
      */
@@ -184,7 +206,7 @@ public class GroupValidationResult implements ValidationResult {
 
     /**
      * Check if this field contains parameter groups.
-     * 
+     *
      * @param fieldName the name of the collection field.
      * @param collectionObject the collection object to check
      */
@@ -234,7 +256,7 @@ public class GroupValidationResult implements ValidationResult {
 
     /**
      * Set the validation result on a parameter group.
-     * 
+     *
      * @param status The validation status the parameter group is receiving
      * @param message The validation message explaining the validation status
      */
@@ -247,7 +269,7 @@ public class GroupValidationResult implements ValidationResult {
     /**
      * Set the validation result on a parameter group. On a sequence of calls, the most serious validation status is
      * recorded, assuming the status enum ordinal increase in order of severity
-     * 
+     *
      * @param status The validation status the parameter group is receiving
      */
     public void setResult(final ValidationStatus status) {
@@ -260,7 +282,7 @@ public class GroupValidationResult implements ValidationResult {
 
     /**
      * Set the validation result on a parameter in a parameter group.
-     * 
+     *
      * @param parameterName The name of the parameter
      * @param status The validation status the field is receiving
      * @param message The validation message explaining the validation status
@@ -281,7 +303,7 @@ public class GroupValidationResult implements ValidationResult {
 
     /**
      * Set the validation result on a nested parameter group.
-     * 
+     *
      * @param parameterName The name of the parameter field
      * @param nestedValidationResult The validation result from a nested field
      */
@@ -304,7 +326,7 @@ public class GroupValidationResult implements ValidationResult {
 
     /**
      * Set the validation result on a nested parameter group map entry.
-     * 
+     *
      * @param parameterName The name of the parameter field
      * @param key The key of the map entry
      * @param nestedMapValidationResult The validation result from a nested map entry
@@ -329,7 +351,7 @@ public class GroupValidationResult implements ValidationResult {
 
     /**
      * Set the validation status on a group map entry.
-     * 
+     *
      * @param parameterName The name of the parameter field
      * @param key The key of the map entry
      * @param status The validation status of the entry
@@ -395,11 +417,11 @@ public class GroupValidationResult implements ValidationResult {
 
         return validationResultBuilder.toString();
     }
-    
+
 
     /**
      * Check if a field should be included for validation.
-     * 
+     *
      * @param field the field to check for inclusion
      * @return true of the field should be included
      */
@@ -425,11 +447,11 @@ public class GroupValidationResult implements ValidationResult {
                     superclassFields.add(field);
                 }
             }
-            
+
             // Check the next super class down
             currentClass = currentClass.getSuperclass();
         }
-        
+
         return superclassFields;
     }
-}
\ No newline at end of file
+}
index 9c829f4..e43c2d1 100644 (file)
@@ -1,26 +1,31 @@
-/*-
+/*
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2018 Ericsson. All rights reserved.
+ *  Modifications Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- * 
+ *
  * SPDX-License-Identifier: Apache-2.0
  * ============LICENSE_END=========================================================
  */
 
 package org.onap.policy.common.parameters;
 
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
+import org.onap.policy.common.parameters.annotations.Min;
+import org.onap.policy.common.parameters.annotations.NotBlank;
+import org.onap.policy.common.parameters.annotations.NotNull;
 
 /**
  * This class holds the result of the validation of a parameter.
@@ -43,6 +48,47 @@ public class ParameterValidationResult implements ValidationResult {
     protected ParameterValidationResult(final Field field, final Object parameterValue) {
         this.field = field;
         this.parameterValue = parameterValue;
+
+        if (parameterValue == null && getAnyAnnotation(field, NotNull.class) != null) {
+            setResult(ValidationStatus.INVALID, "is null");
+
+        } else if (parameterValue instanceof String && getAnyAnnotation(field, NotBlank.class) != null
+                        && parameterValue.toString().trim().isEmpty()) {
+            setResult(ValidationStatus.INVALID, "must be a non-blank string");
+
+        } else if (parameterValue instanceof Number) {
+            Min minAnnot = field.getAnnotation(Min.class);
+            if (minAnnot != null && ((Number) parameterValue).longValue() < minAnnot.value()) {
+                setResult(ValidationStatus.INVALID, "must be >= " + minAnnot.value());
+            }
+        }
+    }
+
+    /**
+     * Gets an annotation for a field, first checking the field, itself, and then checking
+     * at the class level for the current and superclasses.
+     *
+     * @param field field of interest
+     * @param annotClass class of annotation that is desired
+     * @return the field's annotation, or {@code null} if it does not exist
+     */
+    private static <T extends Annotation> T getAnyAnnotation(final Field field, Class<T> annotClass) {
+        T annot = field.getAnnotation(annotClass);
+        if (annot != null) {
+            return annot;
+        }
+
+        // check class level
+        Class<?> clazz = field.getDeclaringClass();
+        while (clazz != Object.class) {
+            if ((annot = clazz.getAnnotation(annotClass)) != null) {
+                return annot;
+            }
+
+            clazz = clazz.getSuperclass();
+        }
+
+        return null;
     }
 
     /**
@@ -66,14 +112,16 @@ public class ParameterValidationResult implements ValidationResult {
     }
 
     /**
-     * Set the validation result on on a parameter field. 
+     * Set the validation result on on a parameter field.
      * @param status The validation status the field is receiving
      * @param message The validation message explaining the validation status
      */
     @Override
     public void setResult(final ValidationStatus status, final String message) {
-        this.status = status;
-        this.message = message;
+        if (this.status == ValidationStatus.CLEAN) {
+            this.status = status;
+            this.message = message;
+        }
     }
 
     /**
diff --git a/common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/Min.java b/common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/Min.java
new file mode 100644 (file)
index 0000000..9ad6d7e
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.parameters.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(RUNTIME)
+@Target(FIELD)
+public @interface Min {
+
+    /**
+     * The minimum value allowed.
+     *
+     * @return the minimum value allowed
+     */
+    long value();
+}
diff --git a/common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/NotBlank.java b/common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/NotBlank.java
new file mode 100644 (file)
index 0000000..169fa59
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.parameters.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a field (i.e., String) may not be empty.
+ */
+@Retention(RUNTIME)
+@Target({TYPE, FIELD})
+public @interface NotBlank {
+
+}
diff --git a/common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/NotNull.java b/common-parameters/src/main/java/org/onap/policy/common/parameters/annotations/NotNull.java
new file mode 100644 (file)
index 0000000..3c7bc8b
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.parameters.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a field may not be null.
+ */
+@Retention(RUNTIME)
+@Target({TYPE, FIELD})
+public @interface NotNull {
+
+}
index fb08d32..57d69f8 100644 (file)
@@ -1,19 +1,20 @@
-/*-
+/*
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2018 Ericsson. All rights reserved.
+ *  Modifications Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- * 
+ *
  * SPDX-License-Identifier: Apache-2.0
  * ============LICENSE_END=========================================================
  */
@@ -28,12 +29,43 @@ import static org.junit.Assert.assertTrue;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Paths;
-
 import org.junit.Test;
+import org.onap.policy.common.parameters.annotations.Min;
+import org.onap.policy.common.parameters.annotations.NotBlank;
+import org.onap.policy.common.parameters.annotations.NotNull;
 import org.onap.policy.common.parameters.testclasses.TestParametersL00;
 import org.onap.policy.common.parameters.testclasses.TestParametersL10;
 
 public class TestValidation {
+    private static final String NOT_BLANK_STRING_MESSAGE =
+                    "field 'notBlankString' type 'java.lang.String' value '' INVALID, must be a non-blank string\n"
+                                    .replace('\'', '"');
+
+    private static final String NULL_STRING_MESSAGE =
+                    "field 'notNullString' type 'java.lang.String' value 'null' INVALID, is null\n".replace('\'', '"');
+
+
+    private static final String NOT_BLANK_OBJECT_NAME = "notBlankObject";
+    private static final String NOT_BLANK_STRING_NAME = "notBlankString";
+    private static final String NOT_NULL_OBJECT_NAME = "notNullObject";
+    private static final String NOT_NULL_STRING_NAME = "notNullString";
+    private static final String MIN_LONG_NAME = "minLong";
+
+    @NotNull
+    private String notNullString;
+
+    @NotNull
+    private Object notNullObject;
+
+    @NotBlank
+    private String notBlankString;
+
+    @NotBlank
+    private Object notBlankObject;
+
+    @Min(value = 10)
+    private long minLong;
+
     @Test
     public void testValidationOk() throws IOException {
         TestParametersL00 l0Parameters = new TestParametersL00("l0Parameters");
@@ -50,11 +82,11 @@ public class TestValidation {
                                         .replaceAll("\\s+", "");
         assertEquals(expectedResult, validationResult.getResult("", "  ", true).replaceAll("\\s+", ""));
     }
-    
+
     @Test
     public void testValidationObservation() throws IOException {
         TestParametersL00 l0Parameters = new TestParametersL00("l0Parameters");
-        
+
         l0Parameters.triggerValidationStatus(ValidationStatus.OBSERVATION, 3);
 
         String expectedResult = new String(Files.readAllBytes(
@@ -65,7 +97,7 @@ public class TestValidation {
         assertTrue(validationResult.isValid());
         assertFalse(validationResult.isClean());
         assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", ""));
-        
+
         l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3);
         l0Parameters.triggerValidationStatus(ValidationStatus.OBSERVATION, 2);
 
@@ -76,7 +108,7 @@ public class TestValidation {
         validationResult = l0Parameters.validate();
         assertTrue(validationResult.isValid());
         assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", ""));
-        
+
         l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3);
         l0Parameters.triggerValidationStatus(ValidationStatus.OBSERVATION, 1);
 
@@ -87,7 +119,7 @@ public class TestValidation {
         validationResult = l0Parameters.validate();
         assertTrue(validationResult.isValid());
         assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", ""));
-        
+
         l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3);
         l0Parameters.triggerValidationStatus(ValidationStatus.OBSERVATION, 0);
 
@@ -95,11 +127,11 @@ public class TestValidation {
         assertTrue(validationResult.isValid());
         assertEquals(null, validationResult.getResult());
     }
-    
+
     @Test
     public void testValidationWarning() throws IOException {
         TestParametersL00 l0Parameters = new TestParametersL00("l0Parameters");
-        
+
         l0Parameters.triggerValidationStatus(ValidationStatus.WARNING, 3);
 
         String expectedResult = new String(Files.readAllBytes(
@@ -109,7 +141,7 @@ public class TestValidation {
         GroupValidationResult validationResult = l0Parameters.validate();
         assertTrue(validationResult.isValid());
         assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", ""));
-        
+
         l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3);
         l0Parameters.triggerValidationStatus(ValidationStatus.WARNING, 2);
 
@@ -120,7 +152,7 @@ public class TestValidation {
         validationResult = l0Parameters.validate();
         assertTrue(validationResult.isValid());
         assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", ""));
-        
+
         l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3);
         l0Parameters.triggerValidationStatus(ValidationStatus.WARNING, 1);
 
@@ -131,7 +163,7 @@ public class TestValidation {
         validationResult = l0Parameters.validate();
         assertTrue(validationResult.isValid());
         assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", ""));
-        
+
         l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3);
         l0Parameters.triggerValidationStatus(ValidationStatus.WARNING, 0);
 
@@ -139,11 +171,11 @@ public class TestValidation {
         assertTrue(validationResult.isValid());
         assertEquals(null, validationResult.getResult());
     }
-    
+
     @Test
     public void testValidationInvalid() throws IOException {
         TestParametersL00 l0Parameters = new TestParametersL00("l0Parameters");
-        
+
         l0Parameters.triggerValidationStatus(ValidationStatus.INVALID, 3);
 
         String expectedResult = new String(Files.readAllBytes(
@@ -153,7 +185,7 @@ public class TestValidation {
         GroupValidationResult validationResult = l0Parameters.validate();
         assertFalse(validationResult.isValid());
         assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", ""));
-        
+
         l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3);
         l0Parameters.triggerValidationStatus(ValidationStatus.INVALID, 2);
 
@@ -164,7 +196,7 @@ public class TestValidation {
         validationResult = l0Parameters.validate();
         assertFalse(validationResult.isValid());
         assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", ""));
-        
+
         l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3);
         l0Parameters.triggerValidationStatus(ValidationStatus.INVALID, 1);
 
@@ -175,7 +207,7 @@ public class TestValidation {
         validationResult = l0Parameters.validate();
         assertFalse(validationResult.isValid());
         assertEquals(expectedResult, validationResult.getResult().replaceAll("\\s+", ""));
-        
+
         l0Parameters.triggerValidationStatus(ValidationStatus.CLEAN, 3);
         l0Parameters.triggerValidationStatus(ValidationStatus.INVALID, 0);
 
@@ -183,7 +215,7 @@ public class TestValidation {
         assertTrue(validationResult.isValid());
         assertEquals(null, validationResult.getResult());
     }
-    
+
     @Test
     public void testValidationEmptySubGroup() throws IOException {
         TestParametersL10 l10Parameters = new TestParametersL10("l10Parameters");
@@ -192,7 +224,201 @@ public class TestValidation {
 
         GroupValidationResult validationResult = l10Parameters.validate();
         assertTrue(validationResult.isValid());
-        
+
         assertTrue(validationResult.getResult("", "", true).contains("UNDEFINED"));
     }
+
+    @Test
+    public void testGetValidationResult() throws Exception {
+        Contained item = new Contained();
+        item.setName("item");
+
+        Container cont = new Container();
+        cont.item = item;
+        GroupValidationResult result = cont.validate();
+        assertEquals(ValidationStatus.INVALID, result.getStatus());
+        assertTrue(result.getResult().contains(">= 1"));
+
+        item.minInt = 1000;
+        result = cont.validate();
+        assertEquals(ValidationStatus.CLEAN, result.getStatus());
+
+        cont.item = null;
+        result = cont.validate();
+        assertEquals(ValidationStatus.INVALID, result.getStatus());
+        assertTrue(result.getResult().contains("is null"));
+    }
+
+    @Test
+    public void testParameterValidationResult_NotNull() throws Exception {
+        ParameterValidationResult result = new ParameterValidationResult(
+                        TestValidation.class.getDeclaredField(NOT_NULL_STRING_NAME), null);
+        assertEquals(ValidationStatus.INVALID, result.getStatus());
+        assertEquals(NULL_STRING_MESSAGE, result.getResult());
+
+        // don't allow overwrite - values should remain unchanged
+        result.setResult(ValidationStatus.WARNING, "unknown");
+        assertEquals(ValidationStatus.INVALID, result.getStatus());
+        assertEquals(NULL_STRING_MESSAGE, result.getResult());
+
+        // non-null should be OK
+        result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_NULL_STRING_NAME), "");
+        assertEquals(ValidationStatus.CLEAN, result.getStatus());
+
+        // non-null should be OK
+        result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_NULL_STRING_NAME), "abc");
+        assertEquals(ValidationStatus.CLEAN, result.getStatus());
+
+        /*
+         * Check plain object fields, too.
+         */
+        result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_NULL_OBJECT_NAME), null);
+        assertEquals(ValidationStatus.INVALID, result.getStatus());
+        assertEquals("field 'notNullObject' type 'java.lang.Object' value 'null' INVALID, is null\n".replace('\'', '"'),
+                        result.getResult());
+
+        // non-null should be OK
+        result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_NULL_OBJECT_NAME),
+                        new Object());
+        assertEquals(ValidationStatus.CLEAN, result.getStatus());
+
+        /*
+         * Class-level annotation.
+         */
+
+        result = new ParameterValidationResult(NotNullSub.class.getDeclaredField(NOT_NULL_STRING_NAME), null);
+        assertEquals(ValidationStatus.INVALID, result.getStatus());
+        assertEquals(NULL_STRING_MESSAGE, result.getResult());
+
+        // non-null should be OK
+        result = new ParameterValidationResult(NotNullSub.class.getDeclaredField(NOT_NULL_STRING_NAME), "");
+        assertEquals(ValidationStatus.CLEAN, result.getStatus());
+    }
+
+    @Test
+    public void testParameterValidationResult_NotBlank() throws Exception {
+        ParameterValidationResult result =
+                        new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_BLANK_STRING_NAME), "");
+        assertEquals(ValidationStatus.INVALID, result.getStatus());
+        assertEquals(NOT_BLANK_STRING_MESSAGE, result.getResult());
+
+        // spaces only
+        result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_BLANK_STRING_NAME), " \t");
+        assertEquals(ValidationStatus.INVALID, result.getStatus());
+
+        // null should be OK
+        result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_BLANK_STRING_NAME), null);
+        assertEquals(ValidationStatus.CLEAN, result.getStatus());
+
+        // null should be OK
+        result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_BLANK_STRING_NAME), "abc");
+        assertEquals(ValidationStatus.CLEAN, result.getStatus());
+
+        /*
+         * Check plain object fields, too.
+         */
+        result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_BLANK_OBJECT_NAME), null);
+        assertEquals(ValidationStatus.CLEAN, result.getStatus());
+
+        result = new ParameterValidationResult(TestValidation.class.getDeclaredField(NOT_BLANK_OBJECT_NAME),
+                        new Object());
+        assertEquals(ValidationStatus.CLEAN, result.getStatus());
+
+        /*
+         * Class-level annotation.
+         */
+        result = new ParameterValidationResult(NotBlankSub.class.getDeclaredField(NOT_BLANK_STRING_NAME), "");
+        assertEquals(ValidationStatus.INVALID, result.getStatus());
+        assertEquals(NOT_BLANK_STRING_MESSAGE, result.getResult());
+
+        // non-null should be OK
+        result = new ParameterValidationResult(NotBlankSub.class.getDeclaredField(NOT_BLANK_STRING_NAME), "abc");
+        assertEquals(ValidationStatus.CLEAN, result.getStatus());
+    }
+
+    @Test
+    public void testParameterValidationResult_Min() throws Exception {
+        ParameterValidationResult result =
+                        new ParameterValidationResult(TestValidation.class.getDeclaredField(MIN_LONG_NAME), 9L);
+        assertEquals(ValidationStatus.INVALID, result.getStatus());
+        assertEquals("field 'minLong' type 'long' value '9' INVALID, must be >= 10\n".replace('\'', '"'),
+                        result.getResult());
+
+        result = new ParameterValidationResult(TestValidation.class.getDeclaredField(MIN_LONG_NAME), -2);
+        assertEquals(ValidationStatus.INVALID, result.getStatus());
+        assertEquals("field 'minLong' type 'long' value '-2' INVALID, must be >= 10\n".replace('\'', '"'),
+                        result.getResult());
+
+        result = new ParameterValidationResult(TestValidation.class.getDeclaredField(MIN_LONG_NAME), 10L);
+        assertEquals(ValidationStatus.CLEAN, result.getStatus());
+
+        result = new ParameterValidationResult(TestValidation.class.getDeclaredField(MIN_LONG_NAME), 11);
+        assertEquals(ValidationStatus.CLEAN, result.getStatus());
+    }
+
+    // these classes are used to test class-level annotations
+
+    @NotNull
+    private static class NotNullBase {
+
+    }
+
+    private static class NotNullSub extends NotNullBase {
+        @SuppressWarnings("unused")
+        private String notNullString;
+    }
+
+    private static class NotBlankBase {
+
+    }
+
+    @NotBlank
+    private static class NotBlankSub extends NotBlankBase {
+        @SuppressWarnings("unused")
+        private String notBlankString;
+    }
+
+    private abstract static class SimpleGroup implements ParameterGroup {
+        private String name;
+
+        @Override
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public void setName(String name) {
+            this.name = name;
+        }
+    }
+
+    private static class Contained extends SimpleGroup {
+        @Min(value = 1)
+        private int minInt;
+
+        @Override
+        public GroupValidationResult validate() {
+            return new GroupValidationResult(this);
+        }
+
+        @SuppressWarnings("unused")
+        public int getMinInt() {
+            return minInt;
+        }
+    }
+
+    private static class Container extends SimpleGroup {
+        @NotNull
+        private Contained item;
+
+        @Override
+        public GroupValidationResult validate() {
+            return new GroupValidationResult(this);
+        }
+
+        @SuppressWarnings("unused")
+        public Contained getItem() {
+            return item;
+        }
+    }
 }
index 2d263fc..1e5764c 100644 (file)
@@ -1,19 +1,20 @@
-/*-
+/*
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2018 Ericsson. All rights reserved.
+ *  Modifications Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- * 
+ *
  * SPDX-License-Identifier: Apache-2.0
  * ============LICENSE_END=========================================================
  */
 package org.onap.policy.common.parameters.testclasses;
 
 import org.onap.policy.common.parameters.GroupValidationResult;
-import org.onap.policy.common.parameters.ParameterConstants;
 import org.onap.policy.common.parameters.ParameterGroup;
 import org.onap.policy.common.parameters.ValidationStatus;
+import org.onap.policy.common.parameters.annotations.NotBlank;
+import org.onap.policy.common.parameters.annotations.NotNull;
 
 public class TestParametersLGeneric implements ParameterGroup {
     private String name;
     private int lgenericIntField = 0;
+
+    @NotNull @NotBlank
     private String lgenericStringField = "Legal " + this.getClass().getCanonicalName();
-    
+
     /**
      * Default constructor.
      */
     public TestParametersLGeneric() {
         // Default Constructor
     }
-    
+
     /**
      * Create a test parameter group.
-     * 
+     *
      * @param name the parameter group name
      */
     public TestParametersLGeneric(final String name) {
@@ -68,7 +72,7 @@ public class TestParametersLGeneric implements ParameterGroup {
 
     /**
      * Trigger a validation message.
-     * 
+     *
      * @param level Number of levels to recurse before stopping
      */
     public void triggerValidationStatus(final ValidationStatus triggerStatus, int level) {
@@ -111,18 +115,12 @@ public class TestParametersLGeneric implements ParameterGroup {
     public GroupValidationResult validate() {
         GroupValidationResult validationResult = new GroupValidationResult(this);
 
-        if (lgenericStringField == null || lgenericStringField.trim().length() == 0) {
-            validationResult.setResult("lgenericStringField", ValidationStatus.INVALID,
-                            "lgenericStringField must be a non-blank string");
-        } else if (lgenericStringField.equals("lgenericStringField")) {
+        if ("lgenericStringField".equals(lgenericStringField)) {
             validationResult.setResult("lgenericStringField", ValidationStatus.WARNING,
                             "using the field name for the parameter value is dangerous");
-        } else if (lgenericStringField.equals("aString")) {
+        } else if ("aString".equals(lgenericStringField)) {
             validationResult.setResult("lgenericStringField", ValidationStatus.OBSERVATION,
                             "this value for name is unhelpful");
-        } else {
-            validationResult.setResult("lgenericStringField", ValidationStatus.CLEAN,
-                            ParameterConstants.PARAMETER_HAS_STATUS_MESSAGE + ValidationStatus.CLEAN.toString());
         }
 
         if (lgenericIntField < 0) {
@@ -134,9 +132,6 @@ public class TestParametersLGeneric implements ParameterGroup {
         } else if (lgenericIntField == 2) {
             validationResult.setResult("lgenericIntField", ValidationStatus.OBSERVATION,
                             "this field has been set to 2");
-        } else {
-            validationResult.setResult("lgenericIntField", ValidationStatus.CLEAN,
-                            ParameterConstants.PARAMETER_HAS_STATUS_MESSAGE + ValidationStatus.CLEAN.toString());
         }
 
         return validationResult;
index 0dec4a6..d930199 100644 (file)
@@ -6,11 +6,11 @@ parameter group "l0Parameters" type "org.onap.policy.common.parameters.testclass
     field "l10StringField" type "java.lang.String" value "" INVALID, l10StringField must be a non-blank string
   parameter group "l00LGenericNested" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID
     field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer
-    field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string
+    field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string
   parameter group map "l00LGenericNestedMap" INVALID, parameter group has status INVALID
     parameter group "l00LGenericNestedMapVal0" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID
       field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer
-      field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string
+      field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string
     parameter group "l00LGenericNestedMapVal1" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID
       field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer
-      field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string
\ No newline at end of file
+      field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string
\ No newline at end of file
index 762ef46..e45a7a6 100644 (file)
@@ -6,24 +6,24 @@ parameter group "l0Parameters" type "org.onap.policy.common.parameters.testclass
     field "l10StringField" type "java.lang.String" value "" INVALID, l10StringField must be a non-blank string
     parameter group "l10LGenericNested0" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID
       field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer
-      field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string
+      field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string
     parameter group "l10LGenericNested1" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID
       field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer
-      field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string
+      field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string
     parameter group map "l10LGenericNestedMap" INVALID, parameter group has status INVALID
       parameter group "l10LGenericNestedMapVal0" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID
         field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer
-        field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string
+        field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string
       parameter group "l10LGenericNestedMapVal1" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID
         field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer
-        field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string
+        field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string
   parameter group "l00LGenericNested" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID
     field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer
-    field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string
+    field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string
   parameter group map "l00LGenericNestedMap" INVALID, parameter group has status INVALID
     parameter group "l00LGenericNestedMapVal0" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID
       field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer
-      field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string
+      field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string
     parameter group "l00LGenericNestedMapVal1" type "org.onap.policy.common.parameters.testclasses.TestParametersLGeneric" INVALID, parameter group has status INVALID
       field "lgenericIntField" type "int" value "-1" INVALID, lgenericIntField must be a positive integer
-      field "lgenericStringField" type "java.lang.String" value "" INVALID, lgenericStringField must be a non-blank string
\ No newline at end of file
+      field "lgenericStringField" type "java.lang.String" value "" INVALID, must be a non-blank string
\ No newline at end of file