• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

opensrp / opensrp-client-core / #175

pending completion
#175

push

github-actions

web-flow
Merge pull request #921 from opensrp/refactor-user-settings-task

Refactor user settings task

18309 of 26768 relevant lines covered (68.4%)

0.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

49.26
opensrp-core/src/main/java/org/smartregister/util/FormUtils.java
1
package org.smartregister.util;
2

3
import android.content.Context;
4
import android.util.Xml;
5

6
import com.google.gson.Gson;
7
import com.google.gson.GsonBuilder;
8

9
import org.apache.commons.codec.CharEncoding;
10
import org.apache.commons.io.IOUtils;
11
import org.json.JSONArray;
12
import org.json.JSONException;
13
import org.json.JSONObject;
14
import org.smartregister.AllConstants;
15
import org.smartregister.CoreLibrary;
16
import org.smartregister.clientandeventmodel.Client;
17
import org.smartregister.clientandeventmodel.Event;
18
import org.smartregister.clientandeventmodel.FormAttributeParser;
19
import org.smartregister.clientandeventmodel.FormEntityConverter;
20
import org.smartregister.clientandeventmodel.FormField;
21
import org.smartregister.clientandeventmodel.SubFormData;
22
import org.smartregister.domain.form.FormSubmission;
23
import org.smartregister.domain.form.SubForm;
24
import org.w3c.dom.Attr;
25
import org.w3c.dom.Document;
26
import org.w3c.dom.Element;
27
import org.w3c.dom.NamedNodeMap;
28
import org.w3c.dom.Node;
29
import org.w3c.dom.NodeList;
30
import org.xmlpull.v1.XmlSerializer;
31

32
import java.io.ByteArrayInputStream;
33
import java.io.FileNotFoundException;
34
import java.io.IOException;
35
import java.io.InputStream;
36
import java.io.StringWriter;
37
import java.text.Format;
38
import java.text.SimpleDateFormat;
39
import java.util.ArrayList;
40
import java.util.Date;
41
import java.util.List;
42
import java.util.Locale;
43
import java.util.Map;
44
import java.util.UUID;
45

46
import javax.xml.parsers.DocumentBuilder;
47
import javax.xml.parsers.DocumentBuilderFactory;
48

49
import timber.log.Timber;
50

51
/**
52
 * Created by koros on 9/28/15.
53
 */
54
public class FormUtils {
1✔
55

56
    public static final String TAG = "FormUtils";
57
    public static final String ecClientRelationships = "ec_client_relationships.json";
58
    private static final String shouldLoadValueKey = "shouldLoadValue";
59
    private static final String relationalIdKey = "relational_id";
60
    private static final String databaseIdKey = "_id";
61
    private static final String injectedBaseEntityIdKey = "injectedBaseEntityId";
62
    private static FormUtils instance;
63
    private Context mContext;
64
    private org.smartregister.Context theAppContext;
65
    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.ENGLISH);
1✔
66
    private Format formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
1✔
67
    private FormEntityConverter formEntityConverter;
68

69
    public FormUtils(Context context) throws Exception {
1✔
70
        mContext = context;
1✔
71
        theAppContext = CoreLibrary.getInstance().context();
1✔
72
        FormAttributeParser formAttributeParser = new FormAttributeParser(context);
1✔
73
        formEntityConverter = new FormEntityConverter(formAttributeParser, mContext);
1✔
74
    }
1✔
75

76
    public static FormUtils getInstance(Context ctx) throws Exception {
77
        if (instance == null)
1✔
78
            instance = new FormUtils(ctx);
1✔
79

80
        if (ctx != null && instance.mContext != ctx)
1✔
81
            instance.mContext = ctx;
1✔
82

83
        return instance;
1✔
84
    }
85

86
    /* Checks if the provided node has Child elements
87
     * @param element
88
     * @return
89
     */
90
    public static boolean hasChildElements(Node element) {
91
        NodeList children = element.getChildNodes();
1✔
92
        for (int i = 0; i < children.getLength(); i++) {
1✔
93
            if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
1✔
94
                return true;
×
95
            }
96
        }
97

98
        return false;
1✔
99
    }
100

101
    private static JSONObject retrieveRelationshipJsonForLink(String link, JSONArray array)
102
            throws Exception {
103
        for (int i = 0; i < array.length(); i++) {
1✔
104
            JSONObject object = array.getJSONObject(i);
1✔
105
            if (relationShipExist(link, object)) {
1✔
106
                System.out.println("Relationship found ##");
1✔
107

108
                return object;
1✔
109
            }
110
        }
111

112
        return null;
×
113
    }
114

