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