package org.activityinfo.model.expr.eval; import org.activityinfo.model.expr.ExprNode; import org.activityinfo.model.expr.ExprParser; import org.activityinfo.model.expr.diagnostic.CircularReferenceException; import org.activityinfo.model.expr.diagnostic.ExprException; import org.activityinfo.model.form.FormField; import org.activityinfo.model.resource.Resource; import org.activityinfo.model.type.ErrorValue; import org.activityinfo.model.type.FieldType; import org.activityinfo.model.type.FieldValue; import org.activityinfo.model.type.MissingFieldType; import org.activityinfo.model.type.expr.CalculatedFieldType; import java.util.logging.Level; import java.util.logging.Logger; public class CalculatedField implements FieldValueSource { private static final Logger LOGGER = Logger.getLogger(CalculatedField.class.getName()); private final FormField field; private ExprNode expr; private ErrorValue errorValue; /** * True if this expression is being evaluated. Used to trap circular * references. */ private boolean evaluating = false; public CalculatedField(FormField field) { this.field = field; CalculatedFieldType type = (CalculatedFieldType) field.getType(); try { expr = ExprParser.parse(type.getExpression().getExpression()); } catch(ExprException e) { LOGGER.log(Level.WARNING, "Expression failed to parse: " + type.getExpression(), e); expr = null; errorValue = new ErrorValue(e); } } @Override public FieldValue getValue(Resource instance, EvalContext context) { if(errorValue != null) { return errorValue; } if(evaluating) { throw new CircularReferenceException(field.getCode()); } evaluating = true; try { return expr.evaluate(context); } finally { evaluating = false; } } @Override public FieldType resolveType(EvalContext context) { if(errorValue != null) { return MissingFieldType.INSTANCE; } try { return expr.resolveType(context); } catch(Exception e) { return MissingFieldType.INSTANCE; } } @Override public FormField getField() { return field; } }