115
    private static boolean relationShipExist(String link, JSONObject json) {
116
        try {
117
            String[] path = link.split("\\.");
1✔
118
            String parentTable = path[0];
1✔
119
            String childTable = path[1];
1✔
120

121
            String jsonParentTableString = json.getString("parent");
1✔
122
            String jsonChildTableString = json.getString("child");
1✔
123

124
            boolean parentToChildExist =
1✔
125
                    jsonParentTableString.equals(parentTable) && jsonChildTableString
1✔
126
                            .equals(childTable);
1✔
127
            boolean childToParentExist =
1✔
128
                    jsonParentTableString.equals(childTable) && jsonChildTableString
1✔
129
                            .equals(parentTable);
1✔
130

131
            if (parentToChildExist || childToParentExist) {
1✔
132
                return true;
1✔
133
            }
134

135
        } catch (Exception e) {
×
136
            Timber.e(e);
×
137
        }
×
138

139
        return false;
×
140
    }
141

142
    public static int getIndexForFormName(String formName, String[] formNames) {
143
        for (int i = 0; i < formNames.length; i++) {
1✔
144
            if (formName.equalsIgnoreCase(formNames[i])) {
1✔
145
                return i;
1✔
146
            }
147
        }
148

149
        return -1;
×
150
    }
151

152
    private void printClient(Client client) {
153
        Timber.d("============== CLIENT ================");
×
154
        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").create();
×
155
        String clientJson = gson.toJson(client);
×
156
        Timber.d(clientJson);
×
157
        Timber.d("====================================");
×
158

159
    }
×
160

161
    private void printEvent(Event event) {
162
        Timber.d("============== EVENT ================");
×
163
        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").create();
×
164
        String eventJson = gson.toJson(event);
×
165
        Timber.d(eventJson);
×
166
        Timber.d("====================================");
×
167
    }
×
168

169
    private List<SubFormData> getSubFormList(FormSubmission formSubmission) {
170
        List<SubFormData> sub_forms = new ArrayList<SubFormData>();
×
171
        List<SubForm> subForms = formSubmission.getFormInstance().getForm().getSub_forms();
×
172
        if (subForms != null) {
×
173
            for (SubForm sf : subForms) {
×
174
                SubFormData sd = new SubFormData();
×
175
                sd.setDefault_bind_path(sf.getDefaultBindPath());
×
176
                sd.setBind_type(sf.getBindType());
×
177

178
                List<FormField> subFormFields = convertFormFields(sf.getFields());
×
179
                sd.setFields(subFormFields);
×
180

181
                sd.setInstances(sf.getInstances());
×
182
                sd.setName(sf.getName());
×
183
                sub_forms.add(sd);
×
184
            }
×
185
        }
186

187
        return sub_forms;
×
188
    }
189

190
    private List<FormField> convertFormFields(List<org.smartregister.domain.form.FormField>
191
                                                      formFields) {
192
        List<FormField> fields = new ArrayList<FormField>();
×
193
        for (org.smartregister.domain.form.FormField ff : formFields) {
×
194
            FormField f = new FormField(ff.getName(), ff.getValue(), ff.getSource());
×
195
            fields.add(f);
×
196
        }
×
197

198
        return fields;
×
199
    }
200

201
    private JSONArray getSubForms(JSONArray subFormDataArray, String entity_id, JSONObject
202
            subFormDefinition, JSONObject overrides) throws Exception {
203
        JSONArray subForms = new JSONArray();
1✔
204

205
        JSONArray subFormFields = getFieldsArrayForSubFormDefinition(subFormDefinition);
1✔
206
        JSONArray subFormInstances = new JSONArray();
1✔
207

208
        // the id of each subform is contained in the attribute of the enclosing element
209
        for (int i = 0; i < subFormDataArray.length(); i++) {
1✔
210
            JSONObject subFormData = subFormDataArray.getJSONObject(i);
×
211
            String relationalId =
212
                    subFormData.has(relationalIdKey) ? subFormData.getString(relationalIdKey)
×
213
                            : entity_id;
×
214
            String id = subFormData.has(databaseIdKey) ? subFormData.getString(databaseIdKey)
×
215
                    : generateRandomUUIDString();
×
216
            JSONObject subFormInstance = getFieldValuesForSubFormDefinition(subFormDefinition,
×
217
                    relationalId, id, subFormData, overrides);
218
            subFormInstances.put(i, subFormInstance);
×
219
        }
220

221
        subFormDefinition.put("instances", subFormInstances);
1✔
222
        subFormDefinition.put("fields", subFormFields);
1✔
223
        subForms.put(0, subFormDefinition);
1✔
224

225
        return subForms;
1✔
226
    }
227

