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