SLING-10117 : Support different configuration validation modes
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurableEntity.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurableEntity.java
index a6f820d..08c7929 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurableEntity.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurableEntity.java
@@ -37,6 +37,12 @@
@SuppressWarnings({"unchecked", "rawtypes"})
private final Map<String, PropertyDescription> properties = (Map)Configurations.newConfiguration();
+ /**
+ * The validation mode.
+ * @since 1.2
+ */
+ private Mode mode;
+
/**
* Clear the object and reset to defaults
*/
@@ -44,6 +50,7 @@
public void clear() {
super.clear();
this.properties.clear();
+ this.setMode(null);
}
/**
@@ -67,6 +74,10 @@
}
}
}
+ final String modeVal = this.getString(InternalConstants.KEY_MODE);
+ if ( modeVal != null ) {
+ this.setMode(Mode.valueOf(modeVal.toUpperCase()));
+ }
} catch (final JsonException | IllegalArgumentException e) {
throw new IOException(e);
}
@@ -81,6 +92,24 @@
}
/**
+ * Get the validation mode.
+ * @return The mode or {@code null}
+ * @since 1.2
+ */
+ public Mode getMode() {
+ return this.mode;
+ }
+
+ /**
+ * Set the validation mode
+ * @value The validation mode
+ * @since 1.2
+ */
+ public void setMode(final Mode value) {
+ this.mode = value;
+ }
+
+ /**
* Convert this object into JSON
*
* @return The json object builder
@@ -97,6 +126,9 @@
}
objBuilder.add(InternalConstants.KEY_PROPERTIES, propBuilder);
}
+ if ( this.getMode() != null ) {
+ objBuilder.add(InternalConstants.KEY_MODE, this.getMode().name());
+ }
return objBuilder;
}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApi.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApi.java
index 15c23ef..15e622e 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApi.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApi.java
@@ -133,6 +133,12 @@
/** The cached region information for feature origins */
private final Map<ArtifactId, Region> regionCache = new LinkedHashMap<>();
+ /**
+ * The default validation mode.
+ * @since 1.2
+ */
+ private Mode mode = Mode.STRICT;
+
/**
* Clear the object and reset to defaults
*/
@@ -147,6 +153,7 @@
this.internalFrameworkProperties.clear();
this.setRegion(null);
this.getFeatureToRegionCache().clear();
+ this.setMode(Mode.STRICT);
}
/**
@@ -221,6 +228,11 @@
Region.valueOf(getString(innerEntry.getValue()).toUpperCase()));
}
}
+
+ final String modeVal = this.getString(InternalConstants.KEY_MODE);
+ if ( modeVal != null ) {
+ this.setMode(Mode.valueOf(modeVal.toUpperCase()));
+ }
} catch (final JsonException | IllegalArgumentException e) {
throw new IOException(e);
@@ -313,6 +325,25 @@
}
/**
+ * Get the validation mode.
+ * The default is {@link Mode#STRICT}
+ * @return The mode
+ * @since 1.2
+ */
+ public Mode getMode() {
+ return this.mode;
+ }
+
+ /**
+ * Set the validation mode
+ * @value The validation mode
+ * @since 1.2
+ */
+ public void setMode(final Mode value) {
+ this.mode = value == null ? Mode.STRICT : value;
+ }
+
+ /**
* Convert this object into JSON
*
* @return The json object builder
@@ -373,6 +404,9 @@
}
objBuilder.add(InternalConstants.KEY_REGION_CACHE, cacheBuilder);
}
+ if ( this.getMode() != Mode.STRICT ) {
+ objBuilder.add(InternalConstants.KEY_MODE, this.getMode().name());
+ }
return objBuilder;
}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/InternalConstants.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/InternalConstants.java
index e126581..a99f32b 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/InternalConstants.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/InternalConstants.java
@@ -74,4 +74,6 @@
static final String KEY_REGION_CACHE = "region-cache";
static final String KEY_DEFAULT = "default";
+
+ static final String KEY_MODE = "mode";
}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Mode.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Mode.java
new file mode 100644
index 0000000..7608c3c
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Mode.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.feature.extension.apiregions.api.config;
+
+/**
+ * The validation mode
+ * @since 1.2
+ */
+public enum Mode {
+
+ /** Default mode - If validation fails, issue an error. */
+ STRICT,
+
+ /** If validation fails, issue a warning (but use the invalid value). */
+ LENIENT,
+
+ /** If validation fails, do not report and use the invalid value. */
+ SILENT,
+
+ /** If validation fails, use the default value (if provided - otherwise remove value) and issue a warning. */
+ DEFINITIVE,
+
+ /** If validation fails, use the default value (if provided - otherwise remove value) and do not issue a warning. */
+ SILENT_DEFINITIVE
+
+}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescription.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescription.java
index 56bb7e1..c315d4f 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescription.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescription.java
@@ -69,6 +69,12 @@
*/
private Object defaultValue;
+ /**
+ * The validation mode.
+ * @since 1.2
+ */
+ private Mode mode;
+
/**
* Create a new description
*/
@@ -96,6 +102,7 @@
this.setOptions(null);
this.setRegex(null);
this.setDefaultValue(null);
+ this.setMode(null);
}
/**
@@ -153,6 +160,10 @@
if ( dv != null ) {
this.setDefaultValue(Configurations.convertToObject(dv));
}
+ final String modeVal = this.getString(InternalConstants.KEY_MODE);
+ if ( modeVal != null ) {
+ this.setMode(Mode.valueOf(modeVal.toUpperCase()));
+ }
} catch (final JsonException | IllegalArgumentException e) {
throw new IOException(e);
}
@@ -207,7 +218,11 @@
if ( this.getDefaultValue() != null ) {
objectBuilder.add(InternalConstants.KEY_DEFAULT, Configurations.convertToJsonValue(this.getDefaultValue()));
}
- return objectBuilder;
+ if ( this.getMode() != null ) {
+ objectBuilder.add(InternalConstants.KEY_MODE, this.getMode().name());
+ }
+
+ return objectBuilder;
}
/**
@@ -392,4 +407,22 @@
public void setDefaultValue(final Object val) {
this.defaultValue = val;
}
-}
+
+ /**
+ * Get the validation mode.
+ * @return The mode or {@code null}
+ * @since 1.2
+ */
+ public Mode getMode() {
+ return this.mode;
+ }
+
+ /**
+ * Set the validation mode
+ * @value The validation mode
+ * @since 1.2
+ */
+ public void setMode(final Mode value) {
+ this.mode = value;
+ }
+ }
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/ConfigurationValidator.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/ConfigurationValidator.java
index f0315b3..44e16d4 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/ConfigurationValidator.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/ConfigurationValidator.java
@@ -26,6 +26,7 @@
import org.apache.sling.feature.extension.apiregions.api.config.ConfigurableEntity;
import org.apache.sling.feature.extension.apiregions.api.config.ConfigurationDescription;
import org.apache.sling.feature.extension.apiregions.api.config.FactoryConfigurationDescription;
+import org.apache.sling.feature.extension.apiregions.api.config.Mode;
import org.apache.sling.feature.extension.apiregions.api.config.PropertyDescription;
import org.apache.sling.feature.extension.apiregions.api.config.Region;
import org.osgi.framework.Constants;
@@ -54,18 +55,33 @@
* @return The result
*/
public ConfigurationValidationResult validate(final Configuration config, final ConfigurableEntity desc, final Region region) {
+ return this.validate(config, desc, region, Mode.STRICT);
+ }
+
+ /**
+ * Validate a configuration
+ *
+ * @param config The OSGi configuration
+ * @param desc The configuration description
+ * @param region The optional region for the configuration
+ * @param mode The optional validation mode. This is used if the configuration/property has no mode is set. Defaults to {@link Mode#STRICT}.
+ * @return The result
+ * @since 1.2
+ */
+ public ConfigurationValidationResult validate(final Configuration config, final ConfigurableEntity desc,
+ final Region region, final Mode mode) {
final ConfigurationValidationResult result = new ConfigurationValidationResult();
if ( config.isFactoryConfiguration() ) {
if ( !(desc instanceof FactoryConfigurationDescription) ) {
result.getErrors().add("Factory configuration cannot be validated against non factory configuration description");
} else {
- validateProperties(config, desc, result.getPropertyResults(), region);
+ validateProperties(config, desc, result.getPropertyResults(), region, mode);
}
} else {
if ( !(desc instanceof ConfigurationDescription) ) {
result.getErrors().add("Configuration cannot be validated against factory configuration description");
} else {
- validateProperties(config, desc, result.getPropertyResults(), region);
+ validateProperties(config, desc, result.getPropertyResults(), region, mode);
}
}
@@ -85,12 +101,13 @@
void validateProperties(final Configuration configuration,
final ConfigurableEntity desc,
final Map<String, PropertyValidationResult> results,
- final Region region) {
+ final Region region,
+ final Mode mode) {
final Dictionary<String, Object> properties = configuration.getConfigurationProperties();
// validate the described properties
for(final Map.Entry<String, PropertyDescription> propEntry : desc.getPropertyDescriptions().entrySet()) {
final Object value = properties.get(propEntry.getKey());
- final PropertyValidationResult result = propertyValidator.validate(value, propEntry.getValue());
+ final PropertyValidationResult result = propertyValidator.validate(value, propEntry.getValue(), mode);
results.put(propEntry.getKey(), result);
}
// validate additional properties
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/FeatureValidator.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/FeatureValidator.java
index 8237869..919511d 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/FeatureValidator.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/FeatureValidator.java
@@ -96,7 +96,7 @@
if ( config.isFactoryConfiguration() ) {
final FactoryConfigurationDescription desc = api.getFactoryConfigurationDescriptions().get(config.getFactoryPid());
if ( desc != null ) {
- final ConfigurationValidationResult r = configurationValidator.validate(config, desc, regionInfo.region);
+ final ConfigurationValidationResult r = configurationValidator.validate(config, desc, regionInfo.region, api.getMode());
result.getConfigurationResults().put(config.getPid(), r);
if ( regionInfo.region != Region.INTERNAL ) {
if ( desc.getOperations().isEmpty() ) {
@@ -121,7 +121,7 @@
} else {
final ConfigurationDescription desc = api.getConfigurationDescriptions().get(config.getPid());
if ( desc != null ) {
- final ConfigurationValidationResult r = configurationValidator.validate(config, desc, regionInfo.region);
+ final ConfigurationValidationResult r = configurationValidator.validate(config, desc, regionInfo.region, api.getMode());
result.getConfigurationResults().put(config.getPid(), r);
} else if ( regionInfo.region!= Region.INTERNAL && api.getInternalConfigurations().contains(config.getPid())) {
final ConfigurationValidationResult cvr = new ConfigurationValidationResult();
@@ -144,7 +144,7 @@
} else {
final FrameworkPropertyDescription fpd = api.getFrameworkPropertyDescriptions().get(frameworkProperty);
if ( fpd != null ) {
- final PropertyValidationResult pvr = propertyValidator.validate(feature.getFrameworkProperties().get(frameworkProperty), fpd);
+ final PropertyValidationResult pvr = propertyValidator.validate(feature.getFrameworkProperties().get(frameworkProperty), fpd, api.getMode());
result.getFrameworkPropertyResults().put(frameworkProperty, pvr);
} else if ( regionInfo.region != Region.INTERNAL && api.getInternalFrameworkProperties().contains(frameworkProperty) ) {
final PropertyValidationResult pvr = new PropertyValidationResult();
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidator.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidator.java
index ecfdb5a..b643d9d 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidator.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidator.java
@@ -24,6 +24,7 @@
import java.util.Collection;
import java.util.List;
+import org.apache.sling.feature.extension.apiregions.api.config.Mode;
import org.apache.sling.feature.extension.apiregions.api.config.Option;
import org.apache.sling.feature.extension.apiregions.api.config.PropertyDescription;
import org.apache.sling.feature.extension.apiregions.api.config.PropertyType;
@@ -40,10 +41,25 @@
* @return A property validation result
*/
public PropertyValidationResult validate(final Object value, final PropertyDescription desc) {
- final PropertyValidationResult result = new PropertyValidationResult();
- if ( value == null ) {
+ return this.validate(value, desc, Mode.STRICT);
+ }
+
+ /**
+ * Validate the value against the property definition
+ * @param value The value to validate
+ * @param desc The property description
+ * @param mode Optional validation mode - this mode is used if the description does not define a mode. Defaults to {@link Mode#STRICT}.
+ * @return A property validation result
+ * @since 1.2.0
+ */
+ public PropertyValidationResult validate(final Object value, final PropertyDescription desc, final Mode mode) {
+ final Context context = new Context();
+ context.description = desc;
+ context.validationMode = desc.getMode() != null ? desc.getMode() : (mode != null ? mode : Mode.STRICT);
+
+ if ( value == null ) {
if ( desc.isRequired() ) {
- result.getErrors().add("No value provided");
+ setResult(context, "No value provided");
}
} else {
final List<Object> values;
@@ -63,36 +79,44 @@
} else {
// single value
values = null;
- validateValue(desc, value, result);
+ validateValue(context, value);
}
if ( values != null ) {
// array or collection
for(final Object val : values) {
- validateValue(desc, val, result);
+ validateValue(context, val);
}
- validateList(desc, values, result.getErrors());
+ validateList(context, values);
}
if ( desc.getDeprecated() != null ) {
- result.getWarnings().add(desc.getDeprecated());
+ context.result.getWarnings().add(desc.getDeprecated());
}
}
- return result;
+ return context.result;
}
+ void setResult(final Context context, final String msg) {
+ if ( context.validationMode == Mode.STRICT ) {
+ context.result.getErrors().add(msg);
+ } else if ( context.validationMode == Mode.LENIENT || context.validationMode == Mode.DEFINITIVE ) {
+ context.result.getWarnings().add(msg);
+ }
+ }
+
/**
* Validate a multi value
* @param prop The property description
* @param values The values
* @param messages The messages to record errors
*/
- void validateList(final PropertyDescription prop, final List<Object> values, final List<String> messages) {
- if ( prop.getCardinality() > 0 && values.size() > prop.getCardinality() ) {
- messages.add("Array/collection contains too many elements, only " + prop.getCardinality() + " allowed");
+ void validateList(final Context context, final List<Object> values) {
+ if ( context.description.getCardinality() > 0 && values.size() > context.description.getCardinality() ) {
+ setResult(context, "Array/collection contains too many elements, only " + context.description.getCardinality() + " allowed");
}
- if ( prop.getIncludes() != null ) {
- for(final String inc : prop.getIncludes()) {
+ if ( context.description.getIncludes() != null ) {
+ for(final String inc : context.description.getIncludes()) {
boolean found = false;
for(final Object val : values) {
if ( inc.equals(val.toString())) {
@@ -101,12 +125,12 @@
}
}
if ( !found ) {
- messages.add("Required included value " + inc + " not found");
+ setResult(context, "Required included value " + inc + " not found");
}
}
}
- if ( prop.getExcludes() != null ) {
- for(final String exc : prop.getExcludes()) {
+ if ( context.description.getExcludes() != null ) {
+ for(final String exc : context.description.getExcludes()) {
boolean found = false;
for(final Object val : values) {
if ( exc.equals(val.toString())) {
@@ -115,7 +139,7 @@
}
}
if ( found ) {
- messages.add("Required excluded value " + exc + " found");
+ setResult(context, "Required excluded value " + exc + " found");
}
}
}
@@ -123,8 +147,7 @@
private static final List<String> PLACEHOLDERS = Arrays.asList("$[env:", "$[secret:", "$[prop:");
- void validateValue(final PropertyDescription desc, final Object value, final PropertyValidationResult result) {
- final List<String> messages = result.getErrors();
+ void validateValue(final Context context, final Object value) {
if ( value != null ) {
// check for placeholder
boolean hasPlaceholder = false;
@@ -138,285 +161,294 @@
}
}
if ( !hasPlaceholder ) {
- switch ( desc.getType() ) {
- case BOOLEAN : validateBoolean(desc, value, messages);
+ switch ( context.description.getType() ) {
+ case BOOLEAN : validateBoolean(context, value);
break;
- case BYTE : validateByte(desc, value, messages);
+ case BYTE : validateByte(context, value);
break;
- case CHARACTER : validateCharacter(desc, value, messages);
+ case CHARACTER : validateCharacter(context, value);
break;
- case DOUBLE : validateDouble(desc, value, messages);
+ case DOUBLE : validateDouble(context, value);
break;
- case FLOAT : validateFloat(desc, value, messages);
+ case FLOAT : validateFloat(context, value);
break;
- case INTEGER : validateInteger(desc, value, messages);
+ case INTEGER : validateInteger(context, value);
break;
- case LONG : validateLong(desc, value, messages);
+ case LONG : validateLong(context, value);
break;
- case SHORT : validateShort(desc, value, messages);
+ case SHORT : validateShort(context, value);
break;
- case STRING : validateRequired(desc, value, messages);
+ case STRING : validateRequired(context, value);
break;
- case EMAIL : validateEmail(desc, value, messages);
+ case EMAIL : validateEmail(context, value);
break;
- case PASSWORD : validatePassword(desc, value, messages, false);
+ case PASSWORD : validatePassword(context, value, false);
break;
- case URL : validateURL(desc, value, messages);
+ case URL : validateURL(context, value);
break;
- case PATH : validatePath(desc, value, messages);
+ case PATH : validatePath(context, value);
break;
- default : messages.add("Unable to validate value - unknown property type : " + desc.getType());
+ default : context.result.getErrors().add("Unable to validate value - unknown property type : " + context.description.getType());
}
- validateRegex(desc, value, messages);
- validateOptions(desc, value, messages);
+ validateRegex(context, value);
+ validateOptions(context, value);
} else {
// placeholder is present
- if ( desc.getType() == PropertyType.PASSWORD ) {
- validatePassword(desc, value, messages, true);
- } else if ( desc.getType() == PropertyType.STRING ) {
+ if ( context.description.getType() == PropertyType.PASSWORD ) {
+ validatePassword(context, value, true);
+ } else if ( context.description.getType() == PropertyType.STRING ) {
// any string is valid, we only mark the result as skipped if a regex or options are set
- if ( desc.getRegex() != null || desc.getOptions() != null || desc.isRequired() ) {
- result.markSkipped();
+ if ( context.description.getRegex() != null || context.description.getOptions() != null || context.description.isRequired() ) {
+ context.result.markSkipped();
}
} else {
- result.markSkipped();
+ context.result.markSkipped();
}
}
} else {
- messages.add("Null value provided for validation");
+ setResult(context, "Null value provided for validation");
}
}
- void validateRequired(final PropertyDescription prop, final Object value, final List<String> messages) {
- if ( prop.isRequired() ) {
+ void validateRequired(final Context context, final Object value) {
+ if ( context.description.isRequired() ) {
final String val = value.toString();
if ( val.isEmpty() ) {
- messages.add("Value is required");
+ setResult(context, "Value is required");
}
}
}
- void validateBoolean(final PropertyDescription prop, final Object value, final List<String> messages) {
+ void validateBoolean(final Context context, final Object value) {
if ( ! (value instanceof Boolean) ) {
if ( value instanceof String ) {
final String v = (String)value;
if ( ! v.equalsIgnoreCase("true") && !v.equalsIgnoreCase("false") ) {
- messages.add("Boolean value must either be true or false, but not " + value);
+ setResult(context, "Boolean value must either be true or false, but not " + value);
}
} else {
- messages.add("Boolean value must either be of type Boolean or String : " + value);
+ setResult(context, "Boolean value must either be of type Boolean or String : " + value);
}
}
}
- void validateByte(final PropertyDescription prop, final Object value, final List<String> messages) {
+ void validateByte(final Context context, final Object value) {
if ( ! (value instanceof Byte) ) {
if ( value instanceof String ) {
final String v = (String)value;
try {
- validateRange(prop, Byte.valueOf(v), messages);
+ validateRange(context, Byte.valueOf(v));
} catch ( final NumberFormatException nfe ) {
- messages.add("Value is not a valid Byte : " + value);
+ setResult(context, "Value is not a valid Byte : " + value);
}
} else if ( value instanceof Number ) {
- validateRange(prop, ((Number)value).byteValue(), messages);
+ validateRange(context, ((Number)value).byteValue());
} else {
- messages.add("Byte value must either be of type Byte or String : " + value);
+ setResult(context, "Byte value must either be of type Byte or String : " + value);
}
} else {
- validateRange(prop, (Byte)value, messages);
+ validateRange(context, (Byte)value);
}
}
- void validateShort(final PropertyDescription prop, final Object value, final List<String> messages) {
+ void validateShort(final Context context, final Object value) {
if ( ! (value instanceof Short) ) {
if ( value instanceof String ) {
final String v = (String)value;
try {
- validateRange(prop, Short.valueOf(v), messages);
+ validateRange(context, Short.valueOf(v));
} catch ( final NumberFormatException nfe ) {
- messages.add("Value is not a valid Short : " + value);
+ setResult(context, "Value is not a valid Short : " + value);
}
} else if ( value instanceof Number ) {
- validateRange(prop, ((Number)value).shortValue(), messages);
+ validateRange(context, ((Number)value).shortValue());
} else {
- messages.add("Short value must either be of type Short or String : " + value);
+ setResult(context, "Short value must either be of type Short or String : " + value);
}
} else {
- validateRange(prop, (Short)value, messages);
+ validateRange(context, (Short)value);
}
}
- void validateInteger(final PropertyDescription prop, final Object value, final List<String> messages) {
+ void validateInteger(final Context context, final Object value) {
if ( ! (value instanceof Integer) ) {
if ( value instanceof String ) {
final String v = (String)value;
try {
- validateRange(prop, Integer.valueOf(v), messages);
+ validateRange(context, Integer.valueOf(v));
} catch ( final NumberFormatException nfe ) {
- messages.add("Value is not a valid Integer : " + value);
+ setResult(context, "Value is not a valid Integer : " + value);
}
} else if ( value instanceof Number ) {
- validateRange(prop, ((Number)value).intValue(), messages);
+ validateRange(context, ((Number)value).intValue());
} else {
- messages.add("Integer value must either be of type Integer or String : " + value);
+ setResult(context, "Integer value must either be of type Integer or String : " + value);
}
} else {
- validateRange(prop, (Integer)value, messages);
+ validateRange(context, (Integer)value);
}
}
- void validateLong(final PropertyDescription prop, final Object value, final List<String> messages) {
+ void validateLong(final Context context, final Object value) {
if ( ! (value instanceof Long) ) {
if ( value instanceof String ) {
final String v = (String)value;
try {
- validateRange(prop, Long.valueOf(v), messages);
+ validateRange(context, Long.valueOf(v));
} catch ( final NumberFormatException nfe ) {
- messages.add("Value is not a valid Long : " + value);
+ setResult(context, "Value is not a valid Long : " + value);
}
} else if ( value instanceof Number ) {
- validateRange(prop, ((Number)value).longValue(), messages);
+ validateRange(context, ((Number)value).longValue());
} else {
- messages.add("Long value must either be of type Long or String : " + value);
+ setResult(context, "Long value must either be of type Long or String : " + value);
}
} else {
- validateRange(prop, (Long)value, messages);
+ validateRange(context, (Long)value);
}
}
- void validateFloat(final PropertyDescription prop, final Object value, final List<String> messages) {
+ void validateFloat(final Context context, final Object value) {
if ( ! (value instanceof Float) ) {
if ( value instanceof String ) {
final String v = (String)value;
try {
- validateRange(prop, Float.valueOf(v), messages);
+ validateRange(context, Float.valueOf(v));
} catch ( final NumberFormatException nfe ) {
- messages.add("Value is not a valid Float : " + value);
+ setResult(context, "Value is not a valid Float : " + value);
}
} else if ( value instanceof Number ) {
- validateRange(prop, ((Number)value).floatValue(), messages);
+ validateRange(context, ((Number)value).floatValue());
} else {
- messages.add("Float value must either be of type Float or String : " + value);
+ setResult(context, "Float value must either be of type Float or String : " + value);
}
} else {
- validateRange(prop, (Float)value, messages);
+ validateRange(context, (Float)value);
}
}
- void validateDouble(final PropertyDescription prop, final Object value, final List<String> messages) {
+ void validateDouble(final Context context, final Object value) {
if ( ! (value instanceof Double) ) {
if ( value instanceof String ) {
final String v = (String)value;
try {
- validateRange(prop, Double.valueOf(v), messages);
+ validateRange(context, Double.valueOf(v));
} catch ( final NumberFormatException nfe ) {
- messages.add("Value is not a valid Double : " + value);
+ setResult(context, "Value is not a valid Double : " + value);
}
} else if ( value instanceof Number ) {
- validateRange(prop, ((Number)value).doubleValue(), messages);
+ validateRange(context, ((Number)value).doubleValue());
} else {
- messages.add("Double value must either be of type Double or String : " + value);
+ setResult(context, "Double value must either be of type Double or String : " + value);
}
} else {
- validateRange(prop, (Double)value, messages);
+ validateRange(context, (Double)value);
}
}
- void validateCharacter(final PropertyDescription prop, final Object value, final List<String> messages) {
+ void validateCharacter(final Context context, final Object value) {
if ( ! (value instanceof Character) ) {
if ( value instanceof String ) {
final String v = (String)value;
if ( v.length() > 1 ) {
- messages.add("Value is not a valid Character : " + value);
+ setResult(context, "Value is not a valid Character : " + value);
}
} else {
- messages.add("Character value must either be of type Character or String : " + value);
+ setResult(context, "Character value must either be of type Character or String : " + value);
}
}
}
- void validateURL(final PropertyDescription prop, final Object value, final List<String> messages) {
+ void validateURL(final Context context, final Object value) {
final String val = value.toString();
try {
new URL(val);
} catch ( final MalformedURLException mue) {
- messages.add("Value is not a valid URL : " + val);
+ setResult(context, "Value is not a valid URL : " + val);
}
}
- void validateEmail(final PropertyDescription prop, final Object value, final List<String> messages) {
+ void validateEmail(final Context context, final Object value) {
final String val = value.toString();
// poor man's validation (should probably use InternetAddress)
if ( !val.contains("@") ) {
- messages.add("Not a valid email address " + val);
+ setResult(context, "Not a valid email address " + val);
}
}
- void validatePassword(final PropertyDescription desc, final Object value, final List<String> messages, final boolean hasPlaceholder) {
+ void validatePassword(final Context context, final Object value, final boolean hasPlaceholder) {
if ( !hasPlaceholder ) {
- messages.add("Value for a password must use a placeholder");
+ setResult(context, "Value for a password must use a placeholder");
}
}
- void validatePath(final PropertyDescription prop, final Object value, final List<String> messages) {
+ void validatePath(final Context context, final Object value) {
final String val = value.toString();
// poor man's validation
if ( !val.startsWith("/") ) {
- messages.add("Not a valid path " + val);
+ setResult(context, "Not a valid path " + val);
}
}
- void validateRange(final PropertyDescription prop, final Number value, final List<String> messages) {
- if ( prop.getRange() != null ) {
- if ( prop.getRange().getMin() != null ) {
+ void validateRange(final Context context, final Number value) {
+ if ( context.description.getRange() != null ) {
+ if ( context.description.getRange().getMin() != null ) {
if ( value instanceof Float || value instanceof Double ) {
- final double min = prop.getRange().getMin().doubleValue();
+ final double min = context.description.getRange().getMin().doubleValue();
if ( value.doubleValue() < min ) {
- messages.add("Value " + value + " is too low; should not be lower than " + prop.getRange().getMin());
+ setResult(context, "Value " + value + " is too low; should not be lower than " + context.description.getRange().getMin());
}
} else {
- final long min = prop.getRange().getMin().longValue();
+ final long min = context.description.getRange().getMin().longValue();
if ( value.longValue() < min ) {
- messages.add("Value " + value + " is too low; should not be lower than " + prop.getRange().getMin());
+ setResult(context, "Value " + value + " is too low; should not be lower than " + context.description.getRange().getMin());
}
}
}
- if ( prop.getRange().getMax() != null ) {
+ if ( context.description.getRange().getMax() != null ) {
if ( value instanceof Float || value instanceof Double ) {
- final double max = prop.getRange().getMax().doubleValue();
+ final double max = context.description.getRange().getMax().doubleValue();
if ( value.doubleValue() > max ) {
- messages.add("Value " + value + " is too high; should not be higher than " + prop.getRange().getMax());
+ setResult(context, "Value " + value + " is too high; should not be higher than " + context.description.getRange().getMax());
}
} else {
- final long max = prop.getRange().getMax().longValue();
+ final long max = context.description.getRange().getMax().longValue();
if ( value.longValue() > max ) {
- messages.add("Value " + value + " is too high; should not be higher than " + prop.getRange().getMax());
+ setResult(context, "Value " + value + " is too high; should not be higher than " + context.description.getRange().getMax());
}
}
}
}
}
- void validateRegex(final PropertyDescription prop, final Object value, final List<String> messages) {
- if ( prop.getRegexPattern() != null ) {
- if ( !prop.getRegexPattern().matcher(value.toString()).matches() ) {
- messages.add("Value " + value + " does not match regex " + prop.getRegex());
+ void validateRegex(final Context context, final Object value) {
+ if ( context.description.getRegexPattern() != null ) {
+ if ( !context.description.getRegexPattern().matcher(value.toString()).matches() ) {
+ setResult(context, "Value " + value + " does not match regex " + context.description.getRegex());
}
}
}
- void validateOptions(final PropertyDescription prop, final Object value, final List<String> messages) {
- if ( prop.getOptions() != null ) {
+ void validateOptions(final Context context, final Object value) {
+ if ( context.description.getOptions() != null ) {
boolean found = false;
- for(final Option opt : prop.getOptions()) {
+ for(final Option opt : context.description.getOptions()) {
if ( opt.getValue().equals(value.toString() ) ) {
found = true;
}
}
if ( !found ) {
- messages.add("Value " + value + " does not match provided options");
+ setResult(context, "Value " + value + " does not match provided options");
}
}
}
+
+ static final class Context {
+
+ public final PropertyValidationResult result = new PropertyValidationResult();
+
+ public PropertyDescription description;
+
+ public Mode validationMode;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/package-info.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/package-info.java
index 69a037c..0cc3da8 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/package-info.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/package-info.java
@@ -17,7 +17,7 @@
* under the License.
*/
-@org.osgi.annotation.versioning.Version("1.1.0")
+@org.osgi.annotation.versioning.Version("1.2.0")
package org.apache.sling.feature.extension.apiregions.api.config.validation;
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurableEntityTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurableEntityTest.java
index 10851d5..b9ea7d7 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurableEntityTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurableEntityTest.java
@@ -43,12 +43,14 @@
entity.setTitle("t");
entity.setDescription("x");
entity.getPropertyDescriptions().put("a", new PropertyDescription());
+ entity.setMode(Mode.SILENT);
entity.clear();
assertTrue(entity.getAttributes().isEmpty());
assertNull(entity.getDeprecated());
assertNull(entity.getTitle());
assertNull(entity.getDescription());
assertTrue(entity.getPropertyDescriptions().isEmpty());
+ assertNull(entity.getMode());
}
@Test public void testFromJSONObject() throws IOException {
@@ -81,4 +83,17 @@
final CE entity = new CE();
entity.fromJSONObject(ext.getJSONStructure().asJsonObject());
}
+
+ @Test public void testSerialisingMode() throws IOException {
+ final CE entity = new CE();
+ entity.setMode(Mode.SILENT);
+
+ final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
+ ext.setJSON("{ \"mode\" : \"SILENT\"}");
+
+ assertEquals(ext.getJSONStructure().asJsonObject(), entity.toJSONObject());
+ entity.clear();
+ entity.fromJSONObject(ext.getJSONStructure().asJsonObject());
+ assertEquals(Mode.SILENT, entity.getMode());
+ }
}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApiTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApiTest.java
index f6ca5f0..ff16509 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApiTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApiTest.java
@@ -77,6 +77,7 @@
entity.getInternalFrameworkProperties().add("iprop");
entity.setRegion(Region.GLOBAL);
entity.getFeatureToRegionCache().put(ArtifactId.parse("g:a:1"), Region.GLOBAL);
+ entity.setMode(Mode.SILENT);
entity.clear();
assertTrue(entity.getAttributes().isEmpty());
assertTrue(entity.getConfigurationDescriptions().isEmpty());
@@ -87,6 +88,7 @@
assertTrue(entity.getInternalFrameworkProperties().isEmpty());
assertNull(entity.getRegion());
assertTrue(entity.getFeatureToRegionCache().isEmpty());
+ assertEquals(Mode.STRICT, entity.getMode());
}
@Test public void testFromJSONObject() throws IOException {
@@ -118,6 +120,7 @@
assertEquals(Region.INTERNAL, entity.getRegion());
assertEquals(Region.INTERNAL, entity.getFeatureToRegionCache().get(ArtifactId.parse("g:a1:feature:1.0.0")));
assertEquals(Region.GLOBAL, entity.getFeatureToRegionCache().get(ArtifactId.parse("g:a2:feature:1.7.3")));
+ assertEquals(Mode.STRICT, entity.getMode());
}
@Test public void testToJSONObject() throws IOException {
@@ -132,7 +135,7 @@
entity.setRegion(Region.INTERNAL);
entity.getFeatureToRegionCache().put(ArtifactId.parse("g:a1:feature:1.0.0"), Region.INTERNAL);
entity.getFeatureToRegionCache().put(ArtifactId.parse("g:a2:feature:1.7.3"), Region.GLOBAL);
-
+ entity.setMode(Mode.SILENT);
final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
ext.setJSON("{ \"a\" : 5, \"configurations\" : { \"pid\": {}}, " +
"\"factory-configurations\" : { \"factory\" : {}}," +
@@ -141,7 +144,8 @@
"\"internal-factory-configurations\" : [\"ifactory\"],"+
"\"internal-framework-properties\" : [\"iprop\"],"+
"\"region\" : \"INTERNAL\","+
- "\"region-cache\" : {\"g:a1:feature:1.0.0\" : \"INTERNAL\", \"g:a2:feature:1.7.3\" : \"GLOBAL\"}}");
+ "\"region-cache\" : {\"g:a1:feature:1.0.0\" : \"INTERNAL\", \"g:a2:feature:1.7.3\" : \"GLOBAL\"}," +
+ "\"mode\" : \"SILENT\"}");
assertEquals(ext.getJSONStructure().asJsonObject(), entity.toJSONObject());
}
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescriptionTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescriptionTest.java
index 4fa0583..2e90b3b 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescriptionTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescriptionTest.java
@@ -52,6 +52,7 @@
entity.setVariable("var");
entity.setType(PropertyType.BYTE);
entity.setDefaultValue("default");
+ entity.setMode(Mode.SILENT);
entity.clear();
assertTrue(entity.getAttributes().isEmpty());
assertNull(entity.getDeprecated());
@@ -68,6 +69,7 @@
assertFalse(entity.isRequired());
assertEquals(PropertyType.STRING, entity.getType());
assertNull(entity.getDefaultValue());
+ assertNull(entity.getMode());
}
@Test public void testFromJSONObject() throws IOException {
@@ -164,4 +166,17 @@
// expected
}
}
+
+ @Test public void testSerialisingMode() throws IOException {
+ final PropertyDescription entity = new PropertyDescription();
+ entity.setMode(Mode.SILENT);
+
+ final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
+ ext.setJSON("{ \"mode\" : \"SILENT\"}");
+
+ assertEquals(ext.getJSONStructure().asJsonObject(), entity.toJSONObject());
+ entity.clear();
+ entity.fromJSONObject(ext.getJSONStructure().asJsonObject());
+ assertEquals(Mode.SILENT, entity.getMode());
+ }
}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidatorTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidatorTest.java
index dd544bd..b2803f4 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidatorTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidatorTest.java
@@ -24,6 +24,7 @@
import java.util.Collections;
import java.util.List;
+import org.apache.sling.feature.extension.apiregions.api.config.Mode;
import org.apache.sling.feature.extension.apiregions.api.config.Option;
import org.apache.sling.feature.extension.apiregions.api.config.PropertyDescription;
import org.apache.sling.feature.extension.apiregions.api.config.PropertyType;
@@ -34,289 +35,205 @@
private final PropertyValidator validator = new PropertyValidator();
- @Test public void testValidateWithNull() {
- final PropertyDescription prop = new PropertyDescription();
+ /**
+ * Helper method to validate an error based on the validation mode
+ */
+ private void validateError(final PropertyDescription prop, final Object value) {
+ validateError(prop, value, 1);
+ }
+
+ /**
+ * Helper method to validate an error based on the validation mode
+ */
+ private void validateError(final PropertyDescription prop, final Object value, final int errors) {
PropertyValidationResult result;
- // prop not required - no error
- result = validator.validate(null, prop);
- assertTrue(result.getErrors().isEmpty());
+ // error - strict mode
+ result = validator.validate(value, prop, Mode.STRICT);
+ assertEquals(errors, result.getErrors().size());
+ assertFalse(result.isValid());
+ assertFalse(result.isSkipped());
+
+ // error - mode lenient
+ result = validator.validate(value, prop, Mode.LENIENT);
+ assertEquals(errors, result.getWarnings().size());
assertTrue(result.isValid());
assertFalse(result.isSkipped());
+ // error - mode silent
+ result = validator.validate(value, prop, Mode.SILENT);
+ assertTrue(result.getWarnings().isEmpty());
+ assertTrue(result.isValid());
+ assertFalse(result.isSkipped());
+
+ // error - mode definitive
+ result = validator.validate(value, prop, Mode.DEFINITIVE);
+ assertEquals(errors, result.getWarnings().size());
+ assertTrue(result.isValid());
+ assertFalse(result.isSkipped());
+
+ // error - mode silent definitive
+ result = validator.validate(value, prop, Mode.SILENT_DEFINITIVE);
+ assertTrue(result.getWarnings().isEmpty());
+ assertTrue(result.isValid());
+ assertFalse(result.isSkipped());
+ }
+
+ /**
+ * Helper method to validate that a value is valid and not skipped
+ */
+ private void validateValid(final PropertyDescription prop, final Object value) {
+ final PropertyValidationResult result = validator.validate(value, prop);
+ assertTrue(result.isValid());
+ assertFalse(result.isSkipped());
+ assertTrue(result.getErrors().isEmpty());
+ }
+
+ @Test public void testValidateWithNull() {
+ final PropertyDescription prop = new PropertyDescription();
+
+ // prop not required - no error
+ validateValid(prop, null);
+
// prop required - error
prop.setRequired(true);
- result = validator.validate(null, prop);
- assertEquals(1, result.getErrors().size());
- assertFalse(result.isValid());
- assertFalse(result.isSkipped());
+ validateError(prop, null);
}
@Test public void testValidateBoolean() {
final PropertyDescription prop = new PropertyDescription();
prop.setType(PropertyType.BOOLEAN);
- PropertyValidationResult result;
- result = validator.validate(Boolean.TRUE, prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
+ validateValid(prop, Boolean.TRUE);
+ validateValid(prop, Boolean.FALSE);
+ validateValid(prop, "TRUE");
+ validateValid(prop, "FALSE");
- result = validator.validate(Boolean.FALSE, prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("TRUE", prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("FALSE", prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("yes", prop);
- assertEquals(1, result.getErrors().size());
- assertFalse(result.isSkipped());
-
- result = validator.validate(1, prop);
- assertEquals(1, result.getErrors().size());
- assertFalse(result.isSkipped());
+ validateError(prop, "yes");
+ validateError(prop, 1);
}
@Test public void testValidateByte() {
final PropertyDescription prop = new PropertyDescription();
prop.setType(PropertyType.BYTE);
- PropertyValidationResult result;
+ validateValid(prop, (byte)1);
+ validateValid(prop, "1");
+ validateValid(prop, 1);
- result = validator.validate((byte)1, prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("1", prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("yes", prop);
- assertEquals(1, result.getErrors().size());
- assertFalse(result.isSkipped());
-
- result = validator.validate(1, prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
+ validateError(prop, "yes");
}
@Test public void testValidateShort() {
final PropertyDescription prop = new PropertyDescription();
prop.setType(PropertyType.SHORT);
- PropertyValidationResult result;
+ validateValid(prop, (short)1);
+ validateValid(prop, "1");
+ validateValid(prop, 1);
- result = validator.validate((short)1, prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("1", prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("yes", prop);
- assertEquals(1, result.getErrors().size());
- assertFalse(result.isSkipped());
-
- result = validator.validate(1, prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
+ validateError(prop, "yes");
}
@Test public void testValidateInteger() {
final PropertyDescription prop = new PropertyDescription();
prop.setType(PropertyType.INTEGER);
- PropertyValidationResult result;
+ validateValid(prop, "1");
+ validateValid(prop, 1);
- result = validator.validate(1, prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("1", prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("yes", prop);
- assertEquals(1, result.getErrors().size());
- assertFalse(result.isSkipped());
-
- result = validator.validate(1, prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
+ validateError(prop, "yes");
}
@Test public void testValidateLong() {
final PropertyDescription prop = new PropertyDescription();
prop.setType(PropertyType.LONG);
- PropertyValidationResult result;
+ validateValid(prop, 1L);
+ validateValid(prop, "1");
+ validateValid(prop, 1);
- result = validator.validate(1L, prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("1", prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("yes", prop);
- assertEquals(1, result.getErrors().size());
- assertFalse(result.isSkipped());
-
- result = validator.validate(1, prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
+ validateError(prop, "yes");
}
@Test public void testValidateFloat() {
final PropertyDescription prop = new PropertyDescription();
prop.setType(PropertyType.FLOAT);
- PropertyValidationResult result;
+ validateValid(prop, 1.1);
+ validateValid(prop, "1.1");
+ validateValid(prop, 1);
- result = validator.validate(1.1, prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("1.1", prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("yes", prop);
- assertEquals(1, result.getErrors().size());
- assertFalse(result.isSkipped());
-
- result = validator.validate(1, prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
+ validateError(prop, "yes");
}
@Test public void testValidateDouble() {
final PropertyDescription prop = new PropertyDescription();
prop.setType(PropertyType.DOUBLE);
- PropertyValidationResult result;
+ validateValid(prop, 1.1d);
+ validateValid(prop, "1.1");
+ validateValid(prop, 1);
- result = validator.validate(1.1d, prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("1.1", prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("yes", prop);
- assertEquals(1, result.getErrors().size());
- assertFalse(result.isSkipped());
-
- result = validator.validate(1, prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
+ validateError(prop, "yes");
}
@Test public void testValidateChar() {
final PropertyDescription prop = new PropertyDescription();
prop.setType(PropertyType.CHARACTER);
- PropertyValidationResult result;
+ validateValid(prop, 'x');
+ validateValid(prop, "y");
- result = validator.validate('x', prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("y", prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("yes", prop);
- assertEquals(1, result.getErrors().size());
- assertFalse(result.isSkipped());
+ validateError(prop, "yes");
}
@Test public void testValidateUrl() {
final PropertyDescription prop = new PropertyDescription();
prop.setType(PropertyType.URL);
- PropertyValidationResult result;
+ validateValid(prop, "https://sling.apache.org/documentation");
- result = validator.validate("https://sling.apache.org/documentation", prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("hello world", prop);
- assertEquals(1, result.getErrors().size());
- assertFalse(result.isSkipped());
+ validateError(prop, "hello world");
}
@Test public void testValidateEmail() {
final PropertyDescription prop = new PropertyDescription();
prop.setType(PropertyType.EMAIL);
- PropertyValidationResult result;
+ validateValid(prop, "a@b.com");
- result = validator.validate("a@b.com", prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("hello world", prop);
- assertEquals(1, result.getErrors().size());
- assertFalse(result.isSkipped());
+ validateError(prop, "hello world");
}
@Test public void testValidatePassword() {
final PropertyDescription prop = new PropertyDescription();
prop.setType(PropertyType.PASSWORD);
- PropertyValidationResult result;
+ validateValid(prop, null);
+ validateValid(prop, "$[secret:dbpassword]");
- result = validator.validate(null, prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("secret", prop);
- assertFalse(result.isValid());
-
- result = validator.validate("$[secret:dbpassword]", prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
+ validateError(prop, "secret");
}
@Test public void testValidatePath() {
final PropertyDescription prop = new PropertyDescription();
prop.setType(PropertyType.PATH);
- PropertyValidationResult result;
+ validateValid(prop, "/a/b/c");
- result = validator.validate("/a/b/c", prop);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("hello world", prop);
- assertEquals(1, result.getErrors().size());
- assertFalse(result.isSkipped());
+ validateError(prop, "hello world");
}
@Test public void testValidateString() {
final PropertyDescription desc = new PropertyDescription();
- PropertyValidationResult result;
- result = validator.validate("hello world", desc);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
-
- result = validator.validate("$[prop:KEY]", desc);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
+ validateValid(desc, "hello world");
+ validateValid(desc, "$[prop:KEY]");
// skip if required
desc.setRequired(true);
- result = validator.validate("$[prop:KEY]", desc);
+ PropertyValidationResult result = validator.validate("$[prop:KEY]", desc);
assertTrue(result.isValid());
assertTrue(result.isSkipped());
desc.setRequired(false);
@@ -336,96 +253,72 @@
desc.setRegex(null);
// empty string - not required
- result = validator.validate("", desc);
- assertTrue(result.isValid());
- assertFalse(result.isSkipped());
+ validateValid(desc, "");
// empty string - required
desc.setRequired(true);
- result = validator.validate("", desc);
- assertFalse(result.isValid());
- assertFalse(result.isSkipped());
+ validateError(desc, "");
desc.setRequired(false);
}
@Test public void testValidateRange() {
- final List<String> messages = new ArrayList<>();
- final PropertyDescription prop = new PropertyDescription();
-
+ final PropertyDescription description = new PropertyDescription();
+ description.setType(PropertyType.INTEGER);
+
// no range set
- validator.validateRange(prop, 2, messages);
- assertTrue(messages.isEmpty());
+ validateValid(description, 2);
// empty range set
- prop.setRange(new Range());
- validator.validateRange(prop, 2, messages);
- assertTrue(messages.isEmpty());
+ description.setRange(new Range());
+ validateValid(description, 2);
// min set
- prop.getRange().setMin(5);
- validator.validateRange(prop, 5, messages);
- assertTrue(messages.isEmpty());
- validator.validateRange(prop, 6, messages);
- assertTrue(messages.isEmpty());
- validator.validateRange(prop, 4, messages);
- assertEquals(1, messages.size());
- messages.clear();
+ description.getRange().setMin(5);
+ validateValid(description, 5);
+ validateValid(description, 6);
- validator.validateRange(prop, 5.0, messages);
- assertTrue(messages.isEmpty());
- validator.validateRange(prop, 6.0, messages);
- assertTrue(messages.isEmpty());
- validator.validateRange(prop, 4.0, messages);
- assertEquals(1, messages.size());
- messages.clear();
+ validateError(description, 4);
+
+ validateValid(description, 5.0);
+ validateValid(description, 6.0);
+
+ validateError(description, 4.0);
// max set
- prop.getRange().setMax(6);
- validator.validateRange(prop, 5, messages);
- assertTrue(messages.isEmpty());
- validator.validateRange(prop, 6, messages);
- assertTrue(messages.isEmpty());
- validator.validateRange(prop, 7, messages);
- assertEquals(1, messages.size());
- messages.clear();
+ description.getRange().setMax(6);
+ validateValid(description, 5);
+ validateValid(description, 6);
- validator.validateRange(prop, 5.0, messages);
- assertTrue(messages.isEmpty());
- validator.validateRange(prop, 6.0, messages);
- assertTrue(messages.isEmpty());
- validator.validateRange(prop, 7.0, messages);
- assertEquals(1, messages.size());
- messages.clear();
+ validateError(description, 7);
+
+ validateValid(description, 5.0);
+ validateValid(description, 6.0);
+
+ validateError(description, 7.0);
}
@Test public void testValidateRegex() {
- final List<String> messages = new ArrayList<>();
final PropertyDescription prop = new PropertyDescription();
// no regex
- validator.validateRegex(prop, "hello world", messages);
- validator.validateRegex(prop, "world", messages);
- assertTrue(messages.isEmpty());
+ validateValid(prop, "hello world");
+ validateValid(prop, "world");
// regex
prop.setRegex("h(.*)");
- validator.validateRegex(prop, "hello world", messages);
- assertTrue(messages.isEmpty());
- validator.validateRegex(prop, "world", messages);
- assertEquals(1, messages.size());
- messages.clear();
+ validateValid(prop, "hello world");
+
+ validateError(prop, "world");
}
@Test public void testValidateOptions() {
- final List<String> messages = new ArrayList<>();
final PropertyDescription prop = new PropertyDescription();
// no options
- validator.validateOptions(prop, "foo", messages);
- validator.validateOptions(prop, "bar", messages);
- assertTrue(messages.isEmpty());
+ validateValid(prop, "foo");
+ validateValid(prop, "bar");
- // options
+ // options - with foo
final List<Option> options = new ArrayList<>();
final Option o1 = new Option();
o1.setValue("foo");
@@ -434,13 +327,10 @@
options.add(o1);
options.add(o2);
prop.setOptions(options);
- validator.validateOptions(prop, "foo", messages);
- assertTrue(messages.isEmpty());
- validator.validateOptions(prop, "bar", messages);
- assertEquals(1, messages.size());
- messages.clear();
- validator.validateOptions(prop, 7, messages);
- assertTrue(messages.isEmpty());
+
+ validateValid(prop, "foo");
+ validateError(prop, "bar");
+ validateValid(prop, 7);
}
@Test public void testValidateList() {
@@ -452,49 +342,32 @@
values.add("c");
// default cardinality - no excludes/includes
- PropertyValidationResult result;
- result = validator.validate(values, prop);
- assertEquals(1, result.getErrors().size());
- assertFalse(result.isSkipped());
+ validateError(prop, values);
// cardinality 3 - no excludes/includes
prop.setCardinality(3);
- result = validator.validate(values, prop);
- assertFalse(result.isSkipped());
- assertTrue(result.getErrors().isEmpty());
+ validateValid(prop, values);
values.add("d");
- result = validator.validate(values, prop);
- assertFalse(result.isSkipped());
- assertEquals(1, result.getErrors().size());
+ validateError(prop, values);
// excludes
prop.setExcludes(new String[] {"d", "e"});
- result = validator.validate(values, prop);
- assertFalse(result.isSkipped());
- assertEquals(2, result.getErrors().size()); // cardinality and exclude
+ validateError(prop, values, 2);
values.remove("d");
- result = validator.validate(values, prop);
- assertFalse(result.isSkipped());
- assertTrue(result.getErrors().isEmpty());
+ validateValid(prop, values);
// includes
prop.setIncludes(new String[] {"b"});
- result = validator.validate(values, prop);
- assertFalse(result.isSkipped());
- assertTrue(result.getErrors().isEmpty());
+ validateValid(prop, values);
prop.setIncludes(new String[] {"x"});
- result = validator.validate(values, prop);
- assertFalse(result.isSkipped());
- assertEquals(1, result.getErrors().size());
+ validateError(prop, values);
values.add("x");
values.remove("a");
- result = validator.validate(values, prop);
- assertFalse(result.isSkipped());
- assertTrue(result.getErrors().isEmpty());
+ validateValid(prop, values);
}
@Test public void testValidateArray() {
@@ -503,48 +376,31 @@
String[] values = new String[] {"a", "b", "c"};
// default cardinality - no excludes/includes
- PropertyValidationResult result;
- result = validator.validate(values, prop);
- assertFalse(result.isSkipped());
- assertEquals(1, result.getErrors().size());
+ validateError(prop, values);
// cardinality 3 - no excludes/includes
prop.setCardinality(3);
- result = validator.validate(values, prop);
- assertFalse(result.isSkipped());
- assertTrue(result.getErrors().isEmpty());
+ validateValid(prop, values);
values = new String[] {"a", "b", "c", "d"};
- result = validator.validate(values, prop);
- assertFalse(result.isSkipped());
- assertEquals(1, result.getErrors().size());
+ validateError(prop, values);
// excludes
prop.setExcludes(new String[] {"d", "e"});
- result = validator.validate(values, prop);
- assertFalse(result.isSkipped());
- assertEquals(2, result.getErrors().size()); // cardinality and exclude
+ validateError(prop, values, 2);
values = new String[] {"a", "b", "c"};
- result = validator.validate(values, prop);
- assertFalse(result.isSkipped());
- assertTrue(result.getErrors().isEmpty());
+ validateValid(prop, values);
// includes
prop.setIncludes(new String[] {"b"});
- result = validator.validate(values, prop);
- assertFalse(result.isSkipped());
- assertTrue(result.getErrors().isEmpty());
+ validateValid(prop, values);
prop.setIncludes(new String[] {"x"});
- result = validator.validate(values, prop);
- assertFalse(result.isSkipped());
- assertEquals(1, result.getErrors().size());
+ validateError(prop, values);
values = new String[] {"b", "c", "x"};
- result = validator.validate(values, prop);
- assertFalse(result.isSkipped());
- assertTrue(result.getErrors().isEmpty());
+ validateValid(prop, values);
}
@Test public void testDeprecation() {