package org.commcare.cases.instance;

import org.javarosa.core.model.instance.ExternalDataInstance;
import org.javarosa.core.model.instance.TreeElement;
import org.javarosa.core.model.instance.TreeReference;
import org.javarosa.core.model.instance.utils.FormLoadingUtils;
import org.javarosa.core.services.Logger;
import org.javarosa.xml.util.InvalidStructureException;

import java.io.IOException;

/**
 * An external data instance that respects CaseDB template specifications.
 *
 * @author Phillip Mates (pmates@dimagi.com)
 */
public class CaseDataInstance extends ExternalDataInstance {
    private static TreeElement caseDbSpecTemplate = null;
    private static final String CASEDB_WILD_CARD = "CASEDB_WILD_CARD";
    private static final Object lock = new Object();

    public CaseDataInstance() {
        // For serialization
    }

    /**
     * Copy constructor
     */
    public CaseDataInstance(ExternalDataInstance instance) {
        super(instance);
    }

    /**
     * Does the reference follow the statically defined CaseDB spec?
     */
    public boolean hasTemplatePath(TreeReference ref) {
        synchronized (lock) {
            loadTemplateSpecLazily();

            return followsTemplateSpec(ref, caseDbSpecTemplate, 0);
        }
    }

    private static void loadTemplateSpecLazily() {
        if (caseDbSpecTemplate == null) {
            try {
                caseDbSpecTemplate =
                        FormLoadingUtils.xmlToTreeElement("/casedb_instance_structure.xml");
            } catch (InvalidStructureException e) {
                throw new RuntimeException("Failed to load casedb template spec");
            } catch (IOException e) {
                throw new RuntimeException("Failed to load casedb template spec");
            }
        }
    }

    private static boolean followsTemplateSpec(TreeReference refToCheck,
                                               TreeElement currTemplateNode,
                                               int currRefDepth) {
        if (currTemplateNode == null) {
            return false;
        }

        if (currRefDepth == refToCheck.size()) {
            return true;
        }

        String name = refToCheck.getName(currRefDepth);

        if (refToCheck.getMultiplicity(currRefDepth) == TreeReference.INDEX_ATTRIBUTE) {
            TreeElement templateAttr = currTemplateNode.getAttribute(null, name);
            return followsTemplateSpec(refToCheck, templateAttr, currRefDepth + 1);
        } else {
            TreeElement nextTemplateNode = currTemplateNode.getChild(name, 0);
            if (nextTemplateNode == null) {
                // didn't find a node of the given name in the template, check
                // if a wild card exists at this level of the template.
                nextTemplateNode = currTemplateNode.getChild(CASEDB_WILD_CARD, 0);
            }
            return followsTemplateSpec(refToCheck, nextTemplateNode, currRefDepth + 1);
        }
    }
}

@@ -269,50 +269,8 @@
 ls;%0A
-import org.javarosa.core.services.Logger;%0A
 impo
@@ -539,24 +539,103 @@
 aInstance %7B%0A
+    private static final String TAG = CaseDataInstance.class.getSimpleName();%0A%0A
     private 
@@ -754,61 +754,8 @@
 RD%22;
-%0A    private static final Object lock = new Object();
 %0A%0A  
@@ -1098,38 +1098,8 @@
 ) %7B%0A
-        synchronized (lock) %7B%0A
     
@@ -1201,26 +1201,16 @@
 te, 0);%0A
-        %7D%0A
     %7D%0A%0A 
@@ -1227,16 +1227,29 @@
  static 
+synchronized 
 void loa
@@ -1268,24 +1268,202 @@
 cLazily() %7B%0A
+        final String errorMsg = TAG + %22: Failed to load casedb template spec xml file %22 +%0A                %22while checking if case related xpath follows the template structure.%22;%0A
         if (
@@ -1739,45 +1739,16 @@
 ion(
-%22Failed to load casedb template spec%22
+errorMsg
 );%0A 
@@ -1831,45 +1831,16 @@
 ion(
-%22Failed to load casedb template spec%22
+errorMsg
 );%0A 

Move lock and use better error message