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