package rocks.inspectit.shared.cs.cmr.property.configuration.validator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import org.apache.commons.lang.StringUtils;
import rocks.inspectit.shared.cs.cmr.property.configuration.GroupedProperty;
import rocks.inspectit.shared.cs.cmr.property.configuration.SingleProperty;
import rocks.inspectit.shared.cs.cmr.property.configuration.validation.PropertyValidation;
import rocks.inspectit.shared.cs.cmr.property.configuration.validation.ValidationError;
import rocks.inspectit.shared.cs.cmr.property.update.IPropertyUpdate;
/**
* Abstract validator for all comparing validators.
*
* @author Ivan Senic
*
* @param <T>
*/
@XmlAccessorType(XmlAccessType.FIELD)
public abstract class AbstractComparingValidator<T> implements IGroupedProperyValidator, ISinglePropertyValidator<T> {
/**
* Name of the property to be tested. Can be <code>null</code> in case of
* {@link ISinglePropertyValidator} validator.
*/
@XmlAttribute(name = "property", required = false)
private String property;
/**
* What to compare the property to. It can be logical name of the property in case of group
* validation or literal in case of single validation.
*/
@XmlAttribute(name = "than", required = true)
private String than;
/**
* Compares property against another property. If comparing proves to be wrong the implementing
* class is responsible of adding the correct {@link ValidationError} to the
* {@link PropertyValidation}.
*
* @param property
* {@link SingleProperty}
* @param againstProperty
* {@link SingleProperty} to compare against.
* @param value
* Value to use for property
* @param against
* value to use for against property.
* @param propertyValidation
* {@link PropertyValidation}
*/
protected abstract void compare(SingleProperty<? extends T> property, SingleProperty<? extends T> againstProperty, T value, T against, PropertyValidation propertyValidation);
/**
* Compares property against value. If comparing proves to be wrong the implementing class is
* responsible of adding the correct {@link ValidationError} to the {@link PropertyValidation}.
*
* @param property
* {@link SingleProperty}
* @param value
* Value to use for validation.
* @param against
* Value to compare against.
* @param propertyValidation
* {@link PropertyValidation}
*/
protected abstract void compare(SingleProperty<? extends T> property, T value, T against, PropertyValidation propertyValidation);
/**
* {@inheritDoc}
*/
@Override
public void validate(SingleProperty<? extends T> property, PropertyValidation propertyValidation) {
T against = getAgainstValue(property, propertyValidation);
if (null != against) {
compare(property, property.getValue(), against, propertyValidation);
}
}
/**
* {@inheritDoc}
*/
@Override
public void validateForValue(SingleProperty<? extends T> property, PropertyValidation propertyValidation, T value) {
T against = getAgainstValue(property, propertyValidation);
if (null != against) {
compare(property, value, against, propertyValidation);
}
}
/**
* Retrieves against value from {@link #than} for given property. If parsing fails the
* {@link PropertyValidation} will be filled with error and <code>null</code> will be returned
* as value.
*
* @param property
* Property to get against value for.
* @param propertyValidation
* Validation to report errors.
* @return Value or <code>null</code> if parsing fails.
*/
private T getAgainstValue(SingleProperty<? extends T> property, PropertyValidation propertyValidation) {
T against = property.parseLiteral(than);
if (null == against) {
ValidationError validationError = new ValidationError(Collections.<SingleProperty<?>> singletonList(property),
"Validation of property " + property.getName() + " failed because literal (" + than + ") to compare against can not be parsed.");
propertyValidation.addValidationError(validationError);
}
return against;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public void validate(GroupedProperty groupProperty, PropertyValidation propertyValidation) {
SingleProperty<?> compare = getProperty(groupProperty, propertyValidation);
SingleProperty<?> against = getAgainstProperty(groupProperty, propertyValidation);
if ((null == compare) || (null == against)) {
return;
}
try {
compare((SingleProperty<T>) compare, (SingleProperty<T>) against, (T) compare.getValue(), (T) against.getValue(), propertyValidation);
} catch (Exception e) {
ValidationError validationError = new ValidationError(new ArrayList<>(groupProperty.getSingleProperties()),
"Validation of grouped property " + groupProperty.getName() + " failed because exception occurred during validation. Exception message: " + e.getMessage());
propertyValidation.addValidationError(validationError);
}
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public void validateForPropertyUpdates(GroupedProperty groupProperty, Collection<IPropertyUpdate<?>> propertyUpdates, PropertyValidation propertyValidation) {
SingleProperty<?> compareProperty = getProperty(groupProperty, propertyValidation);
SingleProperty<?> againstProperty = getAgainstProperty(groupProperty, propertyValidation);
if ((null == compareProperty) || (null == againstProperty)) {
return;
}
T value = (T) compareProperty.getValue();
T against = (T) againstProperty.getValue();
for (IPropertyUpdate<?> propertyUpdate : propertyUpdates) {
if (Objects.equals(compareProperty.getLogicalName(), propertyUpdate.getPropertyLogicalName())) {
value = (T) propertyUpdate.getUpdateValue();
}
if (Objects.equals(againstProperty.getLogicalName(), propertyUpdate.getPropertyLogicalName())) {
against = (T) propertyUpdate.getUpdateValue();
}
}
// no validation if nothing changed
if (Objects.equals(value, compareProperty.getValue()) && Objects.equals(against, againstProperty.getValue())) {
return;
}
try {
compare((SingleProperty<T>) compareProperty, (SingleProperty<T>) againstProperty, value, against, propertyValidation);
} catch (Exception e) {
ValidationError validationError = new ValidationError(new ArrayList<>(groupProperty.getSingleProperties()),
"Validation of grouped property " + groupProperty.getName() + " failed because exception occurred during validation. Exception message: " + e.getMessage());
propertyValidation.addValidationError(validationError);
}
}
/**
* Returns property defined by {@link #property} from the grouped property. If for some reason
* property can not be found, {@link PropertyValidation} will be filled with error(s) and
* <code>null</code> returned.
*
* @param groupProperty
* {@link GroupedProperty} to search in.
* @param propertyValidation
* {@link PropertyValidation} to report errors.
* @return {@link SingleProperty} or <code>null</code> if finding fails.
*/
private SingleProperty<?> getProperty(GroupedProperty groupProperty, PropertyValidation propertyValidation) {
if (StringUtils.isEmpty(property)) {
ValidationError validationError = new ValidationError(new ArrayList<>(groupProperty.getSingleProperties()),
"Validation of grouped property " + groupProperty.getName() + " failed because property logical name is not set.");
propertyValidation.addValidationError(validationError);
return null;
}
SingleProperty<?> p = groupProperty.forLogicalname(property);
if (null == p) {
ValidationError validationError = new ValidationError(new ArrayList<>(groupProperty.getSingleProperties()),
"Validation of grouped property " + groupProperty.getName() + " failed because property with logical name '" + property + "' does not exist.");
propertyValidation.addValidationError(validationError);
}
return p;
}
/**
* Returns property defined by {@link #this} from the grouped property. If for some reason
* property can not be found, {@link PropertyValidation} will be filled with error(s) and
* <code>null</code> returned.
*
* @param groupProperty
* {@link GroupedProperty} to search in.
* @param propertyValidation
* {@link PropertyValidation} to report errors.
* @return {@link SingleProperty} or <code>null</code> if finding fails.
*/
private SingleProperty<?> getAgainstProperty(GroupedProperty groupProperty, PropertyValidation propertyValidation) {
if (StringUtils.isEmpty(than)) {
ValidationError validationError = new ValidationError(new ArrayList<>(groupProperty.getSingleProperties()),
"Validation of grouped property " + groupProperty.getName() + " failed because logical name of the property to compare against is not set.");
propertyValidation.addValidationError(validationError);
return null;
}
SingleProperty<?> p = groupProperty.forLogicalname(than);
if (null == p) {
ValidationError validationError = new ValidationError(new ArrayList<>(groupProperty.getSingleProperties()),
"Validation of grouped property " + groupProperty.getName() + " failed because property with logical name '" + than + "' does not exist.");
propertyValidation.addValidationError(validationError);
}
return p;
}
/**
* Gets {@link #property}.
*
* @return {@link #property}
*/
public String getProperty() {
return property;
}
/**
* Sets {@link #property}.
*
* @param property
* New value for {@link #property}
*/
public void setProperty(String property) {
this.property = property;
}
/**
* Gets {@link #than}.
*
* @return {@link #than}
*/
public String getThan() {
return than;
}
/**
* Sets {@link #than}.
*
* @param than
* New value for {@link #than}
*/
public void setThan(String than) {
this.than = than;
}
}