/*******************************************************************************
* Copyright 2013 Open mHealth
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.openmhealth.reference.concordia;
import java.util.Iterator;
import java.util.List;
import name.jenkins.paul.john.concordia.exception.ConcordiaException;
import name.jenkins.paul.john.concordia.schema.StringSchema;
import name.jenkins.paul.john.concordia.validator.DataValidator;
import name.jenkins.paul.john.concordia.validator.SchemaValidator;
import name.jenkins.paul.john.concordia.validator.ValidationController;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.NullNode;
/**
* <p>
* A type and data validator for enums within string schemas.
* </p>
*
* @author John Jenkins
*/
public class EnumValidator
implements SchemaValidator<StringSchema>, DataValidator<StringSchema> {
/**
* The schema field name for string schemas that may be present and, if so,
* must be an array of strings that are valid values for this data.
*/
public static final String ENUM_SCHEMA_FIELD = "allowed_values";
/**
* Verifies that if {@link #ENUM_SCHEMA_FIELD} exists, it is an array of
* strings.
*
* @param schema
* The {@link StringSchema} to validate.
*
* @param controller
* The controller to use in the event that a sub-schema existed and
* needed to be validated as well.
*/
@Override
public void validate(
final StringSchema schema,
final ValidationController controller)
throws ConcordiaException {
// Attempt to get the enum definition.
Object enumField = schema.getAdditionalFields().get(ENUM_SCHEMA_FIELD);
// If the definition exists, validate it.
if(enumField != null) {
// It must be a JSON array.
if(enumField instanceof List) {
// Iterate through the elements.
Iterator<?> enumFieldIter =
((List<?>) enumField).iterator();
// Each element must be a string.
while(enumFieldIter.hasNext()) {
if(! (enumFieldIter.next() instanceof String)) {
throw
new ConcordiaException(
"An " +
ENUM_SCHEMA_FIELD +
" entry is not a string: " +
enumField.toString());
}
}
}
// It is not a JSON array.
else {
throw
new ConcordiaException(
"The " +
ENUM_SCHEMA_FIELD +
" field list must be a JSON array: " +
enumField.toString());
}
}
}
/**
* Verifies that any data point for this schema is one of the required enum
* values.
*
* @param schema
* The schema to reference when validating the data.
*
* @param data
* The data to validate.
*
* @param controller
* The controller to use in the event that there is sub-data that
* also needed to be validated.
*/
@Override
public void validate(
final StringSchema schema,
final JsonNode data,
final ValidationController controller)
throws ConcordiaException {
// If the data is null, we can ignore it because that value shouldn't
// be in our list. Null will be caught by our default, required
// validation, which will only allow this if the schema defines this
// field as optional.
if((data == null) || (data instanceof NullNode)) {
return;
}
// Get the value of this node.
// We can also safely assume that this will return us a non-null value
// as our default, required validation would have first run to ensure
// that it is a TextNode.
String value = data.textValue();
// Attempt to get the enum definition.
Object enumField = schema.getAdditionalFields().get(ENUM_SCHEMA_FIELD);
// If the definition exists, validate it.
if(enumField != null) {
// We can safely cast here, because our validation above took care
// of this for us.
@SuppressWarnings("unchecked")
List<String> enumFieldIter = (List<String>) enumField;
// Check each of our allowed values against the given value.
if(! enumFieldIter.contains(value)) {
// If one was not found, throw an exception.
throw
new ConcordiaException(
"The value, '" +
value +
"', is not in our list of acceptable values: " +
enumField.toString());
}
}
}
}