228
    public String generateXMLInputForFormWithEntityId(String entityId, String formName, String
229
            overrides) {
230
        try {
231
            // get the field overrides map
232
            JSONObject fieldOverrides = new JSONObject();
1✔
233
            if (overrides != null) {
1✔
234
                fieldOverrides = new JSONObject(overrides);
×
235
                String overridesStr = fieldOverrides.getString("fieldOverrides");
×
236
                fieldOverrides = new JSONObject(overridesStr);
×
237
            }
238

239
            // use the form_definition.json to get the form mappings
240
            String formDefinitionJson = readFileFromAssetsFolder(
1✔
241
                    "www/form/" + formName + "/form_definition.json");
242
            JSONObject formDefinition = new JSONObject(formDefinitionJson);
1✔
243
            String ec_bind_path = formDefinition.getJSONObject("form").getString("ec_bind_type");
1✔
244

245
            String sql =
1✔
246
                    "select * from " + ec_bind_path + " where base_entity_id =?";
247
            Map<String, String> dbEntity = theAppContext.formDataRepository().
1✔
248
                    getMapFromSQLQuery(sql, new String[]{entityId});
1✔
249
            Map<String, String> detailsMap = theAppContext.detailsRepository().
1✔
250
                    getAllDetailsForClient(entityId);
1✔
251
            detailsMap.putAll(dbEntity);
1✔
252

253
            JSONObject entityJson = new JSONObject();
1✔
254
            if (detailsMap != null && !detailsMap.isEmpty()) {
1✔
255
                entityJson = new JSONObject(detailsMap);
×
256
            }
257

258
            //read the xml form model, the expected form model that is passed to the form mirrors it
259
            String formModelString = readFileFromAssetsFolder(
1✔
260
                    "www/form/" + formName + "/model" + ".xml").replaceAll("\n", " ")
1✔
261
                    .replaceAll("\r", " ");
1✔
262
            InputStream is = new ByteArrayInputStream(formModelString.getBytes());
1✔
263
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
1✔
264
            dbf.setValidating(false);
1✔
265
            DocumentBuilder db = dbf.newDocumentBuilder();
1✔
266
            Document document = db.parse(is);
1✔
267

268
            XmlSerializer serializer = Xml.newSerializer();
1✔
269
            StringWriter writer = new StringWriter();
1✔
270
            serializer.setOutput(writer);
1✔
271
            serializer.startDocument(CharEncoding.UTF_8, true);
1✔
272

273
            //skip processing <model><instance>
274
            NodeList els = ((Element) document.getElementsByTagName("model").item(0)).
1✔
275
                    getElementsByTagName("instance");
1✔
276
            Element el = (Element) els.item(0);
1✔
277
            NodeList entries = el.getChildNodes();
1✔
278
            int num = entries.getLength();
1✔
279
            for (int i = 0; i < num; i++) {
1✔
280
                Node n = entries.item(i);
1✔
281
                if (n instanceof Element) {
1✔
282
                    Element node = (Element) n;
1✔
283
                    writeXML(node, serializer, fieldOverrides, formDefinition, entityJson, null);
1✔
284
                }
285
            }
286

287
            serializer.endDocument();
1✔
288

289
            String xml = writer.toString();
1✔
290
            // Add model and instance tags
291
            xml = xml.substring(56);
1✔
292
            System.out.println(xml);
1✔
293

294
            return xml;
1✔
295

296
        } catch (Exception e) {
×
297
            Timber.e(e);
×
298
        }
299
        return "";
×
300
    }
301

