package alien4cloud.tosca.parser.impl.advanced;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import alien4cloud.tosca.parser.impl.base.BaseParserFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.stereotype.Component;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeTuple;
import com.google.common.collect.Maps;
import org.alien4cloud.tosca.model.definitions.PropertyConstraint;
import org.alien4cloud.tosca.model.definitions.constraints.*;
import alien4cloud.tosca.parser.*;
import alien4cloud.tosca.parser.impl.ErrorCode;
import alien4cloud.tosca.parser.impl.base.ScalarParser;
import lombok.AllArgsConstructor;
/**
* Parse a constraint based on the specified operator
*/
@Component
public class ConstraintParser extends AbstractTypeNodeParser implements INodeParser<PropertyConstraint> {
@Resource
private ScalarParser scalarParser;
@Resource
private BaseParserFactory baseParserFactory;
private Map<String, ConstraintParsingInfo> constraintBuildersMap;
public ConstraintParser() {
super("Constraints");
}
@PostConstruct
public void init() {
constraintBuildersMap = Maps.newHashMap();
constraintBuildersMap.put("equal", new ConstraintParsingInfo(EqualConstraint.class, "equal", scalarParser));
constraintBuildersMap.put("greater_than", new ConstraintParsingInfo(GreaterThanConstraint.class, "greaterThan", scalarParser));
constraintBuildersMap.put("greater_or_equal", new ConstraintParsingInfo(GreaterOrEqualConstraint.class, "greaterOrEqual", scalarParser));
constraintBuildersMap.put("less_than", new ConstraintParsingInfo(LessThanConstraint.class, "lessThan", scalarParser));
constraintBuildersMap.put("less_or_equal", new ConstraintParsingInfo(LessOrEqualConstraint.class, "lessOrEqual", scalarParser));
constraintBuildersMap.put("in_range",
new ConstraintParsingInfo(InRangeConstraint.class, "inRange", baseParserFactory.getListParser(scalarParser, "in range constraint expression")));
constraintBuildersMap.put("valid_values", new ConstraintParsingInfo(ValidValuesConstraint.class, "validValues",
baseParserFactory.getListParser(scalarParser, "valid values constraint expression")));
constraintBuildersMap.put("length", new ConstraintParsingInfo(LengthConstraint.class, "length", scalarParser));
constraintBuildersMap.put("min_length", new ConstraintParsingInfo(MinLengthConstraint.class, "minLength", scalarParser));
constraintBuildersMap.put("max_length", new ConstraintParsingInfo(MaxLengthConstraint.class, "maxLength", scalarParser));
constraintBuildersMap.put("pattern", new ConstraintParsingInfo(PatternConstraint.class, "pattern", scalarParser));
}
@Override
public PropertyConstraint parse(Node node, ParsingContextExecution context) {
if (node instanceof MappingNode) {
MappingNode mappingNode = (MappingNode) node;
if (mappingNode.getValue().size() == 1) {
NodeTuple nodeTuple = mappingNode.getValue().get(0);
String operator = ParserUtils.getScalar(nodeTuple.getKeyNode(), context);
// based on the operator we should load the right constraint.
return parseConstraint(operator, nodeTuple.getKeyNode(), nodeTuple.getValueNode(), context);
} else {
ParserUtils.addTypeError(node, context.getParsingErrors(), "Constraint");
}
} else {
ParserUtils.addTypeError(node, context.getParsingErrors(), "Constraint");
}
return null;
}
private PropertyConstraint parseConstraint(String operator, Node keyNode, Node expressionNode, ParsingContextExecution context) {
ConstraintParsingInfo info = constraintBuildersMap.get(operator);
if (info == null) {
context.getParsingErrors().add(new ParsingError(ParsingErrorLevel.WARNING, ErrorCode.UNKNOWN_CONSTRAINT, "Constraint parsing issue",
keyNode.getStartMark(), "Unknown constraint operator, will be ignored.", keyNode.getEndMark(), operator));
return null;
}
PropertyConstraint constraint;
try {
constraint = info.constraintClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new ParsingTechnicalException("Unable to create constraint.", e);
}
BeanWrapper target = new BeanWrapperImpl(constraint);
parseAndSetValue(target, null, expressionNode, context, new MappingTarget(info.expressionPropertyName, info.expressionParser));
return constraint;
}
@AllArgsConstructor(suppressConstructorProperties = true)
private class ConstraintParsingInfo {
private Class<? extends PropertyConstraint> constraintClass;
private String expressionPropertyName;
private INodeParser<?> expressionParser;
}
}