org.ovirt.api.metamodel.analyzer.ConstraintAnalyzer Maven / Gradle / Ivy
/*
* Copyright oVirt Authors
* SPDX-License-Identifier: Apache-2.0
*/
package org.ovirt.api.metamodel.analyzer;
import java.math.BigInteger;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.ovirt.api.metamodel.concepts.ArrayExpression;
import org.ovirt.api.metamodel.concepts.Attribute;
import org.ovirt.api.metamodel.concepts.AttributeExpression;
import org.ovirt.api.metamodel.concepts.BinaryExpression;
import org.ovirt.api.metamodel.concepts.Constraint;
import org.ovirt.api.metamodel.concepts.Expression;
import org.ovirt.api.metamodel.concepts.Link;
import org.ovirt.api.metamodel.concepts.LinkExpression;
import org.ovirt.api.metamodel.concepts.ListType;
import org.ovirt.api.metamodel.concepts.LiteralExpression;
import org.ovirt.api.metamodel.concepts.Method;
import org.ovirt.api.metamodel.concepts.Model;
import org.ovirt.api.metamodel.concepts.Name;
import org.ovirt.api.metamodel.concepts.Operator;
import org.ovirt.api.metamodel.concepts.Parameter;
import org.ovirt.api.metamodel.concepts.ParameterExpression;
import org.ovirt.api.metamodel.concepts.StructType;
import org.ovirt.api.metamodel.concepts.Type;
import org.ovirt.api.metamodel.concepts.UnaryExpression;
/**
* This class is responsible for analyzing the constraints used in the model language.
*/
public class ConstraintAnalyzer {
// References to the model and the method where the constraint is declared:
private Model model;
private Method method;
// The constraint that will be populated by this analyzer:
private Constraint constraint;
/**
* Sets the model.
*/
public void setModel(Model newModel) {
model = newModel;
}
/**
* Gets the method where the constraint is declared.
*/
public Method getMethod() {
return method;
}
/**
* Sets the method where the constraint is declared.
*/
public void setMethod(Method newMethod) {
method = newMethod;
}
/**
* Gets the constraint that is being populated by this analyzer.
*/
public Constraint getConstraint(Constraint constraint) {
return constraint;
}
/**
* Sets the constraint that will be populated by this analyzer.
*/
public void setConstraint(Constraint newConstraint) {
constraint = newConstraint;
}
/**
* Analyzes the given source code and populates the constraint.
*
* @param source the source code of the constraint
*/
public void analyzeSource(String source) {
// First analyze the expressions contained in the source:
ExpressionAnalyzer expressionAnalyzer = new ExpressionAnalyzer();
List expressions = expressionAnalyzer.analyzeExpressions(source);
// Transform all the expressions replacing methods and fields with parameters and attributes:
expressions = expressions.stream().map(this::transform).collect(Collectors.toList());
// Add the resulting expressions to the constraint:
constraint.addExpressions(expressions);
}
private Expression transformMethod(MethodExpression expression) {
// Transform the target:
Expression target = expression.getTarget();
if (target != null) {
target = transform(target);
}
// Transform the parameters:
List parameters = expression.getParameters();
if (!parameters.isEmpty()) {
throw new IllegalArgumentException("The parameters in expression \"" + expression + "\" aren't supported");
}
// Get the name of the method:
Name name = expression.getMethod();
// Calls with no target object should correspond to method parameters, or to static methods that have been
// statically imported:
if (target == null) {
Parameter parameter = method.getParameter(name);
if (parameter != null) {
ParameterExpression replacement = new ParameterExpression();
replacement.setType(parameter.getType());
replacement.setParameter(parameter);
return replacement;
}
}
// Calls with a target object should correspond to attributes of objects used as parameters:
if (target != null) {
Type type = target.getType();
if (type instanceof StructType) {
StructType struct = (StructType) type;
Optional attribute = struct.getAttribute(name);
if (attribute.isPresent()) {
AttributeExpression replacement = new AttributeExpression();
replacement.setType(attribute.get().getType());
replacement.setTarget(target);
replacement.setAttribute(attribute.get());
return replacement;
}
Optional link = struct.getLink(name);
if (link.isPresent()) {
LinkExpression replacement = new LinkExpression();
replacement.setType(link.get().getType());
replacement.setTarget(target);
replacement.setLink(link.get());
return replacement;
}
}
}
// Not supported:
throw new IllegalArgumentException("Don't know how to transform method call \"" + expression + "\"");
}
private Expression transformBinary(BinaryExpression expression) {
// Transform the operands:
Expression left = expression.getLeft();
if (left != null) {
left = transform(left);
}
Expression right = expression.getRight();
if (right != null) {
right = transform(right);
}
// Sort-cuts to the model primitive types:
Type bool = model.getBooleanType();
Type decimal = model.getDecimalType();
Type integer = model.getIntegerType();
// Compute the type of the expression:
Operator operator = expression.getOperator();
Type type = null;
switch (operator) {
case AND:
case EQUAL:
case GREATER_THAN:
case GREATER_THAN_OR_EQUAL:
case LESS_THAN:
case LESS_THAN_OR_EQUAL:
case NOT:
case NOT_EQUAL:
case OR:
type = bool;
break;
case ADD:
case DIVIDE:
case MULTIPLY:
case REMAINDER:
case SUBTRACT:
if (Objects.equals(left.getType(), decimal) || Objects.equals(right.getType(), decimal)) {
type = decimal;
}
else {
type = integer;
}
break;
}
// Return the result:
BinaryExpression result = new BinaryExpression();
result.setType(type);
result.setOperator(operator);
result.setLeft(left);
result.setRight(right);
return result;
}
private Expression transformUnary(UnaryExpression expression) {
// Transform the operand:
Expression operand = expression.getOperand();
if (operand == null) {
throw new IllegalArgumentException("The operand in expression \"" + expression + "\" is null");
}
operand = transform(operand);
// Compute the type:
Operator operator = expression.getOperator();
Type type;
switch (operator) {
case NOT:
type = model.getBooleanType();
break;
case SUBTRACT:
type = operand.getType();
break;
default:
throw new IllegalArgumentException(
"The operator \"" + operator + "\" in expression \"" + expression + "\" isn't supported"
);
}
// Return the result:
UnaryExpression result = new UnaryExpression();
result.setType(type);
result.setOperator(operator);
result.setOperand(operand);
return result;
}
private Expression transformArray(ArrayExpression expression) {
// Transform the array:
Expression array = expression.getArray();
if (array == null) {
throw new IllegalArgumentException("The array in array expression \"" + expression + "\" is null");
}
array = transform(array);
// Transform the index:
Expression index = expression.getIndex();
if (index == null) {
throw new IllegalArgumentException("The index in array expression \"" + expression + "\" is null");
}
index = transform(index);
// Compute the type:
Type type = null;
Type arrayType = array.getType();
if (arrayType instanceof ListType) {
ListType listType = (ListType) arrayType;
type = listType.getElementType();
}
// Return the result:
ArrayExpression result = new ArrayExpression();
result.setType(type);
result.setArray(array);
result.setIndex(index);
return result;
}
private Expression transformField(FieldExpression expression) {
// Transform the target:
Expression target = expression.getTarget();
if (target != null) {
target = transform(target);
}
// Get the field:
Name field = expression.getField();
// Return the result:
FieldExpression result = new FieldExpression();
result.setTarget(target);
result.setField(field);
return result;
}
private Expression transformLiteral(LiteralExpression expression) {
// Compute the type:
Object value = expression.getValue();
Type type = null;
if (value != null) {
if (value instanceof Boolean) {
type = model.getBooleanType();
}
else if (value instanceof BigInteger) {
type = model.getIntegerType();
}
}
// Return the result:
LiteralExpression result = new LiteralExpression();
result.setValue(value);
result.setType(type);
return result;
}
private Expression transform(Expression expression) {
if (expression == null) {
return null;
}
if (expression instanceof ArrayExpression) {
return transformArray((ArrayExpression) expression);
}
if (expression instanceof BinaryExpression) {
return transformBinary((BinaryExpression) expression);
}
if (expression instanceof FieldExpression) {
return transformField((FieldExpression) expression);
}
if (expression instanceof LiteralExpression) {
return transformLiteral((LiteralExpression) expression);
}
if (expression instanceof MethodExpression) {
return transformMethod((MethodExpression) expression);
}
if (expression instanceof UnaryExpression) {
return transformUnary((UnaryExpression) expression);
}
throw new IllegalArgumentException(
"Don't know how to transform expressions of class \"" + expression.getClass().getName() + "\""
);
}
}