302
    private void writeXML(Element node, XmlSerializer serializer, JSONObject fieldOverrides,
303
                          JSONObject formDefinition, JSONObject entityJson, String parentId) {
304
        try {
305
            String nodeName = node.getNodeName();
1✔
306
            String entityId =
307
                    entityJson.has("id") ? entityJson.getString("id") : generateRandomUUIDString();
1✔
308
            String relationalId =
309
                    entityJson.has(relationalIdKey) ? entityJson.getString(relationalIdKey)
1✔
310
                            : parentId;
1✔
311

312
            serializer.startTag("", nodeName);
1✔
313

314
            // write the xml attributes
315
            writeXMLAttributes(node, serializer, entityId, relationalId);
1✔
316

317
            String nodeValue = retrieveValueForNodeName(nodeName, entityJson, formDefinition);
1✔
318
            //overwrite the node value with contents from overrides map
319
            if (fieldOverrides.has(nodeName)) {
1✔
320
                nodeValue = fieldOverrides.getString(nodeName);
×
321
            }
322
            //write the node value
323
            if (nodeValue != null) {
1✔
324
                serializer.text(nodeValue);
1✔
325
            }
326

327
            List<String> subFormNames = getSubFormNames(formDefinition);
1✔
328

329
            // get all child nodes
330
            NodeList entries = node.getChildNodes();
1✔
331
            int num = entries.getLength();
1✔
332
            for (int i = 0; i < num; i++) {
1✔
333
                if (entries.item(i) instanceof Element) {
1✔
334
                    Element child = (Element) entries.item(i);
1✔
335
                    String fieldName = child.getNodeName();
1✔
336

337
                    // its a subform element process it
338
                    if (!subFormNames.isEmpty() && subFormNames.contains(fieldName)) {
1✔
339
                        // its a subform element process it
340
                        // get the subform definition
341
                        JSONArray subForms = formDefinition.getJSONObject("form").
×
342
                                getJSONArray("sub_forms");
×
343
                        JSONObject subFormDefinition = retriveSubformDefinitionForBindPath(subForms,
×
344
                                fieldName);
345
                        if (subFormDefinition != null) {
×
346

347
                            String childTableName = subFormDefinition.getString("ec_bind_type");
×
348
                            String sql = "select * from " + childTableName + " where relational_id = ?";
×
349
                            String childRecordsString = theAppContext.formDataRepository().
×
350
                                    queryList(sql, new String[]{relationalId});
×
351
                            JSONArray childRecords = new JSONArray(childRecordsString);
×
352

353
                            JSONArray fieldsArray = subFormDefinition.getJSONArray("fields");
×
354
                            // check whether we are supposed to load the id of the child record
355
                            JSONObject idFieldDefn = getJsonFieldFromArray("id", fieldsArray);
×
356

357
                            // definition for id
358
                            boolean shouldLoadId =
×
359
                                    idFieldDefn.has(shouldLoadValueKey) && idFieldDefn
×
360
                                            .getBoolean(shouldLoadValueKey);
×
361

362
                            if (shouldLoadId && childRecords.length() > 0) {
×
363
                                for (int k = 0; k < childRecords.length(); k++) {
×
364
                                    JSONObject childEntityJson = childRecords.getJSONObject(k);
×
365
                                    JSONObject obj = getCombinedJsonObjectForObject(
×
366
                                            childEntityJson);
367
                                    writeXML(child, serializer, fieldOverrides, subFormDefinition,
×
368
                                            childEntityJson, entityId);
369
                                }
370

371
                            }
372
                        }
373
                    } else {
×
374
                        // it's not a sub-form element write its value
375
                        serializer.startTag("", fieldName);
1✔
376
                        // write the xml attributes
377
                        // a value node doesn't have id or relationalId fields
378
                        writeXMLAttributes(child, serializer, null, null);
1✔
379
                        // write the node value
380
                        String value = retrieveValueForNodeName(fieldName, entityJson,
1✔
381
                                formDefinition);
382
                        // write the node value
383
                        if (value != null) {
1✔
384
                            serializer.text(value);
1✔
385
                        }
386

387
                        // overwrite the node value with contents from overrides map
388
                        if (fieldOverrides.has(fieldName)) {
1✔
389
                            serializer.text(fieldOverrides.getString(fieldName));
×
390
                        }
391

392
                        serializer.endTag("", fieldName);
1✔
393
                    }
394
                }
395
            }
396

397
            serializer.endTag("", node.getNodeName());
1✔
398

399
        } catch (Exception e) {
×
400
            throw new RuntimeException(e);
×
401
        }
1✔
402
    }
1✔
403

404
    /**
405
     * Retrieve additional details for this record from the details Table.
406
     *
407
     * @param entityJson
408
     * @return
409
     */
410
    private JSONObject getCombinedJsonObjectForObject(JSONObject entityJson) {
411
        try {
412
            String baseEntityId = entityJson.getString("base_entity_id");
×
413
            Map<String, String> map = theAppContext.detailsRepository().
×
414
                    getAllDetailsForClient(baseEntityId);
×
415

416
            for (String key : map.keySet()) {
×
417
                if (!entityJson.has(key)) {
×
418
                    entityJson.put(key, map.get(key));
×
419
                }
420
            }
×
421
        } catch (Exception e) {
×
422
            Timber.e(e);
×
423
        }
×
424
        return entityJson;
×
425
    }
426

427
    /**
428
     * Iterate through the provided array and retrieve a json object whose name attribute matches
429
     * the name supplied
430
     *
431
     * @param nameValue
432
     * @param array
433
     * @return
434
     */
435
    private JSONObject getJsonFieldFromArray(String nameValue, JSONArray array) {
436
        try {
437
            if (array != null) {
1✔
438
                for (int i = 0; i < array.length(); i++) {
1✔
439
                    JSONObject field = array.getJSONObject(i);
1✔
440
                    String name = field.has("name") ? field.getString("name") : null;
1✔
441
                    if (nameValue.equals(name)) {
1✔
442
                        return field;
1✔
443
                    }
444
                }
445
            }
446
        } catch (Exception e) {
×
447
            Timber.e(e);
×
448
        }
×
449
        return null;
×
450
    }
451

452
    /**
453
     * retrieves node value for cases which the nodename don't match the name of the xml element
454
     *
455
     * @param nodeName
456
     * @param entityJson
457
     * @param formDefinition
458
     * @return
459
     */
