package org.activityinfo.model.form;
import com.google.common.collect.Maps;
import org.activityinfo.model.expr.eval.*;
import org.activityinfo.model.resource.Resource;
import org.activityinfo.model.resource.ResourceId;
import org.activityinfo.model.type.*;
import org.activityinfo.model.type.enumerated.EnumValue;
import org.activityinfo.model.type.enumerated.EnumItem;
import org.activityinfo.model.type.enumerated.EnumType;
import org.activityinfo.model.type.expr.CalculatedFieldType;
import java.util.Map;
public class FormEvalContext implements EvalContext {
/**
* Maps form ids and codes to a value source, either
* a static field or calculated field
*/
private final Map<String, ValueSource> symbolMap = Maps.newHashMap();
private final Map<String, ValueSource> fieldMap = Maps.newHashMap();
private Resource formInstance;
public FormEvalContext(FormClass formClass) {
for (FormField field : formClass.getFields()) {
ValueSource source = createValueSource(field);
if (field.hasCode()) {
symbolMap.put(field.getCode(), source);
}
symbolMap.put(field.getId().asString(), source);
fieldMap.put(field.getId().asString(), source);
}
// TODO: cleanup hack: enum values need to be treated as constants, not symbols!
for (FormField field : formClass.getFields()) {
if (field.getType() instanceof EnumType) {
for (EnumItem item : ((EnumType) field.getType()).getValues()) {
symbolMap.put(item.getId().asString(), new ConstantValue(new EnumValue(item.getId())));
}
}
}
}
public FormEvalContext(FormClass formClass, Resource resource) {
this(formClass);
setInstance(resource);
}
public FormEvalContext(FormClass formClass, FormInstance instance) {
this(formClass);
setInstance(instance.asResource());
}
public void setInstance(Resource resource) {
this.formInstance = resource;
}
public void setInstance(FormInstance instance) {
setInstance(instance.asResource());
}
public ResourceId getId() {
return formInstance.getId();
}
public FieldValue getFieldValue(String fieldName) {
assert formInstance != null;
try {
return fieldMap.get(fieldName).getValue(formInstance, this);
} catch(Exception e) {
return new ErrorValue(e);
}
}
public FieldValue getFieldValue(ResourceId fieldId) {
return getFieldValue(fieldId.asString());
}
private ValueSource createValueSource(FormField field) {
if (field.getType() instanceof CalculatedFieldType) {
return new CalculatedField(field);
} else {
return new StaticField(field);
}
}
public FieldType resolveFieldType(ResourceId fieldId) {
return fieldMap.get(fieldId.asString()).resolveType(this);
}
@Override
public FieldValue resolveSymbol(String symbolName) {
return lookupSymbol(symbolName).getValue(formInstance, this);
}
@Override
public FieldType resolveSymbolType(String name) {
return lookupSymbol(name).resolveType(this);
}
private ValueSource lookupSymbol(String symbolName) {
ValueSource valueSource = symbolMap.get(symbolName);
if (valueSource == null) {
// todo : we must fix it, here as temporary solution if symbol name can't be resolved we consider it as ReferenceValue
return new ConstantValue(new ReferenceValue(ResourceId.valueOf(symbolName)));
// throw new RuntimeException("Unknown symbol '" + symbolName + "'");
}
return valueSource;
}
}