Changeset 1520

Show
Ignore:
Timestamp:
12/04/08 14:14:54 (5 weeks ago)
Author:
droos
Message:

more instance parsing and validation

Location:
branches/dev-repeat/javarosa
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • branches/dev-repeat/javarosa/org.javarosa.core.model/src/org/javarosa/core/model/instance/DataModelTree.java

    r1519 r1520  
    5757        public void setRoot(TreeElement topLevel) { 
    5858                root = new QuestionDataGroup(null, 0); 
    59                 ((QuestionDataGroup)root).addChild(topLevel); 
     59                if (topLevel != null) 
     60                        ((QuestionDataGroup)root).addChild(topLevel); 
    6061        } 
    6162         
     
    6465         */ 
    6566        public TreeElement getRoot() { 
    66                 return root == null ? null : (TreeElement)((QuestionDataGroup)root).getChildren().elementAt(0); 
     67                if (root == null) 
     68                        return null; 
     69                else if (((QuestionDataGroup)root).children.size() == 0) 
     70                        return null; 
     71                else     
     72                        return (TreeElement)((QuestionDataGroup)root).getChildren().elementAt(0); 
    6773        } 
    6874 
     
    7581        public static TreeReference unpackReference (IDataReference ref) { 
    7682                return (TreeReference)ref.getReference(); 
    77         } 
    78          
    79         //IS THIS FUNCTION NEEDED? 
    80         //create the specified node in the tree, creating all intermediary nodes 
    81         //terminal = true: create element, false: create group (albeit empty) 
    82         //at each step, if multiplicity = 
    83         //  ALL or count: always create a new node at the step 
    84         //  [0,count): use that specific node 
    85         //return a reference that unambiguously refers to the newly created node 
    86 //      public TreeReference createNode (IDataReference ref, boolean terminal) { 
    87 //              QuestionDataGroup node = (QuestionDataGroup)root; 
    88 //              TreeReference tref = unpackReference(ref); 
    89 //               
    90 //              for (int k = 0; k < tref.size(); k++) { 
    91 //                      String name = (String)tref.names.elementAt(k); 
    92 //                      int count = node.getMultiplicity(name); 
    93 //                      int mult = ((Integer)tref.multiplicity.elementAt(k)).intValue(); 
    94 //                       
    95 //                      TreeElement child; 
    96 //                      if (mult < count) { 
    97 //                              //fetch existing 
    98 //                              child = node.getChild(name, mult); 
    99 //                              if (child == null) 
    100 //                                      return null; //something wrong 
    101 //                      } else if (mult == TreeReference.INDEX_UNBOUND || mult == count) { 
    102 //                              //create new 
    103 //                              if (k == tref.size() - 1 && terminal) { 
    104 //                                      child = new QuestionDataElement(name, count, null); 
    105 //                              } else { 
    106 //                                      child = new QuestionDataGroup(name, count); 
    107 //                              } 
    108 //                              node.addChild(child); 
    109 //                              tref.multiplicity.setElementAt(new Integer(count), k); 
    110 //                      } else { 
    111 //                              return null; 
    112 //                      } 
    113 //                       
    114 //                      if (k < tref.size() - 1) { 
    115 //                              if (child instanceof QuestionDataElement) { 
    116 //                                      throw new IllegalArgumentException(); 
    117 //                              }        
    118 // 
    119 //                              node = (QuestionDataGroup)child; 
    120 //                      } 
    121 //              } 
    122 //      } 
    123         //think this works 
     83        }        
    12484         
    12585        public boolean deleteNode (IDataReference ref) { 
     
    161121                } 
    162122        } 
    163          
    164         public TreeElement getTemplate (TreeReference ref) { 
    165                 TreeReference tref = ref.clone(); //need to set all indexes to 0 first? 
    166                 Vector nodes = explodeReference(ref); 
    167                 if (nodes == null) 
    168                         return null; 
    169                  
    170                 for (int i = 0; i < nodes.size(); i++) { 
    171                         TreeElement node = (TreeElement)nodes.elementAt(i); 
    172                         if (node.repeatable) 
    173                                 tref.multiplicity.setElementAt(new Integer(TreeReference.INDEX_TEMPLATE), i); 
    174                 } 
    175                 return resolveReference(tref); 
    176         } 
    177          
    178         //return a vector of TreeReferences that refer to all nodes in the instance (one ref per node) that match 
    179         //the passed-in TreeReference, accounting for all repeats. 
    180         //the returned refs will each unambiguously refer to a single node (i.e., no multiplicities will be 'ALL') 
     123 
     124        //take a ref that unambiguously refers to a single node and return that node 
     125        //return null if ref is ambiguous, node does not exist, ref is relative, or ref is '/' 
     126        //can be used to retrieve template nodes 
     127        public TreeElement resolveReference (TreeReference ref) { 
     128                if (!ref.isAbsolute()) 
     129                        return null; 
     130                 
     131                TreeElement node = root; 
     132                for (int i = 0; i < ref.size(); i++) { 
     133                        if (!(node instanceof QuestionDataGroup)) { 
     134                                node = null; 
     135                                break; 
     136                        } 
     137                        QuestionDataGroup group = (QuestionDataGroup)node; 
     138                         
     139                        String name = (String)ref.names.elementAt(i); 
     140                        int mult = ((Integer)ref.multiplicity.elementAt(i)).intValue(); 
     141                        if (mult == TreeReference.INDEX_UNBOUND) { 
     142                                if (group.children.size() == 1) { 
     143                                        mult = 0; 
     144                                } else { 
     145                                        //reference is not unambiguous 
     146                                        node = null; 
     147                                        break; 
     148                                } 
     149                        } 
     150                         
     151                        node = group.getChild(name, mult); 
     152                        if (node == null) 
     153                                break; 
     154                } 
     155                return (node == root ? null : node); //never return a reference to '/' 
     156        } 
     157 
     158        //same as resolveReference but return a vector containing all interstitial nodes: top-level instance data node first, and target node last 
     159        //returns null in all the same situations as resolveReference EXCEPT ref '/' will return empty vector 
     160        public Vector explodeReference (TreeReference ref) { 
     161                if (!ref.isAbsolute()) 
     162                        return null; 
     163                 
     164                Vector nodes = new Vector(); 
     165                TreeElement cur = root; 
     166                for (int i = 0; i < ref.size(); i++) { 
     167                        if (!(cur instanceof QuestionDataGroup)) { 
     168                                return null; 
     169                        } 
     170                        QuestionDataGroup group = (QuestionDataGroup)cur; 
     171                         
     172                        String name = (String)ref.names.elementAt(i); 
     173                        int mult = ((Integer)ref.multiplicity.elementAt(i)).intValue(); 
     174                        if (mult == TreeReference.INDEX_UNBOUND) { 
     175                                if (group.children.size() == 1) { 
     176                                        mult = 0; 
     177                                } else { 
     178                                        //reference is not unambiguous 
     179                                        return null; 
     180                                } 
     181                        } 
     182                         
     183                        if (cur != root) { 
     184                                nodes.addElement(cur); 
     185                        } 
     186                         
     187                        cur = group.getChild(name, mult); 
     188                        if (cur == null) { 
     189                                return null; 
     190                        } 
     191                } 
     192                return nodes;            
     193        } 
     194 
     195        //take in a potentially-ambiguous ref, and return a vector of refs for all nodes that match the passed-in ref 
     196        //meaning, search out all repeated nodes that match the pattern of the passed-in ref 
     197        //every ref in the returned vector will be unambiguous (no index will ever be INDEX_UNBOUND) 
     198        //does not return template nodes when matching INDEX_UNBOUND, but will match templates when INDEX_TEMPLATE is explicitly set 
     199        //return null if ref is relative, otherwise return vector of refs (but vector will be empty is no refs match) 
     200        //'/' returns {'/'} 
     201        //can handle sub-repetitions (e.g., {/a[1]/b[1], /a[1]/b[2], /a[2]/b[1]}) 
    181202        public Vector expandReference (TreeReference ref) { 
    182203                if (!ref.isAbsolute()) 
     
    188209        } 
    189210         
    190         //helper function for expandReference(ref) 
     211        //recursive helper function for expandReference 
    191212        //sourceRef: original path we're matching against 
    192213        //node: current node that has matched the sourceRef thus far 
     
    231252        } 
    232253         
    233         public TreeElement resolveReference (TreeReference ref) { 
    234                 if (!ref.isAbsolute()) 
    235                         return null; 
    236                  
    237                 TreeElement node = root; 
    238                 for (int i = 0; i < ref.size(); i++) { 
    239                         if (!(node instanceof QuestionDataGroup)) { 
    240                                 node = null; 
    241                                 break; 
    242                         } 
    243                         QuestionDataGroup group = (QuestionDataGroup)node; 
    244                          
    245                         String name = (String)ref.names.elementAt(i); 
    246                         int mult = ((Integer)ref.multiplicity.elementAt(i)).intValue(); 
    247                         if (mult == TreeReference.INDEX_UNBOUND) { 
    248                                 if (group.children.size() == 1) { 
    249                                         mult = 0; 
    250                                 } else { 
    251                                         //reference is not unambiguous 
    252                                         node = null; 
    253                                         break; 
    254                                 } 
    255                         } 
    256                          
    257                         node = group.getChild(name, mult); 
    258                         if (node == null) 
    259                                 break; 
    260                 } 
    261                 return (node == root ? null : node); //never return a reference to '/' 
     254        //THIS WILL NOT WORK 
     255        //retrieve the template node corresponding to a given repeated node, if one exists 
     256        public TreeElement getTemplate (TreeReference ref) { 
     257                TreeReference tref = ref.clone(); 
     258                for (int i = 0; i < tref.size(); i++) { 
     259                        tref.multiplicity.setElementAt(new Integer(0), i); 
     260                } 
     261                 
     262                Vector nodes = explodeReference(ref); 
     263                if (nodes == null) 
     264                        return null; 
     265                 
     266                for (int i = 0; i < nodes.size(); i++) { 
     267                        TreeElement node = (TreeElement)nodes.elementAt(i); 
     268                        if (node.repeatable) 
     269                                tref.multiplicity.setElementAt(new Integer(TreeReference.INDEX_TEMPLATE), i); 
     270                } 
     271                return resolveReference(tref); 
    262272        } 
    263273         
     
    271281        public TreeElement resolveReference(IDataReference binding) { 
    272282                return resolveReference(unpackReference(binding)); 
    273         } 
    274          
    275         //same as resolve reference but returns a vector containing all interstitial nodes 
    276         public Vector explodeReference (TreeReference ref) { 
    277                 if (!ref.isAbsolute()) 
    278                         return null; 
    279                  
    280                 Vector nodes = new Vector(); 
    281                 TreeElement cur = root; 
    282                 for (int i = 0; i < ref.size(); i++) { 
    283                         if (!(cur instanceof QuestionDataGroup)) { 
    284                                 return null; 
    285                         } 
    286                         QuestionDataGroup group = (QuestionDataGroup)cur; 
    287                          
    288                         String name = (String)ref.names.elementAt(i); 
    289                         int mult = ((Integer)ref.multiplicity.elementAt(i)).intValue(); 
    290                         if (mult == TreeReference.INDEX_UNBOUND) { 
    291                                 if (group.children.size() == 1) { 
    292                                         mult = 0; 
    293                                 } else { 
    294                                         //reference is not unambiguous 
    295                                         return null; 
    296                                 } 
    297                         } 
    298                          
    299                         if (cur != root) { 
    300                                 nodes.addElement(cur); 
    301                         } 
    302                          
    303                         cur = group.getChild(name, mult); 
    304                         if (cur == null) { 
    305                                 return null; 
    306                         } 
    307                 } 
    308                 return nodes; 
    309                  
    310283        } 
    311284         
     
    414387        } 
    415388} 
     389 
     390 
     391//IS THIS FUNCTION NEEDED? 
     392//create the specified node in the tree, creating all intermediary nodes 
     393//terminal = true: create element, false: create group (albeit empty) 
     394//at each step, if multiplicity = 
     395//  ALL or count: always create a new node at the step 
     396//  [0,count): use that specific node 
     397//return a reference that unambiguously refers to the newly created node 
     398//public TreeReference createNode (IDataReference ref, boolean terminal) { 
     399//      QuestionDataGroup node = (QuestionDataGroup)root; 
     400//      TreeReference tref = unpackReference(ref); 
     401//       
     402//      for (int k = 0; k < tref.size(); k++) { 
     403//              String name = (String)tref.names.elementAt(k); 
     404//              int count = node.getMultiplicity(name); 
     405//              int mult = ((Integer)tref.multiplicity.elementAt(k)).intValue(); 
     406//               
     407//              TreeElement child; 
     408//              if (mult < count) { 
     409//                      //fetch existing 
     410//                      child = node.getChild(name, mult); 
     411//                      if (child == null) 
     412//                              return null; //something wrong 
     413//              } else if (mult == TreeReference.INDEX_UNBOUND || mult == count) { 
     414//                      //create new 
     415//                      if (k == tref.size() - 1 && terminal) { 
     416//                              child = new QuestionDataElement(name, count, null); 
     417//                      } else { 
     418//                              child = new QuestionDataGroup(name, count); 
     419//                      } 
     420//                      node.addChild(child); 
     421//                      tref.multiplicity.setElementAt(new Integer(count), k); 
     422//              } else { 
     423//                      return null; 
     424//              } 
     425//               
     426//              if (k < tref.size() - 1) { 
     427//                      if (child instanceof QuestionDataElement) { 
     428//                              throw new IllegalArgumentException(); 
     429//                      }        
     430// 
     431//                      node = (QuestionDataGroup)child; 
     432//              } 
     433//      } 
     434//} 
     435//think this works 
  • branches/dev-repeat/javarosa/org.javarosa.xform/src/org/javarosa/xform/parse/XFormParser.java

    r1519 r1520  
    247247 
    248248        private static void parseTitle (FormDef f, Element e) { 
    249                 //Removed a line here about the form title not being null. Couldn't possibly think 
    250                 //of why that would make sense. CTS - 7/21/2008 
    251249                f.setName(getXMLText(e, true)); 
    252250        } 
     
    835833        //evaluate binds and apply properties 
    836834        //load data based on data type 
    837  
     835        //verify bindings (also detect bindings too high; repeats too high already checked) 
     836         
    838837        //parse instance hierarchy and turn into a skeleton model; ignoring data content, but respecting repeated nodes and 'template' flags 
    839838        private static TreeElement buildInstanceStructure (Element node, QuestionDataGroup parent) { 
     
    906905        } 
    907906         
     907        //pre-process and clean up instance regarding repeats; in particular: 
     908        // 1) catalog which repeat template nodes are explicitly defined, and note which repeats bindings lack templates 
     909        // 2) remove template nodes that are not valid for a repeat binding 
     910        // 3) catch repeat bindings that are too high ('/' or '/data') 
     911        // #4) generate template nodes for repeat bindings that do not have one defined explicitly 
     912        // 5) give a stern warning for any repeated instance nodes that do not correspond to a repeat binding 
     913        // #6) verify that all sets of repeated nodes are homogeneous 
     914        // 7) flag all repeat-related nodes as repeatable 
    908915        private static void processRepeats (DataModelTree instance) { 
    909                 Vector missingTemplates = new Vector(); 
    910                  
    911                 //flag all nodes identified by 'repeat' refs as repeatable 
     916                processTemplates(instance); 
     917                 
     918                //flag all nodes identified by repeat bindings as repeatable (template nodes already taken care of) 
    912919                for (int i = 0; i < repeats.size(); i++) { 
    913920                        TreeReference ref = (TreeReference)repeats.elementAt(i); 
     
    920927                } 
    921928                 
    922                 //locate all valid template nodes and flag them as repeatable as well 
    923                 for (int i = 0; i < repeats.size(); i++) { 
    924                         TreeReference ref = (TreeReference)repeats.elementAt(i); 
    925                         TreeElement template = instance.getTemplate(ref); 
     929                checkDuplicateNodesAreRepeatable(instance.getRoot()); 
     930                 
     931                //check repeat sets for homogeneity 
     932        } 
     933 
     934        private static void processTemplates (DataModelTree instance) { 
     935                DataModelTree repeatTree = buildRepeatTree(repeats, instance.getRoot().getName()); 
     936                 
     937                Vector missingTemplates = new Vector(); //Vector<TreeReference> 
     938                checkRepeatsForTemplate(instance, repeatTree, missingTemplates); 
     939 
     940                removeInvalidTemplates(instance, repeatTree); 
     941                 
     942                 
     943                //if repeatables have no template node, duplicate first as template 
     944 
     945        } 
     946         
     947        //build a pseudo-data model tree that describes the repeat structure of the instance 
     948        //result is a DataModelTree collapsed where all indexes are 0, and repeatable nodes are flagged as such 
     949        //return null if no repeats 
     950        //raises exception for repeats bound too highly 
     951        //ignores (invalid) repeats that bind outside the top-level instance data node 
     952        private static DataModelTree buildRepeatTree (Vector repeatRefs, String topLevelName) { 
     953                QuestionDataGroup root = new QuestionDataGroup(null, 0); 
     954                 
     955                for (int i = 0; i < repeatRefs.size(); i++) { 
     956                        TreeReference repeatRef = (TreeReference)repeatRefs.elementAt(i); 
     957                        if (repeatRef.size() <= 1) { 
     958                                throw new XFormParseException("Cannot repeat root instance node or top-level instance data node"); 
     959                        } 
     960                         
     961                        QuestionDataGroup cur = root; 
     962                        for (int j = 0; j < repeatRef.size(); j++) { 
     963                                String name = (String)repeatRef.names.elementAt(j); 
     964                                QuestionDataGroup child = (QuestionDataGroup)cur.getChild(name, 0); 
     965                                if (child == null) { 
     966                                        child = new QuestionDataGroup(name, 0); 
     967                                        cur.addChild(child); 
     968                                } 
     969                                 
     970                                cur = child; 
     971                                if (j == repeatRef.size() - 1) { 
     972                                        cur.repeatable = true; 
     973                                }                                
     974                        } 
     975                } 
     976                 
     977                if (root.getChildren().size() == 0) 
     978                        return null; 
     979                else 
     980                        return new DataModelTree(root.getChild(topLevelName, 0)); 
     981        } 
     982 
     983        //checks which repeat bindings have explicit template nodes; returns a vector of the bindings that do not 
     984        //for those that exist, flag them as repeatable 
     985        private static void checkRepeatsForTemplate (DataModelTree instance, DataModelTree instanceTree, Vector missingTemplates) { 
     986                checkRepeatsForTemplate((QuestionDataGroup)instanceTree.getRoot(), TreeReference.rootRef(), instance, missingTemplates); 
     987        } 
     988         
     989        //helper function for checkRepeatsForTemplate 
     990        private static void checkRepeatsForTemplate (QuestionDataGroup repeatTreeNode, TreeReference ref, DataModelTree instance, Vector missing) { 
     991                String name = repeatTreeNode.getName(); 
     992                int mult = (repeatTreeNode.repeatable ? TreeReference.INDEX_TEMPLATE : 0); 
     993                ref = ref.clone(); 
     994                ref.add(name, mult); 
     995                 
     996                if (repeatTreeNode.repeatable) { 
     997                        TreeElement template = instance.resolveReference(ref); 
    926998                        if (template == null) { 
    927                                 missingTemplates.addElement(ref); 
     999                                missing.addElement(ref); 
    9281000                        } else { 
    9291001                                template.repeatable = true; 
    9301002                        } 
    9311003                } 
    932                  
    933                 //warn of any 'template' nodes that aren't repeatable 
    934                 //warn of any delete incorrect sub-templates 
    935                 //error for any repeated nodes that aren't repeatable 
    936                 //if repeatables have no template node, duplicate first as template 
    937                 //check repeat sets for homogeneity 
     1004                         
     1005                for (int i = 0; i < repeatTreeNode.getChildren().size(); i++) { 
     1006                        checkRepeatsForTemplate((QuestionDataGroup)repeatTreeNode.getChildren().elementAt(i), ref, instance, missing); 
     1007                } 
     1008        } 
     1009         
     1010        //iterates through instance and removes template nodes that are not valid. a template is invalid if: 
     1011        //  it is declared for a node that is not repeatable 
     1012        //  it is for a repeat that is a child of another repeat and is not located within the parent's template node 
     1013        private static void removeInvalidTemplates (DataModelTree instance, DataModelTree repeatTree) { 
     1014                removeInvalidTemplates(instance.getRoot(), (QuestionDataGroup)repeatTree.getRoot(), true); 
     1015        } 
     1016         
     1017        //helper function for removeInvalidTemplates 
     1018        private static boolean removeInvalidTemplates (TreeElement instanceNode, QuestionDataGroup repeatTreeNode, boolean templateAllowed) { 
     1019                int mult = instanceNode.getMult(); 
     1020                boolean repeatable = (repeatTreeNode == null ? false : repeatTreeNode.repeatable); 
     1021                 
     1022                if (mult == TreeReference.INDEX_TEMPLATE) { 
     1023                        if (!templateAllowed) { 
     1024                                System.out.println("Warning: template nodes for sub-repeats must be located within the template nodes for all ancestor repeats; ignoring template..."); 
     1025                                return true; 
     1026                        } else if (!repeatable) { 
     1027                                System.out.println("Warning: template node found for ref that is not repeatable; ignoring..."); 
     1028                                return true; 
     1029                        } 
     1030                } 
     1031                 
     1032                if (repeatable && mult != TreeReference.INDEX_TEMPLATE) 
     1033                        templateAllowed = false; 
     1034                 
     1035                if (instanceNode instanceof QuestionDataGroup) { 
     1036                        QuestionDataGroup group = (QuestionDataGroup)instanceNode; 
     1037                        for (int i = 0; i < group.getChildren().size(); i++) { 
     1038                                TreeElement child = (TreeElement)group.getChildren().elementAt(i); 
     1039                                QuestionDataGroup rchild = (repeatTreeNode == null ? null : (QuestionDataGroup)repeatTreeNode.getChild(instanceNode.getName(), 0)); 
     1040                                 
     1041                                if (removeInvalidTemplates(child, rchild, templateAllowed)) { 
     1042                                        group.getChildren().removeElementAt(i); 
     1043                                        i--; 
     1044                                } 
     1045                        } 
     1046 
     1047                } 
     1048                return false; 
     1049        } 
     1050         
     1051        private static void checkDuplicateNodesAreRepeatable (TreeElement node) { 
     1052                int mult = node.getMult(); 
     1053                if (mult > 0) { //repeated node 
     1054                        if (!node.repeatable) { 
     1055                                System.out.println("Warning: repeated nodes detected that have no repeat binding in the form; DO NOT bind questions to these nodes!"); 
     1056                                //we could do a more comprehensive safety check in the future 
     1057                        } 
     1058                } 
     1059                 
     1060                if (node instanceof QuestionDataGroup) { 
     1061                        QuestionDataGroup group = (QuestionDataGroup)node; 
     1062                        for (int i = 0; i < group.getChildren().size(); i++) { 
     1063                                checkDuplicateNodesAreRepeatable((TreeElement)group.getChildren().elementAt(i)); 
     1064                        } 
     1065                } 
    9381066        } 
    9391067