460
    private String retrieveValueForNodeName(String nodeName, JSONObject entityJson, JSONObject
461
            formDefinition) {
462
        try {
463
            if (entityJson != null && entityJson.length() > 0) {
1✔
464
                JSONObject fieldsObject =
465
                        formDefinition.has("form") ? formDefinition.getJSONObject("form")
×
466
                                : formDefinition.has("sub_forms") ? formDefinition
×
467
                                .getJSONObject("sub_forms") : formDefinition;
×
468
                if (fieldsObject.has("fields")) {
×
469
                    JSONArray fields = fieldsObject.getJSONArray("fields");
×
470
                    for (int i = 0; i < fields.length(); i++) {
×
471
                        JSONObject field = fields.getJSONObject(i);
×
472
                        String bindPath = field.has("bind") ? field.getString("bind") : null;
×
473
                        String name = field.has("name") ? field.getString("name") : null;
×
474

475
                        boolean matchingNodeFound =
×
476
                                bindPath != null && name != null && bindPath.endsWith(nodeName)
×
477
                                        || name != null && name.equals(nodeName);
×
478

479
                        if (matchingNodeFound) {
×
480
                            if (field.has("shouldLoadValue") && field
×
481
                                    .getBoolean("shouldLoadValue")) {
×
482
                                String keyName = entityJson.has(nodeName) ? nodeName : name;
×
483
                                if (entityJson.has(keyName)) {
×
484
                                    return entityJson.getString(keyName);
×
485
                                } else {
486
                                    return "";
×
487
                                }
488
                            } else {
489
                                // the shouldLoadValue flag isn't set
490
                                return "";
×
491
                            }
492
                        }
493
                    }
494
                }
495
            }
496
        } catch (Exception e) {
×
497
            Timber.e(e);
×
498
        }
1✔
499

500
        return "";
1✔
501
    }
502

503
    /**
504
     * Currently not used but, the method should retrieve the path of a given node,
505
     * useful when confirming if the current node has been properly mapped to its bind_path
506
     **/
507
    private String getXPath(Node node) {
508
        Node parent = node.getParentNode();
×
509
        if (parent == null) {
×
510
            return "/" + node.getNodeName();
×
511
        }
512

513
        return getXPath(parent) + "/";
×
514
    }
515

516
    private List<String> getSubFormNames(JSONObject formDefinition) throws Exception {
517
        List<String> subFormNames = new ArrayList<String>();
1✔
518
        if (formDefinition.has("form") && formDefinition.getJSONObject("form").has("sub_forms")) {
1✔
519
            JSONArray subForms = formDefinition.getJSONObject("form").getJSONArray("sub_forms");
1✔
520
            for (int i = 0; i < subForms.length(); i++) {
1✔
521
                JSONObject subForm = subForms.getJSONObject(i);
1✔
522
                String subFormNameStr = subForm.getString("default_bind_path");
1✔
523
                String[] path = subFormNameStr.split("/");
1✔
524
                String subFormName = path[path.length - 1]; // the last token
1✔
525
                subFormNames.add(subFormName);
1✔
526
            }
527
        }
528

529
        return subFormNames;
1✔
530
    }
531

532
    private JSONObject retriveSubformDefinitionForBindPath(JSONArray subForms, String fieldName)
533
            throws Exception {
534
        for (int i = 0; i < subForms.length(); i++) {
1✔
535
            JSONObject subForm = subForms.getJSONObject(i);
1✔
536
            String subFormNameStr = subForm.getString("default_bind_path");
1✔
537
            String[] path = subFormNameStr.split("/");
1✔
538
            String subFormName = path[path.length - 1]; // the last token
1✔
539

540
            if (fieldName.equalsIgnoreCase(subFormName)) {
1✔
541
                return subForm;
1✔
542
            }
543
        }
544

545
        return null;
×
546
    }
547

548
    private void writeXMLAttributes(Element node, XmlSerializer serializer, String id, String
549
            relationalId) {
550
        try {
551
            // get a map containing the attributes of this node
552
            NamedNodeMap attributes = node.getAttributes();
1✔
553

554
            // get the number of nodes in this map
555
            int numAttrs = attributes.getLength();
1✔
556

557
            if (id != null) {
1✔
558
                serializer.attribute("", databaseIdKey, id);
1✔
559
            }
560

561
            if (relationalId != null) {
1✔
562
                serializer.attribute("", relationalIdKey, relationalId);
×
563
            }
564

565
            for (int i = 0; i < numAttrs; i++) {
1✔
566
                Attr attr = (Attr) attributes.item(i);
1✔
567
                String attrName = attr.getNodeName();
1✔
568
                String attrValue = attr.getNodeValue();
1✔
569
                serializer.attribute("", attrName, attrValue);
1✔
570
            }
571

572
        } catch (Exception e) {
×
573
            throw new RuntimeException(e);
×
574
        }
1✔
575
    }
1✔
576

577
    private String generateRandomUUIDString() {
578
        return UUID.randomUUID().toString();
1✔
579
    }
580

581
    private String retrieveIdForSubmission(JSONObject jsonObject) throws Exception {
582
        JSONArray fields = jsonObject.getJSONObject("form").getJSONArray("fields");
×
583

584
        for (int i = 0; i < fields.length(); i++) {
×
585
            JSONObject field = fields.getJSONObject(i);
×
586

587
            if (field.has("name") && field.getString("name").equalsIgnoreCase("id")) {
×
588
                return field.getString("value");
×
589
            }
590
        }
591
        return null;
×
592
    }
593

