Fix the printing some more
[SXSI/XMLTree.git] / XMLTree.cpp
1 #include "basics.h"\r
2 //#include <cstring>\r
3 #include <stack>\r
4 #include "XMLTree.h"\r
5 #include "timings.h"\r
6 #include <errno.h>\r
7 // functions to convert tag positions to the corresponding tree node and viceversa. \r
8 // These are implemented in order to be able to change the tree and Tags representations, \r
9 // without affecting the code so much.\r
10 // Current implementation corresponds to balanced-parentheses representation for\r
11 // the tree, and storing 2 tags per tree node (opening and closing tags).\r
12 \r
13 // tag position -> tree node\r
14 static treeNode tagpos2node(int t) \r
15  {\r
16     return (treeNode) t;\r
17  }\r
18 \r
19 static int bits8 (int t ) {\r
20   int r = bits(t);\r
21   if (r <= 8)\r
22     return 8;\r
23   else if (r <= 16)\r
24     return 16;\r
25   else \r
26     return r;\r
27 }\r
28 \r
29 // tree node -> tag position\r
30 static int node2tagpos(treeNode x) \r
31 {\r
32   return (int)x;\r
33 }\r
34 \r
35 static int fast_find_close(bp *b,int s)\r
36 {\r
37   return fwd_excess(b,s,-1);\r
38 }\r
39 \r
40 static int fast_inspect(bp* Par,treeNode i)\r
41 {\r
42   int j,l;\r
43   j = i >> logD;\r
44   l = i & (D-1);\r
45   return (Par->B[j] >> (D-1-l)) & 1;\r
46 }\r
47 \r
48 static treeNode fast_first_child(bp *Par, treeNode x)\r
49 {\r
50   x = x+1;\r
51   return (fast_inspect(Par,x) == OP) ? x : NULLT;\r
52 }\r
53 \r
54 static treeNode fast_next_sibling(bp* Par,treeNode x)\r
55 {\r
56   x = fast_find_close(Par,x)+1;\r
57   return (fast_inspect(Par,x) == OP) ? x : NULLT;\r
58 }\r
59 \r
60 \r
61 static treeNode fast_sibling(bp* Par,treeNode x,TagType tag){\r
62 \r
63   if (tag == PCDATA_TAG_ID){\r
64     x = x+2;\r
65     return fast_inspect(Par,x)==OP ? x : NULLT;\r
66   } else return fast_next_sibling(Par,x);\r
67 \r
68 }\r
69 \r
70 static bool fast_isleaf(bp* Par,treeNode x){\r
71   return (fast_inspect(Par,x+1) == CP ? true : false);\r
72 }\r
73 \r
74 \r
75 inline uint get_field_no_power(uint *A, uint len, uint index) {\r
76   \r
77   register uint i=index*len/W, j=index*len-W*i;\r
78   return (j+len <= W) ? (A[i] << (W-j-len)) >> (W-len) : (A[i] >> j) | (A[i+1] << (WW-j-len)) >> (W-len);\r
79 \r
80 }\r
81 \r
82 static uint fast_get_field(uint* A,int len, int idx)\r
83 {\r
84   uint f1, f2;\r
85   switch (len) {\r
86   case 8:\r
87     return (uint) (((uchar*)A)[idx]);\r
88   case 16:\r
89     f2 = ((unsigned short*)A)[idx];\r
90     f1 = ((unsigned short*)A)[idx+1];\r
91     return (f1 << 16) + f2;\r
92   default:\r
93     return   get_field_no_power (A,len,idx);\r
94   };\r
95 \r
96 }\r
97 \r
98 inline bool fast_is_ancestor(bp * Par,treeNode x,treeNode y){\r
99   if (x > y) \r
100     return false;\r
101   else\r
102     return (x==0) || (y <= fast_find_close(Par,x));\r
103 }\r
104 \r
105 \r
106 XMLTree::XMLTree( pb * const par, uint npar,  vector<string> * const TN,  TagIdMap * const tim, \r
107                   uint *empty_texts_bmp, TagType *tags,\r
108                   TextCollection * const TC, bool dis_tc)\r
109  {\r
110    buffer = 0;\r
111     // creates the data structure for the tree topology\r
112     Par = (bp *)umalloc(sizeof(bp));\r
113     STARTTIMER();\r
114     bp_construct(Par, npar, (pb*) par, OPT_DEGREE|0);\r
115     STOPTIMER(Building);\r
116     PRINTTIME("Building parenthesis struct", Building);\r
117     STARTTIMER();\r
118 \r
119    \r
120     // creates structure for tags\r
121 \r
122     TagName = (vector<string>*)TN;\r
123     tIdMap = (TagIdMap *) tim;\r
124     \r
125     uint max_tag = TN->size() - 1;\r
126     \r
127       \r
128     static_bitsequence_builder *bmb = new static_bitsequence_builder_sdarray();\r
129     alphabet_mapper *am = new alphabet_mapper_none();\r
130     Tags = new static_sequence_bs((uint*)tags,npar,am,bmb);\r
131     \r
132     //cout << "Tags test: " << Tags->test((uint*)tags,npar) << endl;\r
133 \r
134     //Ensures that for small tag numbers, we are on an 8bit boundary.\r
135     //Makes tag access way faster with negligeable waste of space.\r
136     tags_blen = bits8(max_tag);\r
137     std::cerr << "Tags blen is " << tags_blen << "\n";\r
138     tags_len = (uint)npar;\r
139     tags_fix = new uint[uint_len(tags_blen,tags_len)];\r
140     for(uint i=0;i<(uint)npar;i++)\r
141        set_field(tags_fix,tags_blen,i,tags[i]);\r
142     delete bmb;    \r
143     free(tags);\r
144     tags = NULL;\r
145 \r
146     STOPTIMER(Building);\r
147     PRINTTIME("Building Tag Structure", Building);\r
148     \r
149     Text = (TextCollection*) TC;\r
150 \r
151 \r
152     EBVector = new static_bitsequence_rrr02(empty_texts_bmp,npar,32);\r
153     //EBVector = new static_bitsequence_sdarray(empty_texts_bmp,npar);\r
154     free(empty_texts_bmp);\r
155     empty_texts_bmp = NULL;\r
156 \r
157     \r
158     disable_tc = dis_tc;\r
159     stream = NULL;\r
160     stream_fd = 0;\r
161     std::cerr << "Number of distinct tags " << TagName->size() << "\n";\r
162     //std::cerr.flush();\r
163  }\r
164 \r
165 \r
166 // ~XMLTree: frees memory of XML tree.\r
167 XMLTree::~XMLTree() \r
168  { \r
169     int i;\r
170 \r
171     destroyTree(Par);\r
172     free(Par); // frees the memory of struct Par\r
173    \r
174     delete tIdMap;\r
175     tIdMap = NULL;\r
176     \r
177     delete TagName;\r
178     TagName = NULL;\r
179     \r
180     delete Tags;\r
181     Tags = NULL;\r
182 \r
183     delete Text; \r
184     Text = NULL;\r
185 \r
186     delete EBVector;\r
187     EBVector = NULL;\r
188     if (stream != NULL){\r
189       fclose(stream);\r
190       stream = NULL;\r
191       stream_fd = 0;\r
192     };\r
193 \r
194  }\r
195 \r
196 \r
197 void XMLTree::print_stats() \r
198  {\r
199     uint total_space = Tags->size()+sizeof(static_sequence*);\r
200     total_space += sizeof(uint*)+sizeof(uint)*(2+uint_len(tags_blen,tags_len));\r
201     cout << "Space usage for XMLTree:" << endl\r
202          << " - tags static_sequence: " << Tags->size()+sizeof(static_sequence*) << endl\r
203          << " - tags access array:    " << sizeof(uint*)+sizeof(uint)*(2+uint_len(tags_blen,tags_len)) << endl\r
204          << " ... add Diego structures ... " << endl\r
205          << " *total* " << total_space << endl;\r
206  }\r
207 \r
208 // Save: saves XML tree data structure to file. \r
209 void XMLTree::Save(int fd) \r
210  {\r
211     FILE *fp;\r
212     char filenameaux[1024];\r
213     int i;\r
214 \r
215     fp = fdopen(fd, "wa");\r
216     // first stores the tree topology\r
217     saveTree(Par, fp);\r
218 \r
219     // stores the table with tag names\r
220     int ntags = TagName->size();\r
221 \r
222     ufwrite(&ntags, sizeof(int), 1, fp);\r
223     for (i = 0; i<ntags;i++)\r
224       fprintf(fp, "%s\n",TagName->at(i).c_str());\r
225     \r
226 \r
227     // stores the tags\r
228     Tags->save(fp);\r
229     ufwrite(&tags_blen,sizeof(uint),1,fp);\r
230     ufwrite(&tags_len,sizeof(uint),1,fp);\r
231     ufwrite(tags_fix,sizeof(uint),uint_len(tags_blen,tags_len),fp);\r
232 \r
233     // flags \r
234     ufwrite(&disable_tc, sizeof(bool),1,fp);\r
235     \r
236     //text positions\r
237     EBVector->save(fp);\r
238     \r
239     // stores the texts   \r
240     if (!disable_tc) {\r
241       Text->Save(fp);\r
242     };\r
243 \r
244 \r
245  }\r
246 \r
247 \r
248 // Load: loads XML tree data structure from file. Returns\r
249 // a pointer to the loaded data structure\r
250 XMLTree *XMLTree::Load(int fd, bool load_tc,int sample_factor) \r
251  {\r
252 \r
253     FILE *fp;\r
254     char buffer[1024];\r
255     XMLTree *XML_Tree;\r
256     int i;\r
257 \r
258 \r
259 \r
260     fp = fdopen(fd, "r");\r
261 \r
262     XML_Tree = new XMLTree();\r
263     STARTTIMER();\r
264     // Load the tree structure\r
265     XML_Tree->Par = (bp *)umalloc(sizeof(bp));\r
266 \r
267     loadTree(XML_Tree->Par, fp); \r
268     STOPTIMER(Loading);\r
269     PRINTTIME("Loading parenthesis struct", Loading);\r
270     STARTTIMER();\r
271 \r
272     XML_Tree->TagName = new vector<string>();\r
273     XML_Tree->tIdMap = new std::unordered_map<string,int>();\r
274     \r
275     string s;\r
276     int ntags;\r
277     \r
278     // Load the tag names\r
279     ufread(&ntags, sizeof(int), 1, fp);\r
280 \r
281     for (i=0; i<ntags;i++) {\r
282       char * r = fgets(buffer,1023,fp);\r
283        if (r==NULL)\r
284          throw "Cannot read tag list";\r
285        s = (const char*) buffer;\r
286        // remove the trailing \n\r
287        s.erase(s.size()-1);       \r
288        XML_Tree->TagName->push_back(s);\r
289        XML_Tree->tIdMap->insert(std::make_pair(s,i));\r
290        \r
291     };\r
292     STOPTIMER(Loading);\r
293     PRINTTIME("Loading tag names struct", Loading);\r
294     STARTTIMER();\r
295 \r
296     // loads the tag structure\r
297     XML_Tree->Tags = static_sequence::load(fp);\r
298     ufread(&XML_Tree->tags_blen,sizeof(uint),1,fp);\r
299     std::cerr << "tags_blen is "<< XML_Tree->tags_blen <<"\n";    \r
300     ufread(&XML_Tree->tags_len,sizeof(uint),1,fp);\r
301     XML_Tree->tags_fix = new uint[uint_len(XML_Tree->tags_blen,XML_Tree->tags_len)];\r
302     ufread(XML_Tree->tags_fix,sizeof(uint),uint_len(XML_Tree->tags_blen,XML_Tree->tags_len),fp);\r
303 \r
304     // TODO ask francisco about this\r
305     /// FIXME:UGLY tests!\r
306     //uint * seq = new uint[XML_Tree->tags_len];\r
307     //for(uint i=0;i<XML_Tree->tags_len;i++)\r
308     //  seq[i] = get_field(XML_Tree->tags_fix,XML_Tree->tags_blen,i);\r
309     //cout << "Tags test: " << XML_Tree->Tags->test(seq,XML_Tree->tags_len) << endl;\r
310     //XML_Tree->Tags->test(seq,XML_Tree->tags_len);\r
311     //delete [] seq;\r
312     /// End ugly tests\r
313     \r
314     STOPTIMER(Loading);\r
315     std::cerr << (uint_len(XML_Tree->tags_blen,XML_Tree->tags_len)*sizeof(uint))/(1024*1024) << " MB for tag sequence" << std::endl;\r
316     PRINTTIME("Loading tag struct", Loading);\r
317     STARTTIMER();\r
318 \r
319     // loads the flags\r
320     \r
321     ufread(&(XML_Tree->disable_tc), sizeof(bool), 1, fp);\r
322     if (load_tc) {\r
323     XML_Tree->EBVector = static_bitsequence_rrr02::load(fp);\r
324     //XML_Tree->EBVector = static_bitsequence_sdarray::load(fp);\r
325 \r
326     STOPTIMER(Loading);\r
327     PRINTTIME("Loading text bitvector struct", Loading);\r
328     STARTTIMER();\r
329 \r
330     // Not used  \r
331     // loads the texts\r
332     if (!XML_Tree->disable_tc){\r
333       XML_Tree->Text = TextCollection::Load(fp,sample_factor);\r
334     }\r
335     else XML_Tree->Text = NULL;\r
336     STOPTIMER(Loading);\r
337     PRINTTIME("Loading TextCollection", Loading);\r
338     STARTTIMER(); \r
339     }\r
340     else {\r
341       XML_Tree->EBVector = NULL;\r
342       XML_Tree->Text = NULL;\r
343       XML_Tree->disable_tc = true;\r
344     };\r
345 \r
346     XML_Tree->stream = NULL;\r
347     XML_Tree->stream_fd = 0;\r
348     \r
349     return XML_Tree;\r
350  }\r
351 \r
352 \r
353 \r
354 // SubtreeSize(x): the number of nodes (and attributes) in the subtree of node x.\r
355 int XMLTree::SubtreeSize(treeNode x) \r
356  {\r
357     return subtree_size(Par, x);\r
358  }\r
359 \r
360 // SubtreeTags(x,tag): the number of occurrences of tag within the subtree of node x.\r
361 int XMLTree::SubtreeTags(treeNode x, TagType tag) \r
362  {\r
363     if (x == Root())\r
364       x = fast_first_child(Par,x);\r
365     \r
366 \r
367     int s = x + 2*subtree_size(Par, x) - 1;\r
368  \r
369     return Tags->rank(tag, s) - Tags->rank(tag, node2tagpos(x)-1);\r
370  }\r
371 int XMLTree::SubtreeElements(treeNode x) \r
372  {\r
373     \r
374     int size = subtree_size(Par,x);\r
375     if (x == Root()){\r
376       x = fast_first_child(Par,x);\r
377       size = size - 1;\r
378     };\r
379 \r
380     int s = x + 2*size - 1;\r
381     int ntext = Tags->rank(PCDATA_TAG_ID, s) - Tags->rank(PCDATA_TAG_ID, node2tagpos(x)-1);\r
382     size = size - ntext;\r
383     treeNode fin = fast_find_close(Par,x);\r
384     treeNode y = Tags->select_next(ATTRIBUTE_TAG_ID,node2tagpos(x));\r
385     while (y != NULLT && y < fin){\r
386       size -= SubtreeSize(y);\r
387       y = Tags->select_next(ATTRIBUTE_TAG_ID,node2tagpos(y));\r
388     };\r
389     return size;    \r
390  }\r
391 \r
392 // IsLeaf(x): returns whether node x is leaf or not. In the succinct representation\r
393 // this is just a bit inspection.\r
394 bool XMLTree::IsLeaf(treeNode x) \r
395  {\r
396    NULLT_IF(x==NULLT);\r
397    return fast_isleaf(Par, x);\r
398  } \r
399 \r
400 // IsAncestor(x,y): returns whether node x is ancestor of node y.\r
401 bool XMLTree::IsAncestor(treeNode x, treeNode y) \r
402  {\r
403     return fast_is_ancestor(Par, x, y);\r
404  }\r
405 \r
406 // IsChild(x,y): returns whether node x is parent of node y.\r
407 bool XMLTree::IsChild(treeNode x, treeNode y) \r
408  {\r
409     if (!fast_is_ancestor(Par, x, y)) return false;\r
410     return depth(Par, x) == (depth(Par, y) + 1);\r
411  }\r
412 \r
413 // IsFirstChild(x): returns whether node x is the first child of its parent.\r
414 bool XMLTree::IsFirstChild(treeNode x)\r
415  {\r
416     return ((x != NULLT)&&(x==Root() || prev_sibling(Par,x) == (treeNode)-1));\r
417  }\r
418 \r
419 \r
420 // NumChildren(x): number of children of node x. Constant time with the data structure\r
421 // of Sadakane.\r
422 int XMLTree::NumChildren(treeNode x) \r
423  {\r
424     return degree(Par, x);\r
425  }\r
426 \r
427 // ChildNumber(x): returns i if node x is the i-th children of its parent.\r
428 int XMLTree::ChildNumber(treeNode x) \r
429  {\r
430     return child_rank(Par, x);\r
431  }\r
432 \r
433 // Depth(x): depth of node x, a simple binary rank on the parentheses sequence.\r
434 int XMLTree::Depth(treeNode x) \r
435  {\r
436     return depth(Par, x);\r
437  }\r
438 \r
439 // Preorder(x): returns the preorder number of node x, just counting the tree\r
440 // nodes (i.e., tags, it disregards the texts in the tree).\r
441 int XMLTree::Preorder(treeNode x) \r
442  {\r
443     return preorder_rank(Par, x);\r
444  }\r
445 \r
446 // Postorder(x): returns the postorder number of node x, just counting the tree\r
447 // nodes (i.e., tags, it disregards the texts in the tree).\r
448 int XMLTree::Postorder(treeNode x) \r
449  {\r
450     return postorder_rank(Par, x);\r
451  }\r
452 /*\r
453 // Tag(x): returns the tag identifier of node x.\r
454 TagType XMLTree::Tag(treeNode x) \r
455  {\r
456     return fast_get_field(tags_fix,tags_blen,node2tagpos(x));\r
457  }\r
458 */\r
459 // DocIds(x): returns the range of text identifiers that descend from node x.\r
460 // returns {NULLT, NULLT} when there are no texts descending from x.\r
461 range XMLTree::DocIds(treeNode x) \r
462  {\r
463    range r;\r
464    if (x == NULLT) {\r
465      r.min = NULLT;\r
466      r.max = NULLT;\r
467      return r;\r
468    };\r
469    int min = EBVector->rank1(x-1);                          \r
470    int max = EBVector->rank1(x+2*subtree_size(Par, x)-2); \r
471    if (min==max) { // range is empty, no texts within the subtree of x\r
472      r.min = NULLT;\r
473      r.max = NULLT;\r
474    }\r
475    else { // the range is non-empty, there are texts within the subtree of x\r
476      r.min = min+1;\r
477      r.max = max;\r
478    }\r
479    return r;\r
480  }\r
481 \r
482 // Parent(x): returns the parent node of node x.\r
483 \r
484 treeNode XMLTree::Parent(treeNode x) \r
485  {\r
486     if (x == Root())\r
487       return NULLT;\r
488     else\r
489       return  parent(Par, x);;\r
490  }\r
491 \r
492 // Child(x,i): returns the i-th child of node x, assuming it exists.\r
493 treeNode XMLTree::Child(treeNode x, int i) \r
494 {\r
495     if (i <= OPTD) return naive_child(Par, x, i);\r
496     else return child(Par, x, i);\r
497 }\r
498 \r
499 // FirstChild(x): returns the first child of node x, assuming it exists. Very fast in BP.\r
500 \r
501 treeNode XMLTree::FirstChild(treeNode x) \r
502  {\r
503    NULLT_IF(x==NULLT);\r
504    return fast_first_child(Par, x);\r
505  }\r
506 \r
507 treeNode XMLTree::FirstElement(treeNode x) \r
508  {\r
509    NULLT_IF(x==NULLT);\r
510    x = fast_first_child(Par, x);\r
511    NULLT_IF(x == NULLT);\r
512    switch (Tag(x)){\r
513   \r
514    case PCDATA_TAG_ID:\r
515      x = x+2;\r
516      return (fast_inspect(Par,x)==OP)? x : NULLT;\r
517      \r
518    case ATTRIBUTE_TAG_ID:  \r
519      x = fast_next_sibling(Par,x);\r
520      if (x != NULLT && Tag(x) == PCDATA_TAG_ID){\r
521        x = x+2;\r
522        return (fast_inspect(Par,x)==OP)? x : NULLT;\r
523      } \r
524      else return x;     \r
525    default:\r
526      return x;\r
527    }\r
528  }\r
529 \r
530 treeNode XMLTree::NextElement(treeNode x) \r
531 {\r
532   NULLT_IF(x==NULLT);\r
533   x = fast_next_sibling(Par, x);\r
534   NULLT_IF(x == NULLT);   \r
535   if (Tag(x) == PCDATA_TAG_ID){\r
536     x = x+2;\r
537      return (fast_inspect(Par,x)==OP)? x : NULLT;\r
538   }\r
539   else return x;  \r
540 }\r
541 value XMLTree::CamlFirstElement(value x)\r
542 {\r
543   return Val_int(FirstElement(Int_val(x)));\r
544 }\r
545 value XMLTree::CamlNextElement(value x)\r
546 {\r
547   return Val_int(NextElement(Int_val(x)));\r
548 }\r
549 \r
550 extern "C" value caml_cpp_fast_first_element(value xmltree, value node){\r
551   return XMLTREE(xmltree)->CamlFirstElement(node);\r
552 }\r
553 \r
554 extern "C" value caml_cpp_fast_next_element(value xmltree, value node){\r
555   return XMLTREE(xmltree)->CamlNextElement(node);\r
556 }\r
557 \r
558 // LastChild(x): returns the last child of node x.\r
559 treeNode XMLTree::LastChild(treeNode x)\r
560  {\r
561    NULLT_IF(x == NULLT || fast_isleaf(Par,x));\r
562    return find_open(Par, fast_find_close(Par, x)-1);\r
563  }\r
564 \r
565 // NextSibling(x): returns the next sibling of node x, assuming it exists.\r
566 treeNode XMLTree::NextSibling(treeNode x) \r
567  {\r
568    NULLT_IF(x==NULLT || x == Root() );\r
569    x = fast_find_close(Par,x)+1;\r
570    return (fast_inspect(Par,x) == CP ? NULLT : x);\r
571  }\r
572 \r
573 \r
574 // PrevSibling(x): returns the previous sibling of node x, assuming it exists.\r
575 treeNode XMLTree::PrevSibling(treeNode x) \r
576  {\r
577    NULLT_IF(x==NULLT);\r
578    return prev_sibling(Par, x);\r
579  }\r
580 \r
581 // TaggedChild(x,tag): returns the first child of node x tagged tag, or NULLT if there is none.\r
582 // Because of the balanced-parentheses representation of the tree, this operation is not supported\r
583 // efficiently, just iterating among the children of node x until finding the desired child.\r
584 treeNode XMLTree::TaggedChild(treeNode x, TagType tag) \r
585  {\r
586    \r
587    NULLT_IF(x==NULLT || fast_isleaf(Par,x));\r
588    treeNode child;   \r
589    child = fast_first_child(Par, x); // starts at first child of node x\r
590    if (Tag(child) == tag)\r
591      return child;\r
592    else\r
593      return TaggedFollowingSibling(child,tag);\r
594  }\r
595 \r
596 // TaggedSibling(x,tag): returns the first sibling of node x tagged tag, or NULLT if there is none.\r
597 treeNode XMLTree::TaggedFollowingSibling(treeNode x, TagType tag)\r
598 {\r
599   NULLT_IF(x==NULLT);\r
600   treeNode sibling = fast_next_sibling(Par, x);\r
601   TagType ctag;\r
602   while (sibling != NULLT) {\r
603     ctag = Tag(sibling);\r
604     if (ctag == tag) // current sibling is labeled with tag of interest\r
605       return sibling; \r
606     sibling = fast_sibling(Par, sibling, ctag); // OK, let's try with the next sibling\r
607   }\r
608   return NULLT; // no such sibling was found   \r
609 }\r
610 \r
611 treeNode XMLTree::SelectChild(treeNode x, TagIdSet *tags)\r
612 {\r
613   \r
614   NULLT_IF(x==NULLT || fast_isleaf(Par,x));\r
615   int i;\r
616   treeNode child = fast_first_child(Par, x);  \r
617   TagType t;\r
618   while (child != NULLT) {\r
619     t = Tag(child);\r
620     if (tags->find(t) != tags->end()) return child;\r
621     child = fast_sibling(Par, child,t);\r
622   }\r
623   return NULLT;  \r
624 }\r
625 \r
626 \r
627 treeNode XMLTree::SelectFollowingSibling(treeNode x, TagIdSet *tags)\r
628 {\r
629 \r
630    NULLT_IF(x==NULLT);\r
631    int i;\r
632    TagType t;\r
633    treeNode sibling = fast_next_sibling(Par, x);\r
634    while (sibling != NULLT) {\r
635      t = Tag(sibling);\r
636      if (tags->find(t) != tags->end()) return sibling;\r
637      sibling = fast_sibling(Par, sibling,t);\r
638    }\r
639    return NULLT;    \r
640  }\r
641 \r
642 \r
643 // TaggedDescendant(x,tag): returns the first node tagged tag with larger preorder than x and within\r
644 // the subtree of x. Returns NULLT if there is none.\r
645 treeNode XMLTree::TaggedDescendant(treeNode x, TagType tag) \r
646  {\r
647    //NULLT_IF(x==NULLT || fast_isleaf(Par,x));\r
648 \r
649    int s = (int) Tags->select_next(tag,node2tagpos(x));\r
650    NULLT_IF (s == -1);\r
651 \r
652    treeNode y = tagpos2node(s); // transforms the tag position into a node position\r
653    \r
654    return (fast_is_ancestor(Par,x,y) ? y : NULLT);\r
655  }\r
656 \r
657 \r
658 treeNode XMLTree::SelectDescendant(treeNode x, TagIdSet *tags)\r
659  {\r
660    NULLT_IF (x ==NULLT || fast_isleaf(Par,x));\r
661    int i;\r
662    treeNode min = NULLT;\r
663    treeNode fc = fast_first_child(Par,x);\r
664    treeNode aux;\r
665    TagIdSet::const_iterator tagit;\r
666    for (tagit = tags->begin(); tagit != tags->end(); tagit++) {\r
667      aux = TaggedDescendant(x, (TagType) *tagit);\r
668      if (aux == fc) return fc;\r
669      if (aux == NULLT) continue;\r
670      if ((min == NULLT) || (aux < min)) min = aux;\r
671    };\r
672    return min;\r
673  }\r
674 \r
675 \r
676 \r
677 // TaggedPrec(x,tag): returns the first node tagged tag with smaller preorder than x and not an\r
678 // ancestor of x. Returns NULLT if there is none.\r
679 treeNode XMLTree::TaggedPreceding(treeNode x, TagType tag) \r
680  {    \r
681     int r, s;\r
682     treeNode node_s, root;\r
683     r = (int)Tags->rank(tag, node2tagpos(x)-1);\r
684     if (r==0) return NULLT; // there is no such node.\r
685     s = (int)Tags->select(tag, r);\r
686     root = root_node(Par);\r
687     node_s = tagpos2node(s);\r
688     while (fast_is_ancestor(Par, node_s, x) && (node_s!=root)) { // the one that we found is an ancestor of x\r
689        r--;\r
690        if (r==0) return NULLT; // there is no such node\r
691        s = (int)Tags->select(tag, r);  // we should use select_prev instead when provided\r
692        node_s = tagpos2node(s);\r
693     }\r
694     return NULLT; // there is no such node \r
695  }\r
696 \r
697 \r
698 // TaggedFoll(x,tag): returns the first node tagged tag with larger preorder than x and not in\r
699 // the subtree of x. Returns NULLT if there is none.\r
700 treeNode XMLTree::TaggedFollowing(treeNode x, TagType tag)\r
701  {\r
702    NULLT_IF (x ==NULLT || x == Root());   \r
703    return tagpos2node(Tags->select_next(tag,fast_find_close(Par, x)));\r
704 \r
705  } \r
706 \r
707 // TaggedFollBelow(x,tag,root): returns the first node tagged tag with larger preorder than x \r
708 // and not in the subtree of x. Returns NULLT if there is none.\r
709 treeNode XMLTree::TaggedFollowingBelow(treeNode x, TagType tag, treeNode ancestor)\r
710 {\r
711   // NULLT_IF (x == NULLT || x == Root() || x == ancestor); \r
712 \r
713   //Special optimisation, test for the following sibling first\r
714   treeNode close = fast_find_close(Par, x);\r
715   /*\r
716    treeNode ns = close+1;\r
717   if (fast_inspect(Par,ns) == OP) {\r
718     TagType tagns = Tag(ns);\r
719     //    cout << GetTagNameByRef(tagns) << endl;\r
720     //cout.flush();\r
721     if (tagns == PCDATA_TAG_ID){\r
722       close = ns+1;\r
723       ns = ns+2;\r
724       if (fast_inspect(Par,ns) != OP)\r
725         goto after;\r
726       tagns = Tag(ns);      \r
727     };\r
728     if (tagns == tag)\r
729       return ns;\r
730   };\r
731  after:\r
732   */\r
733   treeNode s = tagpos2node(Tags->select_next(tag, close));\r
734   \r
735   if (ancestor == Root() || s==NULLT || s < fast_find_close(Par,ancestor)) return s;\r
736   else return NULLT;\r
737\r
738 \r
739 treeNode XMLTree::TaggedFollowingBefore(treeNode x, TagType tag, treeNode closing)\r
740 {\r
741 \r
742   NULLT_IF (x == NULLT || x == Root());\r
743   \r
744   treeNode s = tagpos2node(Tags->select_next(tag, fast_find_close(Par, x)));  \r
745   NULLT_IF (s == NULLT || s >= closing);\r
746   \r
747   return s;\r
748\r
749 \r
750 /* Here we inline TaggedFoll to find the min globally, and only at the end\r
751    we check if the min is below the context node */\r
752 treeNode XMLTree::SelectFollowingBelow(treeNode x, TagIdSet *tags, treeNode ancestor)\r
753  {\r
754 \r
755    NULLT_IF(x==NULLT || x==Root());\r
756 \r
757    treeNode close = fast_find_close(Par,x);\r
758    treeNode ns = close+1;\r
759    if ( (fast_inspect(Par,ns) == OP) && (tags->find(Tag(ns)) != tags->end()))\r
760      return ns;\r
761 \r
762    int i;\r
763    treeNode min = NULLT;\r
764    treeNode aux;\r
765   \r
766 \r
767    TagIdSet::const_iterator tagit;\r
768    for (tagit = tags->begin(); tagit != tags->end(); tagit++) {\r
769 \r
770      aux = tagpos2node(Tags->select_next(*tagit, close));\r
771      \r
772      // The next sibling of x is guaranteed to be below ctx\r
773      // and is the node with lowest preorder which is after ctx.\r
774      // if we find it, we return early;         \r
775      if (aux == NULLT) continue;\r
776      if ((min == NULLT) || (aux < min)) min = aux;\r
777    };\r
778      \r
779    // found the smallest node in preorder which is after x.\r
780    // if ctx is the root node, just return what we found.\r
781 \r
782    if (ancestor == Root()) return min;\r
783    // else check whether if is in below the ctx node\r
784 \r
785    NULLT_IF (min == NULLT || min >= fast_find_close(Par, ancestor));\r
786    \r
787    return min;\r
788    \r
789  }\r
790 treeNode XMLTree::SelectFollowingBefore(treeNode x, TagIdSet *tags, treeNode closing)\r
791  {\r
792 \r
793    NULLT_IF(x==NULLT || x==Root());\r
794    int i;\r
795    treeNode min = NULLT;\r
796    treeNode ns = fast_next_sibling(Par, x);\r
797    treeNode close = ns - 1;\r
798    treeNode aux;\r
799    TagIdSet::const_iterator tagit;\r
800    for (tagit = tags->begin(); tagit != tags->end(); tagit++) {\r
801 \r
802      aux = tagpos2node(Tags->select_next(*tagit, close));\r
803      \r
804      // The next sibling of x is guaranteed to be below ctx\r
805      // and is the node with lowest preorder which is after ctx.\r
806      // if we find it, we return early;\r
807      \r
808      if (aux == ns ) return ns;\r
809      if (aux == NULLT) continue;\r
810      if ((min == NULLT) || (aux < min)) min = aux;\r
811    };\r
812      \r
813    // found the smallest node in preorder which is after x.\r
814    // if ctx is the root node, just return what we found.\r
815 \r
816    NULLT_IF (min == NULLT || min >= closing);\r
817    \r
818    return min;\r
819    \r
820  }\r
821 \r
822 \r
823 // TaggedAncestor(x, tag): returns the closest ancestor of x tagged tag. Return\r
824 // NULLT is there is none.\r
825 treeNode XMLTree::TaggedAncestor(treeNode x, TagType tag)\r
826  {    \r
827     if (x == NULLT || x == Root())\r
828        return NULLT;\r
829     \r
830     treeNode s = parent(Par, x), r = Root();\r
831     while (s != r) {\r
832       if (Tag(s) == tag) return s;\r
833        s = parent(Par, s);\r
834     }\r
835     return NULLT;\r
836  }\r
837 \r
838 \r
839 \r
840 // MyText(x): returns the document identifier of the text below node x, \r
841 // or NULLT if x is not a leaf node or the text is empty. Assumes Doc \r
842 // ids start from 0.\r
843 DocID XMLTree::MyText(treeNode x) \r
844 {\r
845   TagType tag = Tag(x);\r
846   // seems faster than testing EBVector->access(x);\r
847 \r
848   if (tag == PCDATA_TAG_ID || tag == ATTRIBUTE_DATA_TAG_ID)\r
849     //if (EBVector->access(x))\r
850     return (DocID) (EBVector->rank1(x)-1); //-1 because document ids start from 0\r
851   else \r
852     return (DocID) NULLT;\r
853   \r
854 }\r
855 // MyText(x): returns the document identifier of the text below node x, \r
856 // or NULLT if x is not a leaf node or the text is empty. Assumes Doc \r
857 // ids start from 0.\r
858 DocID XMLTree::MyTextUnsafe(treeNode x) \r
859 {\r
860   return (DocID) (EBVector->rank1(x)-1); //-1 because document ids start from 0\r
861 }\r
862 // TextXMLId(d): returns the preorder of document with identifier d in the tree consisting of\r
863 // all tree nodes and all text nodes. Assumes that the tree root has preorder 1.\r
864 int XMLTree::TextXMLId(DocID d) \r
865  {\r
866    NULLT_IF(d == NULLT);\r
867      int s = EBVector->select1(d+1);\r
868    return rank_open(Par, s) + d + 1; // +1 because root has preorder 1\r
869    \r
870  }\r
871 \r
872 // NodeXMLId(x): returns the preorder of node x in the tree consisting \r
873 // of all tree nodes and all text nodes. Assumes that the tree root has\r
874 // preorder 0;\r
875 int XMLTree::NodeXMLId(treeNode x) \r
876  {\r
877    NULLT_IF(x == NULLT);\r
878    if (x == Root()) return 1; // root node has preorder 1\r
879    return rank_open(Par, x) + EBVector->rank1(x-1);\r
880  }\r
881 \r
882 // ParentNode(d): returns the parent node of document identifier d.\r
883 treeNode XMLTree::ParentNode(DocID d) \r
884  {    \r
885    NULLT_IF (d == NULLT);   \r
886    return (treeNode) EBVector->select1(d+1);     \r
887  }\r
888 \r
889 // GetTagId: returns the tag identifier corresponding to a given tag name.\r
890 // Returns NULLT in case that the tag name does not exists.\r
891 TagType XMLTree::GetTagId(unsigned char *tagname)\r
892  {\r
893   \r
894    string s = (char *) tagname;\r
895    TagIdMapIT it = tIdMap->find(s);    \r
896    return (TagType) ((it != tIdMap->end()) ? it->second : -1);\r
897     \r
898  }\r
899 \r
900 \r
901 // GetTagName(tagid): returns the tag name of a given tag identifier.\r
902 // Returns NULL in case that the tag identifier is not valid.\r
903 unsigned char *XMLTree::GetTagName(TagType tagid)\r
904  {\r
905     unsigned char *s;\r
906     if ( tagid < 0 || tagid >= TagName->size())\r
907       return (unsigned char *) "<INVALID TAG>";\r
908     strcpy((char *)s, (*TagName)[tagid].c_str());\r
909     \r
910     return (s == NULL ? (unsigned char*) "<INVALID TAG>" : s);\r
911  }\r
912 \r
913 \r
914 const unsigned char *XMLTree::GetTagNameByRef(TagType tagid)\r
915  {\r
916 \r
917    unsigned char *s;\r
918    if ( tagid < 0 || tagid >= TagName->size())\r
919      return (unsigned char *) "<INVALID TAG>";\r
920    \r
921    return (const unsigned char *) (*TagName)[tagid].c_str();\r
922    \r
923  }\r
924 \r
925 \r
926 \r
927 TagType XMLTree::RegisterTag(unsigned char *tagname)\r
928  {  \r
929     TagType id = XMLTree::GetTagId(tagname);\r
930     if (id == NULLT) {\r
931       string s = (char *) tagname;      \r
932       REGISTER_TAG(TagName,tIdMap,s);      \r
933     };\r
934     \r
935     return id;\r
936  }\r
937 \r
938 \r
939 treeNode XMLTree::Closing(treeNode x) {\r
940   return fast_find_close(Par,x); \r
941 }\r
942 bool XMLTree::IsOpen(treeNode x) { return fast_inspect(Par,x); }\r
943 \r
944 //WARNING this uses directly the underlying implementation for plain text\r
945 \r
946 \r
947 void XMLTree::Print(int fd,treeNode x, bool no_text){\r
948   \r
949   int newfd = dup(fd);\r
950   stream = fdopen(newfd,"wa");\r
951   if (stream == 0){\r
952     perror(NULL);\r
953     return;\r
954   };\r
955 \r
956   if (buffer == 0)\r
957     buffer = new string();\r
958 \r
959   FILE* fp = stream;\r
960   treeNode fin = fast_find_close(Par,x);\r
961   treeNode n = x;\r
962   TagType tag = Tag(n);\r
963   uchar * tagstr;\r
964   range r = DocIds(x);\r
965   treeNode first_idx;\r
966   treeNode first_text = (tag == PCDATA_TAG_ID ?  x : ParentNode(r.min-1));\r
967   treeNode first_att =  NULLT;\r
968   \r
969   if (first_att  == NULLT)\r
970   first_idx = first_text;\r
971   else if (first_text == NULLT)\r
972   first_idx = first_att;\r
973   else\r
974    first_idx = min(first_att,first_text);\r
975    \r
976    uchar * current_text=NULL;\r
977    if (first_idx != NULLT)\r
978    current_text = GetText(MyText(first_idx));\r
979    size_t read = 0;\r
980    std::vector<uchar*> st;\r
981  while (n <= fin){\r
982    if (fast_inspect(Par,n)){\r
983      if (tag == PCDATA_TAG_ID  ) {       \r
984 \r
985        if (no_text)\r
986          myfputs("<$/>",fp);\r
987        else{\r
988          read = myfprintf((const char*) current_text, fp);\r
989          current_text += (read + 1);\r
990        };\r
991        n+=2; // skip closing $\r
992        tag = Tag(n);\r
993       \r
994      }\r
995      else {\r
996        myfputc('<',fp);\r
997        tagstr = (uchar*) GetTagNameByRef(tag);\r
998        myfputs((const char*) tagstr ,fp);\r
999        n++;\r
1000        if (fast_inspect(Par,n)) {\r
1001          st.push_back(tagstr);\r
1002          tag = Tag(n);\r
1003          if (tag == ATTRIBUTE_TAG_ID){\r
1004            n++;\r
1005            if (no_text) myfputs("><@@>",fp);\r
1006            while (fast_inspect(Par,n)){\r
1007              if (no_text) {\r
1008                myfputc('<',fp);\r
1009                myfputs((const char*) &(GetTagNameByRef(Tag(n))[3]),fp);\r
1010                myfputc('>',fp);\r
1011                myfputs("<$@/></",fp);\r
1012                myfputs((const char*) &(GetTagNameByRef(Tag(n))[3]),fp);\r
1013                myfputc('>',fp);\r
1014                n+= 4;\r
1015              }\r
1016              else {\r
1017                myfputc(' ',fp);\r
1018                myfputs((const char*) &(GetTagNameByRef(Tag(n))[3]),fp);\r
1019                n++;\r
1020                myfputs("=\"",fp);\r
1021                read = myfprintf((const char*) current_text,fp);\r
1022                current_text += (read + 1);\r
1023                myfputc('"',fp);\r
1024                n+=3;\r
1025              }\r
1026            };\r
1027            if (no_text) \r
1028              myfputs("</@@>",fp);\r
1029            else myfputc('>',fp);\r
1030            n++;\r
1031            tag=Tag(n);\r
1032          }\r
1033          else {\r
1034            myfputc('>',fp);\r
1035          };\r
1036        }\r
1037        else {// <foo /> tag\r
1038          myfputs("/>",fp);\r
1039          n++;\r
1040          tag=Tag(n);     \r
1041        };     \r
1042      };\r
1043    }\r
1044    else\r
1045      do {\r
1046        myfputs("</",fp);\r
1047        myfputs((const char*)st.back(),fp);\r
1048        myfputc('>', fp);\r
1049        st.pop_back();\r
1050        n++;\r
1051      }while (!fast_inspect(Par,n) && !st.empty());\r
1052    tag=Tag(n);\r
1053  };\r
1054  myfputc('\n',fp);\r
1055  mybufferflush(fp);\r
1056  //fflush(fp);\r
1057  fclose(fp);\r
1058 }\r