package info.guardianproject.odkparser; import info.guardianproject.odkparser.utils.QD; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.javarosa.core.model.FormDef; import org.javarosa.core.model.FormIndex; import org.javarosa.core.model.QuestionDef; import org.javarosa.core.model.SelectChoice; import org.javarosa.core.model.condition.EvaluationContext; import org.javarosa.core.model.data.IAnswerData; import org.javarosa.core.model.instance.TreeElement; import org.javarosa.core.services.PrototypeManager; import org.javarosa.core.services.locale.Localizer; import org.javarosa.core.services.transport.payload.ByteArrayPayload; import org.javarosa.core.util.externalizable.ExtUtil; import org.javarosa.core.util.externalizable.PrototypeFactory; import org.javarosa.form.api.FormEntryCaption; import org.javarosa.form.api.FormEntryController; import org.javarosa.form.api.FormEntryModel; import org.javarosa.form.api.FormEntryPrompt; import org.javarosa.model.xform.XFormSerializingVisitor; import org.javarosa.xform.parse.XFormParser; import org.javarosa.xform.util.XFormUtils; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.witness.informacam.json.JSONException; import org.witness.informacam.json.JSONObject; import org.xml.sax.SAXException; import android.os.Environment; import android.util.Log; public class FormWrapper implements Constants { public FormDef form_def; public FormIndex form_index; public FormEntryController controller; public FormEntryModel fem; public FormEntryPrompt fep; public ArrayList<QD> questions; public Map<String, String> answers = null; public String title; public int num_questions = 0; static PrototypeFactory pf; static { PrototypeManager.registerPrototype("org.javarosa.model.xform.XPathReference"); pf = ExtUtil.defaultPrototypes(); } private static final String LOG = Logger.FORM; public interface ODKFormListener { public boolean saveForm(); } public FormWrapper(InputStream xml, boolean touch) { form_def = loadDefinition(xml); if(!touch) init(null); } public FormWrapper(FormDef form_def) { this.form_def = form_def; init(null); } public FormWrapper(InputStream xml) { form_def = loadDefinition(xml); init(null); } public FormWrapper(InputStream xml, byte[] oldAnswers) { form_def = loadDefinition(xml); init(oldAnswers); } public FormWrapper(FormDef form_def, byte[] oldAnswers) { this.form_def = form_def; init(oldAnswers); } @SuppressWarnings("unused") private QuestionDef getFirstQuestionDef() { controller.jumpToIndex(FormIndex.createBeginningOfFormIndex()); do { FormEntryCaption fec = fem.getCaptionPrompt(); if(fec.getFormElement() instanceof QuestionDef) return (QuestionDef) fec.getFormElement(); } while(controller.stepToNextEvent() != FormEntryController.EVENT_END_OF_FORM); return null; } @SuppressWarnings("unused") private QuestionDef getCurrentQuestionDef() { FormEntryCaption fec = fem.getCaptionPrompt(); if(fec.getFormElement() instanceof QuestionDef) return (QuestionDef) fec.getFormElement(); return null; } public void inflatePreviousAnswers(byte[] bytes) { TreeElement savedRoot = XFormParser.restoreDataModel(bytes, null).getRoot(); for(int t=0; t<savedRoot.getNumChildren(); t++) { TreeElement childElement = savedRoot.getChildAt(t); if(answers == null) { answers = new HashMap<String, String>(); } try { //Log.d(LOG, "HERE IS " + childElement.getValue().getValue()); answers.put(childElement.getName(), String.valueOf(childElement.getValue().getValue())); } catch(NullPointerException e) { // there is no value here //Log.e(LOG, "no value for " + childElement.getName()); continue; } } form_def.preloadInstance(savedRoot); } public static JSONObject parseXMLAnswersAsJSON(byte[] bytes) { TreeElement savedRoot = XFormParser.restoreDataModel(bytes, null).getRoot(); JSONObject answers = new JSONObject(); for(int t=0; t<savedRoot.getNumChildren(); t++) { TreeElement childElement = savedRoot.getChildAt(t); try { answers.put(childElement.getName(), childElement.getValue().getDisplayText()); } catch (JSONException e) { e.printStackTrace(); continue; } catch (NullPointerException e) { e.printStackTrace(); continue; } } return answers; } private List<QD> init(byte[] oldAnswers) { EvaluationContext ec = new EvaluationContext(); form_def.setEvaluationContext(ec); fem = new FormEntryModel(form_def); controller = new FormEntryController(fem); if(oldAnswers != null && oldAnswers.length > 0) inflatePreviousAnswers(oldAnswers); else form_def.initialize(true); title = controller.getModel().getForm().getTitle(); form_index = controller.getModel().getFormIndex(); controller.jumpToIndex(FormIndex.createBeginningOfFormIndex()); Localizer l = form_def.getLocalizer(); l.setDefaultLocale(l.getAvailableLocales()[0]); l.setLocale(l.getAvailableLocales()[0]); do { FormEntryCaption fec = fem.getCaptionPrompt(); if(fec.getFormElement() instanceof QuestionDef) { if(questions == null) questions = new ArrayList<QD>(); QuestionDef qd = (QuestionDef) fec.getFormElement(); //Log.d(LOG, "this question def textId: " + qd.getTextID()); QD questionDef = null; if(answers != null && answers.containsKey(qd.getTextID())) questionDef = new QD(qd, answers.get(qd.getTextID())); else questionDef = new QD(qd); FormEntryPrompt fep = fem.getQuestionPrompt(); questionDef.questionText = fep.getQuestionText(); if(fep.getHelpText() != null) questionDef.helperText = fep.getHelpText(); if(fep.getControlType() == org.javarosa.core.model.Constants.CONTROL_SELECT_MULTI || fep.getControlType() == org.javarosa.core.model.Constants.CONTROL_SELECT_ONE) { questionDef.selectChoiceText = new ArrayList<String>(); for(SelectChoice sc : fep.getSelectChoices()) { questionDef.selectChoiceText.add(fep.getSelectChoiceText(sc)); } } questions.add(questionDef); } } while(controller.stepToNextEvent() != FormEntryController.EVENT_END_OF_FORM); num_questions = questions.size(); return questions; } public JSONObject processFormAsJSON() { JSONObject informaObject = new JSONObject(); form_def.postProcessInstance(); try { XFormSerializingVisitor serializer = new XFormSerializingVisitor(); ByteArrayPayload payload = new ByteArrayPayload(serializer.serializeInstance(form_def.getInstance()), form_def.getName(), form_def.getID()); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.parse(payload.getPayloadStream()); doc.getDocumentElement().normalize(); NodeList answers = doc.getDocumentElement().getChildNodes(); //Log.d(LOG, "there are " + answers.getLength() + " child nodes"); for(int n=0; n<answers.getLength(); n++) { Node node = answers.item(n); //Log.d(LOG, "node: " + node.getNodeName()); if(node.getNodeType() == Node.ELEMENT_NODE) { try { informaObject.put(node.getNodeName(), ((Element) node).getElementsByTagName(node.getNodeName()).item(0).getChildNodes().item(0).getNodeValue()); } catch(NullPointerException e) { //Log.e(LOG, "Could not get value for " + node.getNodeName() + "\n" + e.toString()); e.printStackTrace(); } } } return informaObject; } catch(IOException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } catch (ParserConfigurationException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } catch (SAXException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } catch (DOMException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } catch (JSONException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } return null; } public OutputStream processFormAsXML(OutputStream os) { Log.d(LOG, "SAVING AS XML NOW!"); try { XFormSerializingVisitor serializer = new XFormSerializingVisitor(); ByteArrayPayload payload = new ByteArrayPayload(serializer.serializeInstance(form_def.getInstance()), form_def.getName(), form_def.getID()); InputStream is = payload.getPayloadStream(); byte[] data = new byte[(int) payload.getLength()]; int read = is.read(data, 0, (int) payload.getLength()); if(read > 0) { os.write(data); os.flush(); os.close(); return os; } } catch (FileNotFoundException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } catch (IOException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } catch (NullPointerException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } return null; } public boolean saveTest() { try { File testDir = new File(Environment.getExternalStorageDirectory(),"odktest"); if(!testDir.exists()) testDir.mkdir(); File testFile = new File(testDir, "text.xml"); XFormSerializingVisitor serializer = new XFormSerializingVisitor(); ByteArrayPayload payload = new ByteArrayPayload(serializer.serializeInstance(form_def.getInstance()), form_def.getName(), form_def.getID()); InputStream is = payload.getPayloadStream(); byte[] data = new byte[(int) payload.getLength()]; int read = is.read(data, 0, (int) payload.getLength()); if(read > 0) { OutputStream os = new FileOutputStream(testFile); os.write(data); os.flush(); os.close(); return true; } } catch (FileNotFoundException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } catch (IOException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } return false; } public boolean answerQuestion(QuestionDef qd, IAnswerData answer) { int event = controller.jumpToIndex(FormIndex.createBeginningOfFormIndex()); do { FormEntryCaption fec = fem.getCaptionPrompt(); if(fec.getFormElement() instanceof QuestionDef && ((QuestionDef) fec.getFormElement()).equals(qd)) { try { if(answer.getValue() != null && !answer.getValue().equals("null")) { //Log.d(LOG, "fyi answer data: " + answer.hashCode() + " (" + answer.getValue() + ")"); controller.answerQuestion(answer); return controller.saveAnswer(answer); } } catch(NullPointerException e) { Log.d(LOG, e.toString()); e.printStackTrace(); } } } while((event = controller.stepToNextEvent()) != FormEntryController.EVENT_END_OF_FORM); return false; } public static FormDef loadDefinition(InputStream xml) { return XFormUtils.getFormFromInputStream(xml); } public static byte[] getBytesFromFile(File file) throws IOException { byte[] bytes = new byte[(int) file.length()]; FileInputStream fis = new FileInputStream(file); fis.read(bytes, 0, bytes.length); fis.close(); return bytes; } }