package org.activityinfo.model.form; import com.google.common.base.Strings; import com.google.common.collect.Sets; import org.activityinfo.model.resource.Record; import org.activityinfo.model.resource.ResourceId; import org.activityinfo.model.type.*; import javax.validation.constraints.NotNull; import java.util.Collections; import java.util.Set; import static com.google.common.base.Preconditions.checkNotNull; /** * The smallest logical unit of data entry. */ public class FormField extends FormElement { private final ResourceId id; private String code; private String label; private String description; private String relevanceConditionExpression; private FieldType type; private boolean readOnly; private boolean visible = true; private Set<ResourceId> superProperties = Sets.newHashSet(); private boolean required; public FormField(ResourceId id) { checkNotNull(id); this.id = id; } public ResourceId getId() { return id; } public String getName() { return id.asString(); } /** * @return user-assigned code for this field that can be * used in expressions. */ public String getCode() { return code; } public FormField setCode(String code) { this.code = code; return this; } public boolean hasCode() { return code != null; } /** * * @return true if {@code} is a valid code, starting with a letter and * containing only letters, numbers, and the underscore symbol */ public static boolean isValidCode(String code) { return code != null && code.matches("^[A-Za-z][A-Za-z0-9_]*"); } @NotNull public String getLabel() { return label; } public FormField setLabel(String label) { assert label != null; this.label = label; return this; } public String getRelevanceConditionExpression() { return relevanceConditionExpression; } public void setRelevanceConditionExpression(String relevanceConditionExpression) { this.relevanceConditionExpression = relevanceConditionExpression; } /** * @return an extended description of this field, presented to be * presented to the user during data entry */ @NotNull public String getDescription() { return description; } public FormField setDescription(String description) { this.description = description; return this; } public FieldType getType() { assert type != null : "type is missing for " + id; return type; } public FormField setType(FieldType type) { this.type = type; return this; } /** * * @return true if this field requires a response before submitting the form */ public boolean isRequired() { return required; } public FormField setRequired(boolean required) { this.required = required; return this; } public boolean hasRelevanceConditionExpression() { return !Strings.isNullOrEmpty(relevanceConditionExpression); } /** * @return true if this field is read-only. */ public boolean isReadOnly() { return readOnly; } public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; } /** * @return true if this field is visible to the user */ public boolean isVisible() { return visible; } public FormField setVisible(boolean visible) { this.visible = visible; return this; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; FormField formField = (FormField) o; if (id != null ? !id.equals(formField.id) : formField.id != null) return false; return true; } @Override public int hashCode() { return id != null ? id.hashCode() : 0; } @Override public String toString() { return "FormField{" + "id=" + id + ", label=" + label + ", type=" + type.getTypeClass().getId() + '}'; } public Set<ResourceId> getSuperProperties() { return superProperties; } public void addSuperProperty(ResourceId propertyId) { superProperties.add(propertyId); } public void setSuperProperties(Set<ResourceId> superProperties) { this.superProperties = superProperties; } public FormField setSuperProperty(ResourceId superProperty) { this.superProperties = Collections.singleton(superProperty); return this; } public boolean isSubPropertyOf(ResourceId parentProperty) { return this.superProperties.contains(parentProperty); } @Override public Record asRecord() { assert type != null : id + " has no type"; Record record = new Record(); record.set("id", id.asString()); record.set("code", code); record.set("description", description); record.set("label", label); record.set("type", toRecord(type)); record.set("required", required); record.set("visible", visible); record.set("relevanceConditionExpression", relevanceConditionExpression); if(!superProperties.isEmpty()) { record.set("superProperties", new ReferenceValue(superProperties).asRecord()); } return record; } private Record toRecord(FieldType type) { Record record = new Record(); record.set("typeClass", type.getTypeClass().getId()); if(type instanceof ParametrizedFieldType) { record.set("parameters", ((ParametrizedFieldType)type).getParameters()); } return record; } public static FormField fromRecord(Record record) { FormField formField = new FormField(ResourceId.valueOf(record.getString("id"))) .setDescription(record.isString("description")) .setLabel(Strings.nullToEmpty(record.isString("label"))) .setType(typeFromRecord(record.getRecord("type"))) .setVisible(record.getBoolean("visible", true)) .setRequired(record.getBoolean("required", false)); if (record.has("relevanceConditionExpression")) { formField.setRelevanceConditionExpression(record.getString("relevanceConditionExpression")); } if(record.has("superProperties")) { ReferenceValue superProperties = ReferenceValue.fromRecord(record.getRecord("superProperties")); formField.setSuperProperties(superProperties.getResourceIds()); } if(record.has("code")) { formField.setCode(record.getString("code")); } return formField; } private static FieldType typeFromRecord(Record record) { String typeClassId = record.getString("typeClass"); FieldTypeClass typeClass = TypeRegistry.get().getTypeClass(typeClassId); if(typeClass instanceof ParametrizedFieldTypeClass) { return ((ParametrizedFieldTypeClass)typeClass).deserializeType(record.getRecord("parameters")); } else { return typeClass.createType(); } } }