blob: 35677c84ce567a38385cf6e0aff34fa9810adb7a [file] [log] [blame]
/*
* 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.bval.util;
import java.util.List;
import java.util.Map;
import org.apache.bval.DynamicMetaBean;
import org.apache.bval.model.MetaBean;
import org.apache.bval.model.MetaProperty;
import org.apache.bval.model.Validation;
import org.apache.bval.model.ValidationContext;
import org.apache.bval.model.ValidationListener;
/**
* Stateless helper methods used by the validators.
*
* @author Carlos Vara
*/
public class ValidationHelper {
/**
* Interface implemented by the call-back object passed to
* {@link ValidationHelper#validateContext(ValidationContext, ValidateCallback, boolean)}
* . Its {@link #validate()} method will be called accordingly for every
* dispatch.
*/
public static interface ValidateCallback {
void validate();
}
/**
* validate a complex 'bean' with related beans according to
* validation rules in 'metaBean'
*
* @param context
* - the context is initialized with: <br>
* &nbsp;&nbsp;bean - the root object start validation at
* or a collection of root objects <br>
* &nbsp;&nbsp;metaBean - the meta information for the root
* object(s)
* @param context
* The current validation context.
*/
static public void validateContext(ValidationContext<?> context, ValidateCallback s, boolean treatMapsLikeBeans) {
if (context.getBean() != null) {
if (!treatMapsLikeBeans && context.getBean() instanceof Map<?, ?>) {
validateMapInContext(context, s);
} else if (context.getBean() instanceof Iterable<?>) {
validateIterableInContext(context, s);
} else if (context.getBean() instanceof Object[]) {
validateArrayInContext(context, s);
} else { // to One Bean (or Map like Bean)
validateBeanInContext(context, s);
}
}
}
/**
* Validates a single object.
*
* @param <VL>
* @param context
* The validation context, its current bean must be a single
* object.
* @param s
*/
static protected <VL extends ValidationListener> void validateBeanInContext(ValidationContext<VL> context, ValidateCallback s) {
if (getDynamicMetaBean(context) != null) {
context.setMetaBean(getDynamicMetaBean(context).resolveMetaBean(context.getBean()));
}
s.validate();
}
/**
* Iterates the values of an array, setting the current context
* appropriately and validating each value.
*
* @param <VL>
* @param context
* The validation context, its current bean must be an array.
*/
static protected <VL extends ValidationListener> void validateArrayInContext(ValidationContext<VL> context, ValidateCallback s) {
int index = 0;
DynamicMetaBean dyn = getDynamicMetaBean(context);
Object[] array = (Object[]) context.getBean();
MetaBean metaBean = context.getMetaBean();
context.setCurrentIndex(null);
try {
for (Object each : array) {
context.setCurrentIndex(index++);
if (each == null) {
continue; // Null values are not validated
}
if (dyn != null) {
context.setBean(each, dyn.resolveMetaBean(each));
} else {
context.setBean(each);
}
s.validate();
}
} finally {
context.moveUp(array, metaBean);
}
}
/**
* Iterates the values of an {@link Iterable} object, setting the current
* context appropriately and validating each value.
*
* @param <VL>
* @param context
* The validation context, its current bean must implement
* {@link Iterable}.
*/
static protected <VL extends ValidationListener> void validateIterableInContext(ValidationContext<VL> context, ValidateCallback s) {
final boolean positional = context.getBean() instanceof List<?>;
int index = 0;
Iterable<?> iterable = (Iterable<?>) context.getBean();
MetaBean metaBean = context.getMetaBean();
context.setCurrentIndex(null);
try {
// jsr303 spec: Each object provided by the iterator is validated.
final DynamicMetaBean dyn = getDynamicMetaBean(context);
for (Object each : iterable) {
if (positional) {
context.setCurrentIndex(index++);
}
if (each == null) {
continue; // Null values are not validated
}
if (dyn != null) {
context.setBean(each, dyn.resolveMetaBean(each));
} else {
context.setBean(each);
}
s.validate();
}
} finally {
context.moveUp(iterable, metaBean);
}
}
/**
* Iterates the values of a {@link Map}, setting the current context
* appropriately and validating each value.
*
* @param <VL>
* @param context
* The validation context, its current bean must implement
* {@link Map}.
*/
static protected <VL extends ValidationListener> void validateMapInContext(ValidationContext<VL> context, ValidateCallback s) {
// jsr303 spec: For Map, the value of each Map.Entry is validated (key
// is not validated).
Map<?, ?> currentBean = (Map<?, ?>) context.getBean();
MetaBean metaBean = context.getMetaBean();
final DynamicMetaBean dyn = getDynamicMetaBean(context);
context.setCurrentKey(null);
try {
for (Map.Entry<?, ?> entry : currentBean.entrySet()) {
Object value = entry.getValue();
if (value == null) {
continue;
}
context.setCurrentKey(entry.getKey());
if (dyn == null) {
context.setBean(value);
} else {
context.setBean(value, dyn.resolveMetaBean(value));
}
s.validate();
}
} finally {
context.moveUp(currentBean, metaBean);
}
}
/**
* @param <VL>
* @param context
* The current validation context.
* @return the current {@link DynamicMetaBean} in context, or
* <code>null</code> if the current meta bean is not dynamic.
*/
static private <VL extends ValidationListener> DynamicMetaBean getDynamicMetaBean(ValidationContext<VL> context) {
return context.getMetaBean() instanceof DynamicMetaBean ? (DynamicMetaBean) context.getMetaBean() : null;
}
/**
* Validate a single bean only, no related beans will be validated.
*/
static public <VL extends ValidationListener> void validateBean(ValidationContext<VL> context) {
// execute all property level validations
for (MetaProperty prop : context.getMetaBean().getProperties()) {
context.setMetaProperty(prop);
validateProperty(context);
}
// execute all bean level validations
context.setMetaProperty(null);
for (Validation validation : context.getMetaBean().getValidations()) {
validation.validate(context);
}
}
/**
* Validate a single property only. Performs all validations
* for this property.
*/
static public <VL extends ValidationListener> void validateProperty(ValidationContext<VL> context) {
for (Validation validation : context.getMetaProperty().getValidations()) {
validation.validate(context);
}
}
}