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