package org.activityinfo.io.xform; import com.google.common.base.Strings; import com.google.common.collect.Lists; import org.activityinfo.io.xform.form.*; import org.activityinfo.model.form.*; import org.activityinfo.model.legacy.CuidAdapter; import org.activityinfo.model.resource.ResourceId; import org.activityinfo.model.type.Cardinality; import org.activityinfo.model.type.FieldType; import org.activityinfo.model.type.enumerated.EnumItem; import org.activityinfo.model.type.enumerated.EnumType; import org.activityinfo.model.type.geo.GeoPointType; import org.activityinfo.model.type.number.QuantityType; import org.activityinfo.model.type.primitive.TextType; import org.activityinfo.model.type.time.LocalDateType; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; /** * Creates a {@link org.activityinfo.model.form.FormClass} from an XForm Document. */ public class XFormReader { private static final Logger LOGGER = Logger.getLogger(XFormReader.class.getName()); private XForm xForm; private FormClass formClass = new FormClass(ResourceId.generateId()); private Map<String, Bind> bindings = new HashMap<>(); private Map<String, InstanceElement> instanceElements = new HashMap<>(); private Map<InstanceElement, InstanceElement> instanceElementParents = new HashMap<>(); public XFormReader(XForm xForm) { this.xForm = xForm; } public void setActivityId(int activityId) { this.formClass.setId(CuidAdapter.activityFormClass(activityId)); } public void setDatabaseId(int databaseId) { this.formClass.setOwnerId(CuidAdapter.databaseId(databaseId)); } public FormClass build() { formClass.setLabel(xForm.getHead().getTitle()); findInstanceElements("", xForm.getHead().getModel().getInstance().getRoot()); findBindings(); addFieldElements(formClass, xForm.getBody().getElements()); return formClass; } private void findInstanceElements(String pathPrefix, InstanceElement element) { String path = pathPrefix + "/" + element.getName(); instanceElements.put(path, element); for(InstanceElement child : element.getChildren()) { instanceElementParents.put(child, element); findInstanceElements(path, child); } } private void findBindings() { for (Bind binding : xForm.getHead().getModel().getBindings()) { bindings.put(binding.getNodeSet(), binding); } } private void addFieldElements(FormElementContainer container, List<BodyElement> elements) { for(BodyElement element : elements) { if (element instanceof Group) { Group group = (Group) element; if (!Strings.isNullOrEmpty(element.getLabel())) { FormSection section = createSection(group); addFieldElements(section, group.getElements()); container.addElement(section); } else { addFieldElements(container, group.getElements()); } } else { container.addElement(createField(element)); } } } private FormSection createSection(Group element) { FormSection section = new FormSection(ResourceId.generateId()); section.setLabel(element.getLabel()); return section; } private FormElement createField(BodyElement element) { if(Strings.isNullOrEmpty(element.getLabel())) { throw new RuntimeException("Element " + element.getRef() + " has no label"); } InstanceElement instanceElement = instanceElements.get(element.getRef()); Bind bind = bindings.get(element.getRef()); FieldType type = createType(element, bind); FormField field = new FormField(ResourceId.generateFieldId(type.getTypeClass())); field.setCode(buildCode(null, instanceElement).toString()); field.setLabel(element.getLabel()); field.setDescription(element.getHint()); field.setType(type); return field; } private FieldType createType(BodyElement element, Bind binding) { switch (binding.getType()) { case STRING: return TextType.INSTANCE; case DATE: case DATETIME: return LocalDateType.INSTANCE; case SELECT1: return new EnumType(Cardinality.SINGLE, enumItems((SelectElement) element)); case SELECT: return new EnumType(Cardinality.MULTIPLE, enumItems((SelectElement) element)); case INT: case DECIMAL: return new QuantityType(); case GEOPOINT: return GeoPointType.INSTANCE; } throw new IllegalArgumentException(binding.getType().name()); } private List<EnumItem> enumItems(SelectElement element) { List<EnumItem> enumItems = Lists.newArrayList(); for (Item item : element.getItems()) { EnumItem enumItem = new EnumItem(EnumItem.generateId(), item.getLabel()); enumItem.setCode(item.getValue()); enumItems.add(enumItem); } return enumItems; } private StringBuilder buildCode(InstanceElement instanceElement, InstanceElement parentInstanceElement) { StringBuilder stringBuilder = null; if (parentInstanceElement != null) { stringBuilder = buildCode(parentInstanceElement, instanceElementParents.get(parentInstanceElement)); if (instanceElement != null) { if (stringBuilder == null) { stringBuilder = new StringBuilder(); } else { stringBuilder.append('/'); } stringBuilder.append(instanceElement.getName()); } } return stringBuilder; } }