594
    public Object getObjectAtPath(String[] path, JSONObject jsonObject) throws Exception {
595
        JSONObject object = jsonObject;
1✔
596
        int i = 0;
1✔
597
        while (i < path.length - 1) {
1✔
598
            if (object.has(path[i])) {
1✔
599
                Object o = object.get(path[i]);
1✔
600
                if (o instanceof JSONObject) {
1✔
601
                    object = object.getJSONObject(path[i]);
1✔
602
                } else if (o instanceof JSONArray) {
1✔
603
                    object = object.getJSONArray(path[i]).getJSONObject(0);
1✔
604
                }
605
            }
606
            i++;
1✔
607
        }
608
        return object.has(path[i]) ? object.get(path[i]) : null;
1✔
609
    }
610

611
    public JSONArray getPopulatedFieldsForArray(JSONObject fieldsDefinition, String entityId,
612
                                                JSONObject jsonObject, JSONObject overrides)
613
            throws Exception {
614
        String bindPath = fieldsDefinition.getString("bind_type");
×
615
        String sql = "select * from " + bindPath + " where id=?";
×
616
        String dbEntity = theAppContext.formDataRepository().queryUniqueResult(sql, new String[]{entityId});
×
617

618
        JSONObject entityJson = new JSONObject();
×
619

620
        if (dbEntity != null && !dbEntity.isEmpty()) {
×
621
            entityJson = new JSONObject(dbEntity);
×
622
        }
623

624
        JSONArray fieldsArray = fieldsDefinition.getJSONArray("fields");
×
625

626
        for (int i = 0; i < fieldsArray.length(); i++) {
×
627
            JSONObject item = fieldsArray.getJSONObject(i);
×
628

629
            if (!item.has("name")) {
×
630
                continue; // skip elements without name
×
631
            }
632

633
            String itemName = item.getString("name");
×
634
            boolean shouldLoadValue =
×
635
                    item.has("shouldLoadValue") && item.getBoolean("shouldLoadValue");
×
636

637
            if (item.has("bind")) {
×
638
                String pathSting = item.getString("bind");
×
639
                pathSting = pathSting.startsWith("/") ? pathSting.substring(1) : pathSting;
×
640
                String[] path = pathSting.split("/");
×
641
                String value = getValueForPath(path, jsonObject);
×
642
                item.put("value", value);
×
643
            }
644

645
            if (shouldLoadValue && overrides.has(item.getString("name"))) {
×
646
                // if the value is not set use the value in the overrides filed
647
                if (!item.has("value")) {
×
648
                    item.put("value", overrides.getString(item.getString("name")));
×
649
                }
650
            }
651

652
            // map the id field for child elements
653
            if (isForeignIdPath(item)) {
×
654
                String value = null;
×
655
                if (entityJson.length() > 0 && shouldLoadValue) {
×
656
                    //retrieve the child attributes
657
                    value = retrieveValueForLinkedRecord(item.getString("source"), entityJson);
×
658
                }
659

660
                // generate uuid if its still not available
661
                if (item.getString("source").endsWith(".id") && value == null) {
×
662
                    value = generateRandomUUIDString();
×
663
                }
664

665
                if (value != null && !item.has("value")) {
×
666
                    item.put("value", value);
×
667
                }
668
            }
669

670
            // add source property if not available
671
            if (!item.has("source")) {
×
672
                item.put("source", bindPath + "." + item.getString("name"));
×
673
            }
674

675
            if (itemName.equalsIgnoreCase("id") && !isForeignIdPath(item)) {
×
676
                assert entityId != null;
×
677
                item.put("value", entityId);
×
678
            }
679

680
            if (itemName.equalsIgnoreCase(injectedBaseEntityIdKey)) {
×
681
                assert entityId != null;
×
682
                item.put("value", entityId);
×
683
            }
684

685
            if (itemName.equalsIgnoreCase("start") || itemName.equalsIgnoreCase("end")) {
×
686
                try {
687
                    boolean isEndTime = itemName.equalsIgnoreCase("end");
×
688
                    String val =
689
                            item.has("value") ? item.getString("value") : sdf.format(new Date());
×
690

691
                    if (isEndTime) {
×
692
                        val = formatter.format(new Date());
×
693
                    } else {
694
                        Date d = sdf.parse(val);
×
695
                        //parse the date to match OpenMRS format
696
                        val = formatter.format(d);
×
697
                    }
698

699
                    item.put("value", val);
×
700
                } catch (Exception e) {
×
701
                    Timber.e(e);
×
702
                }
×
703
            }
704
        }
705
        return fieldsArray;
×
706
    }
707

708
    private boolean isForeignIdPath(JSONObject item) throws Exception {
709
        // e.g ibu.anak.id
710
        return item.has("source") && item.getString("source").split("\\.").length > 2;
×
711
    }
712

