package org.activityinfo.model.form; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Strings; import com.google.common.collect.Lists; import org.activityinfo.model.resource.*; import javax.annotation.Nullable; import javax.validation.constraints.NotNull; import java.util.List; /** * The FormClass defines structure and semantics for {@code Resource}s. * * {@code Resources} which fulfill the contract described by a {@code FormClass} * are called {@code FormInstances}. */ public class FormClass implements IsResource, FormElementContainer { /** * Because FormClasses are themselves FormInstances, they have a class id of their own */ public static final ResourceId CLASS_ID = ResourceId.valueOf("_class"); /** * Instances of FormClass have one FormField: a label, which has its own * FormField id. It is defined at the application level to be a subproperty of * {@code _label} */ public static final String LABEL_FIELD_ID = "_class_label"; @NotNull private ResourceId id; private ResourceId ownerId; private String label; private String description; private final List<FormElement> elements = Lists.newArrayList(); public FormClass(ResourceId id) { Preconditions.checkNotNull(id); this.id = id; } public ResourceId getOwnerId() { return ownerId; } public FormClass setOwnerId(ResourceId ownerId) { this.ownerId = ownerId; return this; } public ResourceId getParentId() { return ownerId; } public void setParentId(ResourceId resourceId) { setOwnerId(resourceId); } public FormElementContainer getParent(FormElement childElement) { return getContainerElementsImpl(this, childElement); } private static FormElementContainer getContainerElementsImpl(FormElementContainer container, final FormElement searchElement) { if (container.getElements().contains(searchElement)) { return container; } for (FormElement elem : container.getElements()) { if (elem instanceof FormElementContainer) { final FormElementContainer result = getContainerElementsImpl((FormElementContainer) elem, searchElement); if (result != null) { return result; } } } return null; } public void traverse(FormElementContainer element, TraverseFunction traverseFunction) { for (FormElement elem : Lists.newArrayList(element.getElements())) { traverseFunction.apply(elem, element); if (elem instanceof FormElementContainer) { traverse((FormElementContainer) elem, traverseFunction); } } } public void remove(final FormElement formElement) { traverse(this, new TraverseFunction() { @Override public void apply(FormElement element, FormElementContainer container) { if (element.equals(formElement)) { container.getElements().remove(formElement); } } }); } public ResourceId getId() { return id; } public void setId(ResourceId id) { this.id = id; } public String getLabel() { return label; } public FormClass setLabel(String label) { this.label = label; return this; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public List<FormElement> getElements() { return elements; } public List<FormField> getFields() { final List<FormField> fields = Lists.newArrayList(); collectFields(fields, getElements(), new Predicate<FormElement>() { @Override public boolean apply(@Nullable FormElement input) { return input instanceof FormField; } }); return fields; } public List<FormSection> getSections() { final List<FormSection> sections = Lists.newArrayList(); collectFields(sections, getElements(), new Predicate<FormElement>() { @Override public boolean apply(@Nullable FormElement input) { return input instanceof FormSection; } }); return sections; } private static void collectFields(List result, List<FormElement> elements, Predicate<FormElement> predicate) { for (FormElement element : elements) { if (predicate.apply(element)) { result.add(element); } if (element instanceof FormField) { // do nothing } else if (element instanceof FormSection) { final FormSection formSection = (FormSection) element; collectFields(result, formSection.getElements(), predicate); } } } public FormField getField(ResourceId fieldId) { for(FormField field : getFields()) { if(field.getId().equals(fieldId)) { return field; } } throw new IllegalArgumentException("No such field: " + fieldId); } public FormClass addElement(FormElement element) { elements.add(element); return this; } public FormField addField(ResourceId fieldId) { FormField field = new FormField(fieldId); elements.add(field); return field; } public FormClass insertElement(int index, FormElement element) { elements.add(index, element); return this; } @Override public String toString() { return "<FormClass: " + getLabel() + ">"; } public static FormClass fromResource(Resource resource) { FormClass formClass = new FormClass(resource.getId()); formClass.setOwnerId(resource.getOwnerId()); formClass.setLabel(Strings.nullToEmpty(resource.isString(LABEL_FIELD_ID))); formClass.elements.addAll(fromRecords(resource.getRecordList("elements"))); return formClass; } private static List<FormElement> fromRecords(List<Record> elementArray) { List<FormElement> elements = Lists.newArrayList(); for(Record elementRecord : elementArray) { if("section".equals(elementRecord.isString("type"))) { FormSection section = new FormSection(ResourceId.valueOf(elementRecord.getString("id"))); section.setLabel(elementRecord.getString("label")); section.getElements().addAll(fromRecords(elementRecord.getRecordList("elements"))); elements.add(section); } else { elements.add(FormField.fromRecord(elementRecord)); } } return elements; } public Resource asResource() { Resource resource = Resources.createResource(); resource.setId(id); resource.setOwnerId(ownerId); resource.set("classId", CLASS_ID); resource.set(LABEL_FIELD_ID, label); resource.set("elements", FormElement.asRecordList(elements)); return resource; } }