/** * @param elements * @param multiplicities * @return */ public TreeReference getChildInstanceRef(Vector elements, Vector multiplicities) { if (elements.size() == 0) return null; // get reference for target element TreeReference ref = FormInstance.unpackReference(((IFormElement) elements.lastElement()).getBind()).clone(); for (int i = 0; i < ref.size(); i++) { // There has to be a better way to encapsulate this if (ref.getMultiplicity(i) != TreeReference.INDEX_ATTRIBUTE) { ref.setMultiplicity(i, 0); } } // fill in multiplicities for repeats along the way for (int i = 0; i < elements.size(); i++) { IFormElement temp = (IFormElement) elements.elementAt(i); if (temp instanceof GroupDef && ((GroupDef) temp).getRepeat()) { TreeReference repRef = FormInstance.unpackReference(temp.getBind()); if (repRef.isParentOf(ref, false)) { int repMult = ((Integer) multiplicities.elementAt(i)).intValue(); ref.setMultiplicity(repRef.size() - 1, repMult); } else { return null; // question/repeat hierarchy is not consistent // with instance instance and bindings } } } return ref; }
public static void applyDataType( FormInstance dm, String path, TreeReference parent, int dataType) { TreeReference ref = childRef(path, parent); Vector v = dm.expandReference(ref); for (int i = 0; i < v.size(); i++) { TreeElement e = dm.resolveReference((TreeReference) v.elementAt(i)); e.dataType = dataType; } }
public void createNewRepeat(FormIndex index) throws InvalidReferenceException { TreeReference destRef = getChildInstanceRef(index); TreeElement template = instance.getTemplate(destRef); instance.copyNode(template, destRef); preloadInstance(instance.resolveReference(destRef)); triggerTriggerables(destRef); // trigger conditions that depend on the creation of this new node initializeTriggerables(destRef); // initialize conditions for the node (and sub-nodes) }
public void setInstance(FormInstance instance) { if (instance.getFormId() != -1 && getID() != instance.getFormId()) { System.err.println( "Warning: assigning incompatible instance (type " + instance.getFormId() + ") to a formdef (type " + getID() + ")"); } instance.setFormId(getID()); this.instance = instance; attachControlsToInstanceData(); }
public void formEntrySaved(FormDef form, FormInstance instanceData, boolean formWasCompleted) { System.out.println("form is complete: " + formWasCompleted); //Warning, this might be null final SubmissionProfile profile = form.getSubmissionProfile(); if (formWasCompleted) { // We want to cache this send before we actually ask, otherwise the user could quit before it is // either sent _or_ saved. IStorageUtility storage = StorageManager.getStorage(FormInstance.STORAGE_KEY); try { Logger.log("formentry","writing data: " + instanceData.getName()); storage.write(instanceData); final int record = instanceData.getID(); TransportMessage message = JRDemoContext._().buildMessage(instanceData, profile); CompletedFormOptionsState completed = new CompletedFormOptionsState(message) { public void sendData(TransportMessage message) { JRDemoFormTransportState send = new JRDemoFormTransportState(message, record) { public void done() { JRDemoUtil.goToList(cameFromFormList); } public void sendToBackground() { JRDemoUtil.goToList(cameFromFormList); } }; send.start(); } public void skipSend(TransportMessage message) { // Message should already be cached. abort(); } }; completed.start(); } catch (StorageFullException e) { new RuntimeException("Storage full, unable to save data."); } } else { abort(); } }
public static void mergeDataModel( FormInstance parent, FormInstance child, TreeReference parentRef) { TreeElement parentNode = parent.resolveReference(parentRef); // ugly if (parentNode == null) { parentRef = parent.addNode(parentRef); parentNode = parent.resolveReference(parentRef); } TreeElement childNode = child.getRoot(); int mult = parentNode.getChildMultiplicity(childNode.getName()); childNode.setMult(mult); parentNode.addChild(childNode); }
private void evaluateTriggerable(Triggerable t, TreeReference anchorRef) { TreeReference contextRef = t.contextRef.contextualize(anchorRef); Vector v = instance.expandReference(contextRef); for (int i = 0; i < v.size(); i++) { EvaluationContext ec = new EvaluationContext(exprEvalContext, (TreeReference) v.elementAt(i)); t.apply(instance, ec, this); } }
public boolean isRepeatRelevant(TreeReference repeatRef) { boolean relev = true; Condition c = (Condition) conditionRepeatTargetIndex.get(repeatRef.genericize()); if (c != null) { relev = c.evalBool(instance, new EvaluationContext(exprEvalContext, repeatRef)); } // check the relevancy of the immediate parent if (relev) { TreeElement templNode = instance.getTemplate(repeatRef); TreeReference parentPath = templNode.getParent().getRef().genericize(); TreeElement parentNode = instance.resolveReference(parentPath.contextualize(repeatRef)); relev = parentNode.isRelevant(); } return relev; }
/** * Deletes the inner-most repeat that this node belongs to and returns the corresponding * FormIndex. Behavior is currently undefined if you call this method on a node that is not * contained within a repeat. * * @param index * @return */ public FormIndex deleteRepeat(FormIndex index) { Vector indexes = new Vector(); Vector multiplicities = new Vector(); Vector elements = new Vector(); collapseIndex(index, indexes, multiplicities, elements); // loop backwards through the elements, removing objects from each // vector, until we find a repeat // TODO: should probably check to make sure size > 0 for (int i = elements.size() - 1; i >= 0; i--) { IFormElement e = (IFormElement) elements.elementAt(i); if (e instanceof GroupDef && ((GroupDef) e).getRepeat()) { break; } else { indexes.removeElementAt(i); multiplicities.removeElementAt(i); elements.removeElementAt(i); } } // build new formIndex which includes everything // up to the node we're going to remove FormIndex newIndex = buildIndex(indexes, multiplicities, elements); TreeReference deleteRef = getChildInstanceRef(newIndex); TreeElement deleteElement = instance.resolveReference(deleteRef); TreeReference parentRef = deleteRef.getParentRef(); TreeElement parentElement = instance.resolveReference(parentRef); int childMult = deleteElement.getMult(); parentElement.removeChild(deleteElement); // update multiplicities of other child nodes for (int i = 0; i < parentElement.getNumChildren(); i++) { TreeElement child = parentElement.getChildAt(i); if (child.getMult() > childMult) { child.setMult(child.getMult() - 1); } } triggerTriggerables(deleteRef); return newIndex; }
/** * meant to be called after deserialization and initialization of handlers * * @param newInstance true if the form is to be used for a new entry interaction, false if it is * using an existing IDataModel */ public void initialize(boolean newInstance) { if (newInstance) { // only preload new forms (we may have to revisit // this) preloadInstance(instance.getRoot()); } if (getLocalizer() != null && getLocalizer().getLocale() == null) { getLocalizer().setToDefault(); } initializeTriggerables(); }
public boolean evaluateConstraint(TreeReference ref, IAnswerData data) { if (data == null) return true; TreeElement node = instance.resolveReference(ref); Constraint c = node.getConstraint(); if (c == null) return true; EvaluationContext ec = new EvaluationContext(exprEvalContext, ref); ec.isConstraint = true; ec.candidateValue = data; return c.constraint.eval(instance, ec); }
public static Object getValue(String xpath, TreeReference context, FormInstance tree) { TreeElement node = tree.resolveReference(ref(xpath).contextualize(context)); if (node == null) { throw new RuntimeException( "Could not find node [" + xpath + "] when parsing saved instance!"); } if (node.isRelevant()) { IAnswerData val = node.getValue(); return (val == null ? null : val.getValue()); } else { return null; } }
public int getNumRepetitions(FormIndex index) { Vector indexes = new Vector(); Vector multiplicities = new Vector(); Vector elements = new Vector(); if (!index.isInForm()) { throw new RuntimeException("not an in-form index"); } collapseIndex(index, indexes, multiplicities, elements); if (!(elements.lastElement() instanceof GroupDef) || !((GroupDef) elements.lastElement()).getRepeat()) { throw new RuntimeException("current element not a repeat"); } // so painful TreeElement templNode = instance.getTemplate(index.getReference()); TreeReference parentPath = templNode.getParent().getRef().genericize(); TreeElement parentNode = instance.resolveReference(parentPath.contextualize(index.getReference())); return parentNode.getChildMultiplicity(templNode.getName()); }
public static QuestionDef findQuestionByRef(TreeReference ref, IFormElement fe) { if (fe instanceof FormDef) { ref = ref.genericize(); } if (fe instanceof QuestionDef) { QuestionDef q = (QuestionDef) fe; TreeReference bind = FormInstance.unpackReference(q.getBind()); return (ref.equals(bind) ? q : null); } else { for (int i = 0; i < fe.getChildren().size(); i++) { QuestionDef ret = findQuestionByRef(ref, fe.getChild(i)); if (ret != null) return ret; } return null; } }
public static void addData(FormInstance dm, String xpath, Object data, int dataType) { if (data == null) { dataType = -1; } IAnswerData val; switch (dataType) { case -1: val = null; break; case Constants.DATATYPE_TEXT: val = new StringData((String) data); break; case Constants.DATATYPE_INTEGER: val = new IntegerData((Integer) data); break; case Constants.DATATYPE_LONG: val = new LongData((Long) data); break; case Constants.DATATYPE_DECIMAL: val = new DecimalData((Double) data); break; case Constants.DATATYPE_BOOLEAN: val = new StringData(((Boolean) data).booleanValue() ? "t" : "f"); break; case Constants.DATATYPE_DATE: val = new DateData((Date) data); break; case Constants.DATATYPE_DATE_TIME: val = new DateTimeData((Date) data); break; case Constants.DATATYPE_TIME: val = new TimeData((Date) data); break; case Constants.DATATYPE_CHOICE_LIST: val = (SelectMultiData) data; break; default: throw new IllegalArgumentException("Don't know how to handle data type [" + dataType + "]"); } TreeReference ref = absRef(xpath, dm); if (dm.addNode(ref, val, dataType) == null) { throw new RuntimeException("error setting value during object backup [" + xpath + "]"); } }
public static void importRMS(FormInstance dm, IStorageUtility storage, Class type, String path) { if (!Externalizable.class.isAssignableFrom(type) || !Restorable.class.isAssignableFrom(type)) { return; } boolean idMatters = Persistable.class.isAssignableFrom(type); String childName = ((Restorable) PrototypeFactory.getInstance(type)).getRestorableType(); TreeElement e = dm.resolveReference(absRef(path, dm)); Vector children = e.getChildrenWithName(childName); for (int i = 0; i < children.size(); i++) { FormInstance child = subDataModel((TreeElement) children.elementAt(i)); Restorable inst = (Restorable) PrototypeFactory.getInstance(type); // restore record id first so 'importData' has access to it int recID = -1; if (idMatters) { recID = ((Integer) getValue(RECORD_ID_TAG, child)).intValue(); ((Persistable) inst).setID(recID); } inst.importData(child); try { if (idMatters) { storage.write((Persistable) inst); } else { storage.add((Externalizable) inst); } } catch (Exception ex) { throw new RuntimeException( "Error importing RMS during restore! [" + type.getName() + ":" + recID + "]; " + ex.getMessage()); } } }
public boolean postProcessInstance() { return postProcessInstance(instance.getRoot()); }
public void finalizeTriggerables() { // // DAGify the triggerables based on dependencies and sort them so that // trigbles come only after the trigbles they depend on // Vector partialOrdering = new Vector(); for (int i = 0; i < triggerables.size(); i++) { Triggerable t = (Triggerable) triggerables.elementAt(i); Vector deps = new Vector(); if (t.canCascade()) { for (int j = 0; j < t.getTargets().size(); j++) { TreeReference target = (TreeReference) t.getTargets().elementAt(j); Vector triggered = (Vector) triggerIndex.get(target); if (triggered != null) { for (int k = 0; k < triggered.size(); k++) { Triggerable u = (Triggerable) triggered.elementAt(k); if (!deps.contains(u)) deps.addElement(u); } } } } for (int j = 0; j < deps.size(); j++) { Triggerable u = (Triggerable) deps.elementAt(j); Triggerable[] edge = {t, u}; partialOrdering.addElement(edge); } } Vector vertices = new Vector(); for (int i = 0; i < triggerables.size(); i++) vertices.addElement(triggerables.elementAt(i)); triggerables.removeAllElements(); while (vertices.size() > 0) { // determine root nodes Vector roots = new Vector(); for (int i = 0; i < vertices.size(); i++) { roots.addElement(vertices.elementAt(i)); } for (int i = 0; i < partialOrdering.size(); i++) { Triggerable[] edge = (Triggerable[]) partialOrdering.elementAt(i); roots.removeElement(edge[1]); } // if no root nodes while graph still has nodes, graph has cycles if (roots.size() == 0) { throw new RuntimeException( "Cannot create partial ordering of triggerables due to dependency cycle. Why wasn't this caught during parsing?"); } // remove root nodes and edges originating from them for (int i = 0; i < roots.size(); i++) { Triggerable root = (Triggerable) roots.elementAt(i); triggerables.addElement(root); vertices.removeElement(root); } for (int i = partialOrdering.size() - 1; i >= 0; i--) { Triggerable[] edge = (Triggerable[]) partialOrdering.elementAt(i); if (roots.contains(edge[0])) partialOrdering.removeElementAt(i); } } triggerablesInOrder = true; // // build the condition index for repeatable nodes // conditionRepeatTargetIndex = new Hashtable(); for (int i = 0; i < triggerables.size(); i++) { Triggerable t = (Triggerable) triggerables.elementAt(i); if (t instanceof Condition) { Vector targets = t.getTargets(); for (int j = 0; j < targets.size(); j++) { TreeReference target = (TreeReference) targets.elementAt(j); if (instance.getTemplate(target) != null) { conditionRepeatTargetIndex.put(target, (Condition) t); } } } } }
public static TreeReference topRef(FormInstance dm) { return ref("/" + dm.getRoot().getName()); }
private static FormInstance newDataModel(String topTag) { FormInstance dm = new FormInstance(); dm.addNode(ref("/" + topTag)); return dm; }
public static FormInstance createRootDataModel(Restorable r) { FormInstance inst = createDataModel(r); inst.schema = "http://openrosa.org/backup"; addData(inst, "timestamp", new Date(), Constants.DATATYPE_DATE_TIME); return inst; }
public void setAnswer(IAnswerData data, TreeReference ref) { setAnswer(data, instance.resolveReference(ref)); }
public void setValue(IAnswerData data, TreeReference ref) { setValue(data, ref, instance.resolveReference(ref)); }
/** * Link a deserialized instance back up with its parent FormDef. this allows select/select1 * questions to be internationalizable in chatterbox, and (if using CHOICE_INDEX mode) allows the * instance to be serialized to xml */ public void attachControlsToInstanceData() { attachControlsToInstanceData(instance.getRoot()); }