713
    public String retrieveValueForLinkedRecord(String link, JSONObject entityJson) {
714
        try {
715
            String entityRelationships = readFileFromAssetsFolder(
1✔
716
                    "www/form/entity_relationship" + AllConstants.JSON_FILE_EXTENSION);
717
            JSONArray json = new JSONArray(entityRelationships);
1✔
718
            Timber.i(json.toString());
1✔
719

720
            JSONObject rJson;
721

722
            if ((rJson = retrieveRelationshipJsonForLink(link, json)) != null) {
1✔
723
                String[] path = link.split("\\.");
1✔
724
                String parentTable = path[0];
1✔
725
                String childTable = path[1];
1✔
726

727
                String joinValueKey =
728
                        parentTable.equals(rJson.getString("parent")) ? rJson.getString("from")
1✔
729
                                : rJson.getString("to");
1✔
730
                joinValueKey = joinValueKey.contains(".") ? joinValueKey
1✔
731
                        .substring(joinValueKey.lastIndexOf(".") + 1) : joinValueKey;
1✔
732

733
                String val = entityJson.getString(joinValueKey);
1✔
734

735
                String joinField =
736
                        parentTable.equals(rJson.getString("parent")) ? rJson.getString("to")
1✔
737
                                : rJson.getString("from");
1✔
738
                String sql =
1✔
739
                        "select * from " + childTable + " where " + joinField + "=?";
740
                Timber.d(sql);
1✔
741
                String dbEntity = theAppContext.formDataRepository().queryUniqueResult(sql, new String[]{val});
×
742
                JSONObject linkedEntityJson = new JSONObject();
×
743

744
                if (dbEntity != null && !dbEntity.isEmpty()) {
×
745
                    linkedEntityJson = new JSONObject(dbEntity);
×
746
                }
747

748
                // finally retrieve the value from the child entity, need to improve or remove
749
                // entirely these hacks
750
                String sourceKey = link.substring(link.lastIndexOf(".") + 1);
×
751

752
                if (linkedEntityJson.has(sourceKey)) {
×
753
                    return linkedEntityJson.getString(sourceKey);
×
754
                }
755
            }
756

757
        } catch (Exception e) {
1✔
758
            Timber.e(e);
1✔
759
        }
×
760
        return null;
1✔
761
    }
762

763
    public JSONArray getFieldsArrayForSubFormDefinition(JSONObject fieldsDefinition) throws
764
            Exception {
765
        JSONArray fieldsArray = fieldsDefinition.getJSONArray("fields");
1✔
766
        String bindPath = fieldsDefinition.getString("bind_type");
1✔
767

768
        JSONArray subFormFieldsArray = new JSONArray();
1✔
769

770
        for (int i = 0; i < fieldsArray.length(); i++) {
1✔
771
            JSONObject field = new JSONObject();
1✔
772
            JSONObject item = fieldsArray.getJSONObject(i);
1✔
773

774
            if (!item.has("name")) {
1✔
775
                continue; // skip elements without name
×
776
            }
777

778
            field.put("name", item.getString("name"));
1✔
779

780
            if (!item.has("source")) {
1✔
781
                field.put("source", bindPath + "." + item.getString("name"));
1✔
782
            } else {
783
                field.put("source", bindPath + "." + item.getString("source"));
1✔
784
            }
785

786
            subFormFieldsArray.put(i, field);
1✔
787
        }
788

789
        return subFormFieldsArray;
1✔
790
    }
791

792
    public JSONObject getFieldValuesForSubFormDefinition(JSONObject fieldsDefinition, String
793
            relationalId, String entityId, JSONObject jsonObject, JSONObject overrides) throws
794
            Exception {
795

796
        JSONArray fieldsArray = fieldsDefinition.getJSONArray("fields");
×
797

798
        JSONObject fieldsValues = new JSONObject();
×
799

800
        for (int i = 0; i < fieldsArray.length(); i++) {
×
801
            JSONObject item = fieldsArray.getJSONObject(i);
×
802

803
            if (!item.has("name")) {
×
804
                continue; // skip elements without name
×
805
            }
806

807
            if (item.has("bind")) {
×
808
                String pathSting = item.getString("bind");
×
809
                pathSting = pathSting.startsWith("/") ? pathSting.substring(1) : pathSting;
×
810
                String[] path = pathSting.split("/");
×
811

812
                //check if we need to override this val
813
                if (overrides.has(item.getString("name"))) {
×
814
                    fieldsValues.put(item.getString("name"),
×
815
                            overrides.getString(item.getString("name")));
×
816
                } else {
817
                    String value = getValueForPath(path, jsonObject);
×
818
                    fieldsValues.put(item.getString("name"), value);
×
819
                }
820
            }
821

822
            //TODO: generate the id for the record
823
            if (item.has("name") && item.getString("name").equalsIgnoreCase("id")) {
×
824
                String id = entityId != null ? entityId : generateRandomUUIDString();
×
825
                fieldsValues.put(item.getString("name"), id);
×
826
            }
827

828
            //TODO: generate the relational for the record
829
            if (item.has("name") && item.getString("name").equalsIgnoreCase(relationalIdKey)) {
×
830
                fieldsValues.put(item.getString("name"), relationalId);
×
831
            }
832

833
            //TODO: generate the injectedBaseEntityIdKey for the record
834
            if (item.has("name") && item.getString("name")
×
835
                    .equalsIgnoreCase(injectedBaseEntityIdKey)) {
×
836
                fieldsValues.put(item.getString("name"), relationalId);
×
837
            }
838

839
            populateRelationField(item, fieldsValues, relationalId);
×
840
        }
841
        return fieldsValues;
×
842
    }
