package fr.imag.adele.apam.maven.plugin.validation;
import fr.imag.adele.apam.declarations.PropertyDefinition;
import fr.imag.adele.apam.maven.plugin.validation.property.Type;
import fr.imag.adele.apam.maven.plugin.validation.property.TypeParser;
import fr.imag.adele.apam.util.Attribute;
/**
* The common validations for property definitions in abstract components
*
* NOTE this class is intended to be sub-classed to specialize the verification
* in specific kinds of components.
*
* @author vega
*
*/
public class PropertyValidator extends AbstractValidator<PropertyDefinition,Void> {
/**
* The parser used to validate all defined property types
*/
private final TypeParser typeParser;
/**
* The validator used to validate substitution expressions
*/
private final ContextualExpressionValidator expressionValidator;
public PropertyValidator(ComponentValidator<?> parent) {
super(parent);
this.typeParser = new TypeParser();
this.expressionValidator = new ContextualExpressionValidator(this,typeParser);
}
/**
* Parses the specified type
*/
protected Type getType(PropertyDefinition property) {
return typeParser.parse(property.getType());
}
/**
* Validates the property definition
*/
public final Void validate(PropertyDefinition property) {
initializeState(property,typeParser.parse(property.getType()));
validateName();
validateType();
validateDefaultValue();
validateInstrumentation();
validateRefinement();
return null;
}
/**
* validates the property values
*/
public final void validate(String propertyName, String value) {
PropertyDefinition property = getComponent().getEffectiveDeclaration(getGroup()).getPropertyDefinition(propertyName);
Type propertyType = property != null ? typeParser.parse(property.getType()) : null;
initializeState(property,propertyType);
if (Attribute.isFinalAttribute(propertyName)) {
error("Property " + quoted(propertyName) + " is an internal property and cannot be redefined");
}
if (getProperty() == null) {
error("Property " + quoted(propertyName) + " is not defined");
return;
}
validateTypedValue(value);
validateRefinement(value);
}
/**
* Validate allowed property names
*/
protected void validateName() {
if (Attribute.isReservedAttributePrefix(getProperty().getName())) {
error("Property " + quoted(getProperty().getName()) + " is reserved");
}
}
/**
* Validate the type is well defined
*/
protected void validateType() {
if (getType() == null) {
error("Property " + quoted(getProperty().getName()) + " has invalid type "+getProperty().getType());
}
}
/**
* Validates the default value has a type compatible with the type of the property
*
*/
protected void validateDefaultValue() {
validateTypedValue(getProperty().getDefaultValue());
}
/**
* Validates that a value (that can have substitution expressions) is compatible with the type
* of the property
*/
private void validateTypedValue(String value) {
if (value == null)
return;
/*
* If the type could not be determined, we can not perform this validation
*/
if (getType() == null)
return;
if (expressionValidator.isContextualExpression(value)) {
/*
* If the value is a substitution expression try to parse and type it. Verify the type of the expression
* matches the type of the property
*/
Type expressionType = validate(value,expressionValidator);
if (expressionType != null && !expressionType.isAssignableTo(getType())) {
error("Property " + quoted(getProperty().getName()) + " has invalid value "+value+", expected type is "+getType()+" but found "+expressionType);
}
}
else {
/*
* For normal values, just validates the literal is accepted by the type
*/
if (getType().value(value) == null) {
error("Property " + quoted(getProperty().getName()) + " has invalid value "+value+" for type "+getType());
}
}
}
/**
* Validates if a instrumentation of a property is valid
*
* In general, instrumentation is not allowed at all level of abstraction. This method must
* be redefined in subclasses specialized for specific levels of abstraction that support it
*/
protected void validateInstrumentation() {
if (getProperty().getCallback() != null) {
error("Property " + quoted(getProperty().getName()) + ", cannot specify a callback for an abstract component");
}
if (getProperty().getField() != null) {
error("Property " + quoted(getProperty().getName()) + ", cannot specify an injection field for an abstract component");
}
}
/**
* Validates if a refinement of a property defined in the group is allowed
*
* In general, this is not allowed at all level of abstraction. This method must be redefined in subclasses
* specialized for specific levels of abstraction that support it
*/
protected void validateRefinement() {
PropertyDefinition groupDeclaration = getGroup() != null ? getGroup().getPropertyDefinition(getProperty().getName()) : null;
if (Attribute.isFinalAttribute(getProperty().getName())) {
error("Property " + quoted(getProperty().getName()) + " cannot be redefined");
}
else if (groupDeclaration != null && Attribute.isInheritedAttribute(getProperty().getName())) {
error("Property " + quoted(getProperty().getName()) + " is already defined in component "+getGroup().getName());
}
}
/**
* Validates if a refinement of a property value in the group is allowed
*
*/
protected void validateRefinement(String value) {
String groupvalue = getGroup() != null ? getGroup().getProperty(getProperty().getName()) : null;
if (groupvalue != null && value != null && Attribute.isInheritedAttribute(getProperty().getName())) {
error("Property " + quoted(getProperty().getName()) + " already valued in parent component "+getGroup().getName());
}
}
/**
* The property declaration currently validated
*/
private PropertyDefinition property;
/**
* The type of the property currently validated
*/
private Type propertyType;
/**
* Initializes the internal state of the validator
*/
protected void initializeState(PropertyDefinition property, Type propertyType) {
this.property = property;
this.propertyType = propertyType;
}
protected PropertyDefinition getProperty() {
return property;
}
protected Type getType() {
return propertyType;
}
/**
* Frees all references to the state of validation
*/
@Override
public void resetState() {
this.property = null;
this.propertyType = null;
super.resetState();
}
}