/*
* Copyright 2007 T-Rank AS
*
* 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 no.trank.openpipe.config;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import no.trank.openpipe.api.PipelineException;
import no.trank.openpipe.config.annotation.NotEmpty;
import no.trank.openpipe.config.annotation.NotNull;
import no.trank.openpipe.config.annotation.NullNotEmpty;
/**
* A utility class for validating fields of an instance according to the annotations: {@link NotNull}, {@link NotEmpty}
* and {@link NullNotEmpty}.
*
* @version $Revision$
*/
public class BeanValidator {
private static final Logger log = LoggerFactory.getLogger(BeanValidator.class);
/**
* Validates fields of the given object, including fields of super classes.
* <br>
* Currently {@link NotNull}, {@link NotEmpty} and {@link NullNotEmpty} are supported. {@link NotEmpty} and
* {@link NullNotEmpty} are supported for fields of type {@link CharSequence}, {@link Collection} and
* {@link Map}. For unsupported types a warning is logged, but no exception is thrown.
*
* @param obj the object whose fields should be validated.
*
* @throws PipelineException if {@link NotNull} is declared for a field that is <tt>null</tt>, or if {@link NotEmpty}
* is declared for a field that is <tt>empty</tt> or <tt>null</tt>.
*/
public static void validate(Object obj) throws PipelineException {
Class<?> clazz = obj.getClass();
while (clazz != null && clazz != Object.class) {
validate(obj, clazz);
clazz = clazz.getSuperclass();
}
}
private static void validate(Object obj, Class<?> clazz) throws PipelineException {
final Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
final Restriction restriction = getRestriction(field);
if (restriction != Restriction.NONE) {
final Object o = getObject(obj, field);
if (o != null) {
if (restriction.notEmpty() && empty(o)) {
throw new PipelineException("Field " + field.getName() + " cannot be empty");
}
} else if (restriction.notNull()) {
throw new PipelineException("Field " + field.getName() + " cannot be null");
}
}
}
}
private static boolean empty(Object obj) {
if (obj instanceof CharSequence) {
return ((CharSequence) obj).length() <= 0;
}
if (obj instanceof Collection) {
return ((Collection<?>) obj).isEmpty();
}
if (obj instanceof Map) {
return ((Map<?, ?>) obj).isEmpty();
}
log.warn("Empty check not supported for type: {}", obj.getClass().getName());
return false;
}
private static Object getObject(Object obj, Field field) {
field.setAccessible(true);
try {
return field.get(obj);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private static Restriction getRestriction(AccessibleObject object) {
if (declaresNotEmpty(object)) {
return Restriction.NOT_EMPTY;
}
if (declaresNullNotEmpty(object)) {
return Restriction.NULL_NOT_EMPTY;
}
return declaresNotNull(object) ? Restriction.NOT_NULL : Restriction.NONE;
}
private static boolean declaresNotNull(AccessibleObject field) {
return field.getAnnotation(NotNull.class) != null;
}
private static boolean declaresNotEmpty(AccessibleObject field) {
return field.getAnnotation(NotEmpty.class) != null;
}
private static boolean declaresNullNotEmpty(AccessibleObject field) {
return field.getAnnotation(NullNotEmpty.class) != null;
}
private static enum Restriction {
NONE(false, false), NOT_NULL(true, false), NOT_EMPTY(true, true), NULL_NOT_EMPTY(false, true);
private final boolean notNull;
private final boolean notEmpty;
Restriction(boolean notNull, boolean notEmpty) {
this.notNull = notNull;
this.notEmpty = notEmpty;
}
public boolean notNull() {
return notNull;
}
public boolean notEmpty() {
return notEmpty;
}
}
}