843

844
    private String getECClientRelationships() {
845
        return AssetHandler.readFileFromAssetsFolder(ecClientRelationships, mContext);
×
846
    }
847

848
    /**
849
     * see if the current field is a client_relationship field, if so set it's value to the
850
     * relationId since that's the parent base_entity_id
851
     *
852
     * @param fieldItem
853
     * @param fieldsValues
854
     * @param relationalId
855
     */
856
    private void populateRelationField(JSONObject fieldItem, JSONObject fieldsValues, String
857
            relationalId) {
858
        try {
859
            if (fieldItem.has("name")) {
×
860
                String fieldName = fieldItem.getString("name");
×
861
                String relationships = getECClientRelationships();
×
862
                JSONArray jsonArray = new JSONArray(relationships);
×
863
                for (int i = 0; i < jsonArray.length(); i++) {
×
864
                    JSONObject rObject = jsonArray.getJSONObject(i);
×
865
                    if (rObject.has("field") && rObject.getString("field")
×
866
                            .equalsIgnoreCase(fieldName)) {
×
867
                        fieldsValues.put(fieldName, relationalId);
×
868
                    }
869
                }
870
            }
871

872
        } catch (JSONException e) {
×
873
            Timber.e(e);
×
874
        }
×
875
    }
×
876

877
    public String getValueForPath(String[] path, JSONObject jsonObject) throws Exception {
878
        JSONObject object = jsonObject;
1✔
879
        String value = null;
1✔
880
        int i = 0;
1✔
881

882
        while (i < path.length - 1) {
1✔
883
            if (object.has(path[i])) {
1✔
884
                Object o = object.get(path[i]);
1✔
885
                if (o instanceof JSONObject) {
1✔
886
                    object = object.getJSONObject(path[i]);
1✔
887
                } else if (o instanceof JSONArray) {
×
888
                    object = object.getJSONArray(path[i]).getJSONObject(0);
×
889
                }
890
            }
891

892
            i++;
1✔
893
        }
894
        Object valueObject = object.has(path[i]) ? object.get(path[i]) : null;
1✔
895

896
        if (valueObject == null) {
1✔
897
            return value;
1✔
898
        }
899
        if (valueObject instanceof JSONObject && ((JSONObject) valueObject).has("content")) {
1✔
900
            value = ((JSONObject) object.get(path[i])).getString("content");
×
901
        } else if (valueObject instanceof JSONArray) {
1✔
902
            value = ((JSONArray) valueObject).get(0).toString();
1✔
903
        } else if (!(valueObject instanceof JSONObject)) {
1✔
904
            value = valueObject.toString();
1✔
905
        }
906

907
        return value;
1✔
908
    }
909

910
    private String readFileFromAssetsFolder(String fileName) throws IOException {
911
        String fileContents = null;
1✔
912
        InputStream inputStream = null;
1✔
913
        try {
914

915
            inputStream = mContext.getAssets().open(fileName);
1✔
916
            fileContents = IOUtils.toString(inputStream);
1✔
917

918
        } catch (IOException e) {
×
919
            Timber.e(e);
×
920
        } finally {
921
            if (inputStream != null) {
1✔
922
                inputStream.close();
1✔
923
            }
924
        }
925

926
        return fileContents;
1✔
927
    }
928

929
    public JSONObject getFormJson(String formIdentity) {
930
        if (mContext != null) {
1✔
931

932
            InputStream inputStream = null;
1✔
933
            try {
934
                String locale = mContext.getResources().getConfiguration().locale.getLanguage();
1✔
935
                locale = locale.equalsIgnoreCase(Locale.ENGLISH.getLanguage()) ? "" : "-" + locale;
1✔
936
                try {
937
                    inputStream = mContext.getApplicationContext().getAssets()
1✔
938
                            .open("json.form" + locale + "/" + formIdentity + AllConstants.JSON_FILE_EXTENSION);
1✔
939
                } catch (FileNotFoundException e) {
×
940
                    // file for the language not found, defaulting to english language
941
                    inputStream = mContext.getApplicationContext().getAssets()
×
942
                            .open("json.form/" + formIdentity + AllConstants.JSON_FILE_EXTENSION);
×
943
                }
1✔
944
                String rawForm = IOUtils.toString(inputStream);
1✔
945
                return new JSONObject(rawForm);
1✔
946
            } catch (IOException | JSONException e) {
×
947
                Timber.e(e);
×
948
            } finally {
949
                if (inputStream != null) {
1✔
950
                    try {
951
                        inputStream.close();
1✔
952
                    } catch (IOException e) {
×
953
                        Timber.e(e);
×
954
                    }
1✔
955
                }
956
            }
957
        }
958
        return null;
×
959
    }
960

961
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc