Rewrite printing function to make it faster. Now also handles
[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 /*\r
319 int XMLTree::SubtreeTags(treeNode x, TagType tag) \r
320  {\r
321     if (x == Root())\r
322       x = fast_first_child(Par,x);\r
323     \r
324 \r
325     int s = x + 2*subtree_size(Par, x) - 1;\r
326  \r
327     return (Tags->rank(tag, s) - Tags->rank(tag, node2tagpos(x)-1))+1;\r
328  }\r
329 */\r
330 int XMLTree::SubtreeElements(treeNode x) \r
331  {\r
332     \r
333     int size = subtree_size(Par,x);\r
334     if (x == Root()){\r
335       x = fast_first_child(Par,x);\r
336       size = size - 1;\r
337     };\r
338 \r
339     int s = x + 2*size - 1;\r
340     int ntext = Tags->rank(PCDATA_TAG_ID, s) - Tags->rank(PCDATA_TAG_ID, node2tagpos(x)-1);\r
341     size = size - ntext;\r
342     treeNode fin = fast_find_close(Par,x);\r
343     treeNode y = Tags->select_next(ATTRIBUTE_TAG_ID,node2tagpos(x));\r
344     while (y != NULLT && y < fin){\r
345       size -= SubtreeSize(y);\r
346       y = Tags->select_next(ATTRIBUTE_TAG_ID,node2tagpos(y));\r
347     };\r
348     return size;    \r
349  }\r
350 \r
351 // IsLeaf(x): returns whether node x is leaf or not. In the succinct representation\r
352 // this is just a bit inspection.\r
353 bool XMLTree::IsLeaf(treeNode x) \r
354  {\r
355    NULLT_IF(x==NULLT);\r
356    return fast_isleaf(Par, x);\r
357  } \r
358 \r
359 // IsAncestor(x,y): returns whether node x is ancestor of node y.\r
360 bool XMLTree::IsAncestor(treeNode x, treeNode y) \r
361  {\r
362     return fast_is_ancestor(Par, x, y);\r
363  }\r
364 \r
365 // IsChild(x,y): returns whether node x is parent of node y.\r
366 bool XMLTree::IsChild(treeNode x, treeNode y) \r
367  {\r
368     if (!fast_is_ancestor(Par, x, y)) return false;\r
369     return depth(Par, x) == (depth(Par, y) + 1);\r
370  }\r
371 \r
372 // IsFirstChild(x): returns whether node x is the first child of its parent.\r
373 /*bool XMLTree::IsFirstChild(treeNode x)\r
374  {\r
375     return ((x != NULLT)&&(x==Root() || prev_sibling(Par,x) == (treeNode)-1));\r
376  }\r
377 */\r
378 \r
379 // NumChildren(x): number of children of node x. Constant time with the data structure\r
380 // of Sadakane.\r
381 int XMLTree::NumChildren(treeNode x) \r
382  {\r
383     return degree(Par, x);\r
384  }\r
385 \r
386 // ChildNumber(x): returns i if node x is the i-th children of its parent.\r
387 int XMLTree::ChildNumber(treeNode x) \r
388  {\r
389     return child_rank(Par, x);\r
390  }\r
391 \r
392 // Depth(x): depth of node x, a simple binary rank on the parentheses sequence.\r
393 int XMLTree::Depth(treeNode x) \r
394  {\r
395     return depth(Par, x);\r
396  }\r
397 \r
398 // Preorder(x): returns the preorder number of node x, just counting the tree\r
399 // nodes (i.e., tags, it disregards the texts in the tree).\r
400 int XMLTree::Preorder(treeNode x) \r
401  {\r
402     return preorder_rank(Par, x);\r
403  }\r
404 \r
405 // Postorder(x): returns the postorder number of node x, just counting the tree\r
406 // nodes (i.e., tags, it disregards the texts in the tree).\r
407 int XMLTree::Postorder(treeNode x) \r
408  {\r
409     return postorder_rank(Par, x);\r
410  }\r
411 /*\r
412 // Tag(x): returns the tag identifier of node x.\r
413 TagType XMLTree::Tag(treeNode x) \r
414  {\r
415     return fast_get_field(tags_fix,tags_blen,node2tagpos(x));\r
416  }\r
417 */\r
418 // DocIds(x): returns the range of text identifiers that descend from node x.\r
419 // returns {NULLT, NULLT} when there are no texts descending from x.\r
420 range XMLTree::DocIds(treeNode x) \r
421  {\r
422    range r;\r
423    if (x == NULLT) {\r
424      r.min = NULLT;\r
425      r.max = NULLT;\r
426      return r;\r
427    };\r
428    int min = EBVector->rank1(x-1);                          \r
429    int max = EBVector->rank1(x+2*subtree_size(Par, x)-2); \r
430    if (min==max) { // range is empty, no texts within the subtree of x\r
431      r.min = NULLT;\r
432      r.max = NULLT;\r
433    }\r
434    else { // the range is non-empty, there are texts within the subtree of x\r
435      r.min = min+1;\r
436      r.max = max;\r
437    }\r
438    return r;\r
439  }\r
440 \r
441 // Parent(x): returns the parent node of node x.\r
442 /*\r
443 treeNode XMLTree::Parent(treeNode x) \r
444  {\r
445     if (x == Root())\r
446       return NULLT;\r
447     else\r
448       return  parent(Par, x);;\r
449  }*/\r
450 \r
451 // Child(x,i): returns the i-th child of node x, assuming it exists.\r
452 treeNode XMLTree::Child(treeNode x, int i) \r
453 {\r
454     if (i <= OPTD) return naive_child(Par, x, i);\r
455     else return child(Par, x, i);\r
456 }\r
457 \r
458 // FirstChild(x): returns the first child of node x, assuming it exists. Very fast in BP.\r
459 /*\r
460 treeNode XMLTree::FirstChild(treeNode x) \r
461  {\r
462    NULLT_IF(x==NULLT);\r
463    return fast_first_child(Par, x);\r
464  }\r
465 */\r
466 /*\r
467 treeNode XMLTree::FirstElement(treeNode x) \r
468  {\r
469    NULLT_IF(x==NULLT);\r
470    x = fast_first_child(Par, x);\r
471    NULLT_IF(x == NULLT);\r
472    switch (Tag(x)){\r
473   \r
474    case PCDATA_TAG_ID:\r
475      x = x+2;\r
476      return (fast_inspect(Par,x)==OP)? x : NULLT;\r
477      \r
478    case ATTRIBUTE_TAG_ID:  \r
479      x = fast_next_sibling(Par,x);\r
480      if (x != NULLT && Tag(x) == PCDATA_TAG_ID){\r
481        x = x+2;\r
482        return (fast_inspect(Par,x)==OP)? x : NULLT;\r
483      } \r
484      else return x;     \r
485    default:\r
486      return x;\r
487    }\r
488  }\r
489 *//*\r
490 treeNode XMLTree::NextElement(treeNode x) \r
491 {\r
492   NULLT_IF(x==NULLT);\r
493   x = fast_next_sibling(Par, x);\r
494   NULLT_IF(x == NULLT);   \r
495   if (Tag(x) == PCDATA_TAG_ID){\r
496     x = x+2;\r
497      return (fast_inspect(Par,x)==OP)? x : NULLT;\r
498   }\r
499   else return x;  \r
500   }*/\r
501 \r
502 // LastChild(x): returns the last child of node x.\r
503 treeNode XMLTree::LastChild(treeNode x)\r
504  {\r
505    NULLT_IF(x == NULLT || fast_isleaf(Par,x));\r
506    return find_open(Par, fast_find_close(Par, x)-1);\r
507  }\r
508 \r
509 // NextSibling(x): returns the next sibling of node x, assuming it exists.\r
510 /*treeNode XMLTree::NextSibling(treeNode x) \r
511  {\r
512    NULLT_IF(x==NULLT || x == Root() );\r
513    x = fast_find_close(Par,x)+1;\r
514    return (fast_inspect(Par,x) == CP ? NULLT : x);\r
515  }\r
516 */\r
517 \r
518 // PrevSibling(x): returns the previous sibling of node x, assuming it exists.\r
519 treeNode XMLTree::PrevSibling(treeNode x) \r
520  {\r
521    NULLT_IF(x==NULLT);\r
522    return prev_sibling(Par, x);\r
523  }\r
524 \r
525 // TaggedChild(x,tag): returns the first child of node x tagged tag, or NULLT if there is none.\r
526 // Because of the balanced-parentheses representation of the tree, this operation is not supported\r
527 // efficiently, just iterating among the children of node x until finding the desired child.\r
528 /*\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      if (aux == NULLT) continue;\r
702      if ((min == NULLT) || (aux < min)) min = aux;\r
703    };\r
704      \r
705    // found the smallest node in preorder which is after x.\r
706    // if ctx is the root node, just return what we found.\r
707 \r
708    if (ancestor == Root() || min == NULLT || min < fast_find_close(Par, ancestor)) return min;\r
709    else return NULLT;\r
710    \r
711  }\r
712 /*\r
713 treeNode XMLTree::SelectFollowingBefore(treeNode x, TagIdSet *tags, treeNode ancestor_closing)\r
714  {\r
715 \r
716    NULLT_IF(x==NULLT || x==Root());\r
717 \r
718    treeNode close = fast_find_close(Par,x);\r
719    treeNode ns = close+1;\r
720    if ( (fast_inspect(Par,ns) == OP) && (tags->find(Tag(ns)) != tags->end()))\r
721      return ns;\r
722 \r
723    int i;\r
724    treeNode min = NULLT;\r
725    treeNode aux;\r
726   \r
727 \r
728    TagIdSet::const_iterator tagit;\r
729    for (tagit = tags->begin(); tagit != tags->end(); ++tagit) {\r
730 \r
731      aux = tagpos2node(Tags->select_next(*tagit, close));\r
732      if (aux == NULLT) continue;\r
733      if ((min == NULLT) || (aux < min)) min = aux;\r
734    };\r
735      \r
736    // found the smallest node in preorder which is after x.\r
737    // if ctx is the root node, just return what we found.\r
738 \r
739    if (ancestor_closing == Root() || min == NULLT || min < ancestor_closing) return min;\r
740    else return NULLT;\r
741    \r
742  }\r
743 */\r
744 /*\r
745 treeNode XMLTree::SelectFollowingBefore(treeNode x, TagIdSet *tags, treeNode closing)\r
746  {\r
747 \r
748    NULLT_IF(x==NULLT || x==Root());\r
749    int i;\r
750    treeNode min = NULLT;\r
751    treeNode ns = fast_next_sibling(Par, x);\r
752    treeNode close = ns - 1;\r
753    treeNode aux;\r
754    TagIdSet::const_iterator tagit;\r
755    for (tagit = tags->begin(); tagit != tags->end(); tagit++) {\r
756 \r
757      aux = tagpos2node(Tags->select_next(*tagit, close));\r
758      \r
759      // The next sibling of x is guaranteed to be below ctx\r
760      // and is the node with lowest preorder which is after ctx.\r
761      // if we find it, we return early;\r
762      \r
763      if (aux == ns ) return ns;\r
764      if (aux == NULLT) continue;\r
765      if ((min == NULLT) || (aux < min)) min = aux;\r
766    };\r
767      \r
768    // found the smallest node in preorder which is after x.\r
769    // if ctx is the root node, just return what we found.\r
770 \r
771    NULLT_IF (min == NULLT || min >= closing);\r
772    \r
773    return min;\r
774    \r
775  }\r
776 */\r
777 \r
778 // TaggedAncestor(x, tag): returns the closest ancestor of x tagged tag. Return\r
779 // NULLT is there is none.\r
780 treeNode XMLTree::TaggedAncestor(treeNode x, TagType tag)\r
781  {    \r
782     if (x == NULLT || x == Root())\r
783        return NULLT;\r
784     \r
785     treeNode s = parent(Par, x), r = Root();\r
786     while (s != r) {\r
787       if (Tag(s) == tag) return s;\r
788        s = parent(Par, s);\r
789     }\r
790     return NULLT;\r
791  }\r
792 \r
793 \r
794 \r
795 // MyText(x): returns the document identifier of the text below node x, \r
796 // or NULLT if x is not a leaf node or the text is empty. Assumes Doc \r
797 // ids start from 0.\r
798 DocID XMLTree::MyText(treeNode x) \r
799 {\r
800   TagType tag = Tag(x);\r
801   // seems faster than testing EBVector->access(x);\r
802 \r
803   if (tag == PCDATA_TAG_ID || tag == ATTRIBUTE_DATA_TAG_ID)\r
804     //if (EBVector->access(x))\r
805     return (DocID) (EBVector->rank1(x)-1); //-1 because document ids start from 0\r
806   else \r
807     return (DocID) NULLT;\r
808   \r
809 }\r
810 // MyText(x): returns the document identifier of the text below node x, \r
811 // or NULLT if x is not a leaf node or the text is empty. Assumes Doc \r
812 // ids start from 0.\r
813 DocID XMLTree::MyTextUnsafe(treeNode x) \r
814 {\r
815   return (DocID) (EBVector->rank1(x)-1); //-1 because document ids start from 0\r
816 }\r
817 // TextXMLId(d): returns the preorder of document with identifier d in the tree consisting of\r
818 // all tree nodes and all text nodes. Assumes that the tree root has preorder 1.\r
819 int XMLTree::TextXMLId(DocID d) \r
820  {\r
821    NULLT_IF(d == NULLT);\r
822      int s = EBVector->select1(d+1);\r
823    return rank_open(Par, s) + d + 1; // +1 because root has preorder 1\r
824    \r
825  }\r
826 \r
827 // NodeXMLId(x): returns the preorder of node x in the tree consisting \r
828 // of all tree nodes and all text nodes. Assumes that the tree root has\r
829 // preorder 0;\r
830 int XMLTree::NodeXMLId(treeNode x) \r
831  {\r
832    NULLT_IF(x == NULLT);\r
833    if (x == Root()) return 1; // root node has preorder 1\r
834    return rank_open(Par, x) + EBVector->rank1(x-1);\r
835  }\r
836 \r
837 // ParentNode(d): returns the parent node of document identifier d.\r
838 treeNode XMLTree::ParentNode(DocID d) \r
839  {    \r
840    NULLT_IF (d == NULLT);   \r
841    return (treeNode) EBVector->select1(d+1);     \r
842  }\r
843 \r
844 // GetTagId: returns the tag identifier corresponding to a given tag name.\r
845 // Returns NULLT in case that the tag name does not exists.\r
846 TagType XMLTree::GetTagId(unsigned char *tagname)\r
847  {\r
848   \r
849    string s = (char *) tagname;\r
850    TagIdMapIT it = tIdMap->find(s);    \r
851    return (TagType) ((it != tIdMap->end()) ? it->second : -1);\r
852     \r
853  }\r
854 \r
855 \r
856 // GetTagName(tagid): returns the tag name of a given tag identifier.\r
857 // Returns NULL in case that the tag identifier is not valid.\r
858 unsigned char *XMLTree::GetTagName(TagType tagid)\r
859  {\r
860     unsigned char *s;\r
861     if ( tagid < 0 || tagid >= TagName->size())\r
862       return (unsigned char *) "<INVALID TAG>";\r
863     strcpy((char *)s, (*TagName)[tagid].c_str());\r
864     \r
865     return (s == NULL ? (unsigned char*) "<INVALID TAG>" : s);\r
866  }\r
867 \r
868 \r
869 const unsigned char *XMLTree::GetTagNameByRef(TagType tagid)\r
870  {\r
871 \r
872    unsigned char *s;\r
873    if ( tagid < 0 || tagid >= TagName->size())\r
874      return (unsigned char *) "<INVALID TAG>";\r
875    \r
876    return (const unsigned char *) (*TagName)[tagid].c_str();\r
877    \r
878  }\r
879 \r
880 \r
881 \r
882 TagType XMLTree::RegisterTag(unsigned char *tagname)\r
883  {  \r
884     TagType id = XMLTree::GetTagId(tagname);\r
885     if (id == NULLT) {\r
886       string s = (char *) tagname;      \r
887       REGISTER_TAG(TagName,tIdMap,s);      \r
888     };\r
889     \r
890     return id;\r
891  }\r
892 \r
893 \r
894 treeNode XMLTree::Closing(treeNode x) {\r
895   return fast_find_close(Par,x); \r
896 }\r
897 bool XMLTree::IsOpen(treeNode x) { return fast_inspect(Par,x); }\r
898 \r
899 //WARNING this uses directly the underlying implementation for plain text\r
900 \r
901 \r
902 void XMLTree::Print(int fd,treeNode x, bool no_text){\r
903   \r
904   if (buffer == 0)\r
905     buffer = new string();\r
906   \r
907   treeNode fin = fast_find_close(Par,x);\r
908   treeNode n = x;\r
909   TagType tag = Tag(n);\r
910   uchar * tagstr;\r
911   range r = DocIds(x);\r
912   treeNode first_idx;\r
913   treeNode first_text = (tag == PCDATA_TAG_ID ?  x : ParentNode(r.min-1));\r
914   treeNode first_att =  NULLT;\r
915   \r
916   if (first_att  == NULLT)\r
917     first_idx = first_text;\r
918   else if (first_text == NULLT)\r
919     first_idx = first_att;\r
920   else\r
921     first_idx = min(first_att,first_text);\r
922    \r
923    uchar * current_text=NULL;\r
924 \r
925    if (first_idx != NULLT)\r
926      current_text = GetText(MyText(first_idx));\r
927   \r
928    size_t read = 0;\r
929    std::vector<uchar*> st;\r
930    while (n <= fin){\r
931      if (fast_inspect(Par,n)){\r
932        if (tag == PCDATA_TAG_ID) {       \r
933          \r
934          if (no_text)\r
935            _dputs("<$/>", fd);\r
936          else {\r
937            read = _dprintf((const char*) current_text, fd);\r
938            current_text += (read + 1);\r
939          };\r
940          n+=2; // skip closing $\r
941          tag = Tag(n);\r
942          \r
943        }\r
944        else {\r
945          _dputc('<',fd);\r
946          tagstr = (uchar*) GetTagNameByRef(tag);\r
947          _dputs((const char*) tagstr, fd);\r
948          n++;\r
949          if (fast_inspect(Par,n)) {\r
950            st.push_back(tagstr);\r
951            tag = Tag(n);\r
952            if (tag == ATTRIBUTE_TAG_ID){\r
953              n++;\r
954              if (no_text) _dputs("><@@>",fd);\r
955 \r
956              while (fast_inspect(Par,n)){\r
957                if (no_text) {\r
958                  _dputc('<', fd);\r
959                  _dputs((const char*) &(GetTagNameByRef(Tag(n))[3]), fd);\r
960                  _dputc('>', fd);\r
961                  _dputs("<$@/></", fd);\r
962                  _dputs((const char*) &(GetTagNameByRef(Tag(n))[3]), fd);\r
963                  _dputc('>', fd);\r
964                  n+= 4;\r
965                } else {\r
966                  _dputc(' ', fd);\r
967                  _dputs((const char*) &(GetTagNameByRef(Tag(n))[3]), fd);\r
968                  n++;\r
969                  _dputs("=\"", fd);\r
970                  read = _dprintf((const char*) current_text, fd);\r
971                  current_text += (read + 1);\r
972                  _dputc('"', fd);\r
973                  n+=3;\r
974                }\r
975              };\r
976              if (no_text) _dputs("</@@>", fd);\r
977              else _dputc('>', fd);\r
978              n++;\r
979              tag=Tag(n);\r
980            \r
981            } else \r
982              _dputc('>', fd);\r
983            \r
984          } else {// <foo /> tag\r
985            _dputs("/>", fd);\r
986            n++;\r
987            tag=Tag(n);   \r
988          };     \r
989        };\r
990      }\r
991      else\r
992        do {\r
993          _dputs("</", fd);\r
994          _dputs((const char*)st.back(), fd);\r
995          _dputc('>', fd);\r
996          st.pop_back();\r
997          n++;\r
998        } while (!(fast_inspect(Par,n) || st.empty()));\r
999      tag = Tag(n);\r
1000    };\r
1001    _dputc('\n', fd);\r
1002    _flush(fd);\r
1003 }\r