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