Update code according to libbp renaming.
[SXSI/XMLTree.git] / XMLTree.cpp
1 #include "common.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 bp_inspect(Par,x)==OP ? x : NULLT;\r
34   } else return bp_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                   TextCollectionBuilder::index_type_t _index_type )\r
71  {\r
72    buffer = 0;\r
73    print_stack = 0;\r
74    // creates the data structure for the tree topology\r
75    STARTTIMER();\r
76    Par = bp_construct(npar, (pb*) par, OPT_FAST_PREORDER_SELECT | OPT_DEGREE|0);\r
77    STOPTIMER(Building);\r
78    PRINTTIME("Building parenthesis struct", Building);\r
79    STARTTIMER();\r
80 \r
81 \r
82     // creates structure for tags\r
83 \r
84     TagName = (vector<string>*)TN;\r
85     tIdMap = (TagIdMap *) tim;\r
86 \r
87     uint max_tag = TN->size() - 1;\r
88 \r
89 \r
90     static_bitsequence_builder *bmb = new static_bitsequence_builder_sdarray();\r
91     alphabet_mapper *am = new alphabet_mapper_none();\r
92     Tags = new static_sequence_bs((uint*)tags,npar,am,bmb);\r
93 \r
94     //cout << "Tags test: " << Tags->test((uint*)tags,npar) << endl;\r
95 \r
96     //Ensures that for small tag numbers, we are on an 8bit boundary.\r
97     //Makes tag access way faster with negligeable waste of space.\r
98     tags_blen = bits8(max_tag);\r
99     std::cerr << "Tags blen is " << tags_blen << "\n";\r
100     tags_len = (uint)npar;\r
101     tags_fix = new uint[uint_len(tags_blen,tags_len)];\r
102     for(uint i=0;i<(uint)npar;i++)\r
103        set_field(tags_fix,tags_blen,i,tags[i]);\r
104     delete bmb;\r
105     free(tags);\r
106     tags = NULL;\r
107 \r
108     STOPTIMER(Building);\r
109     PRINTTIME("Building Tag Structure", Building);\r
110 \r
111     Text = (TextCollection*) TC;\r
112 \r
113 \r
114     EBVector = new static_bitsequence_rrr02(empty_texts_bmp,npar,32);\r
115     //EBVector = new static_bitsequence_sdarray(empty_texts_bmp,npar);\r
116     free(empty_texts_bmp);\r
117     empty_texts_bmp = NULL;\r
118 \r
119 \r
120     disable_tc = dis_tc;\r
121     text_index_type = _index_type;\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     bp_delete(Par);\r
133     Par = NULL;\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 \r
150  }\r
151 \r
152 \r
153 void XMLTree::print_stats()\r
154  {\r
155     uint total_space = Tags->size()+sizeof(static_sequence*);\r
156     total_space += sizeof(uint*)+sizeof(uint)*(2+uint_len(tags_blen,tags_len));\r
157     cout << "Space usage for XMLTree:" << endl\r
158          << " - tags static_sequence: " << Tags->size()+sizeof(static_sequence*) << endl\r
159          << " - tags access array:    " << sizeof(uint*)+sizeof(uint)*(2+uint_len(tags_blen,tags_len)) << endl\r
160          << " ... add Diego structures ... " << endl\r
161          << " *total* " << total_space << endl;\r
162  }\r
163 \r
164 // Save: saves XML tree data structure to file.\r
165 void XMLTree::Save(int fd, char * name)\r
166  {\r
167     FILE *fp;\r
168     int i;\r
169 \r
170     fp = fdopen(fd, "wa");\r
171     // first stores the tree topology\r
172     saveTree(Par, fp);\r
173 \r
174     // stores the table with tag names\r
175     int ntags = TagName->size();\r
176 \r
177     ufwrite(&ntags, sizeof(int), 1, fp);\r
178     for (i = 0; i<ntags;i++)\r
179       fprintf(fp, "%s\n",TagName->at(i).c_str());\r
180 \r
181 \r
182     // stores the tags\r
183     Tags->save(fp);\r
184     ufwrite(&tags_blen,sizeof(uint),1,fp);\r
185     ufwrite(&tags_len,sizeof(uint),1,fp);\r
186     ufwrite(tags_fix,sizeof(uint),uint_len(tags_blen,tags_len),fp);\r
187 \r
188     // flags\r
189     ufwrite(&disable_tc, sizeof(bool),1,fp);\r
190 \r
191     //text positions\r
192     EBVector->save(fp);\r
193 \r
194     // stores the texts\r
195     if (!disable_tc) {\r
196 \r
197       ufwrite(&text_index_type, sizeof(TextCollectionBuilder::index_type_t), 1, fp);\r
198 \r
199 \r
200       string file(name);\r
201       switch (text_index_type){\r
202       case TextCollectionBuilder::index_type_default:\r
203         file.append(".default");\r
204         break;\r
205       case TextCollectionBuilder::index_type_swcsa:\r
206         file.append(".swcsa");\r
207         break;\r
208       case TextCollectionBuilder::index_type_rlcsa:\r
209         file.append(".rlcsa");\r
210         break;\r
211       };\r
212 \r
213       Text->Save(fp, file.c_str());\r
214 \r
215 \r
216     }\r
217  }\r
218 \r
219 // Load: loads XML tree data structure from file. Returns\r
220 // a pointer to the loaded data structure\r
221 XMLTree *XMLTree::Load(int fd, bool load_tc,int sample_factor, char * name)\r
222  {\r
223 \r
224     FILE *fp;\r
225     char buffer[1024];\r
226     XMLTree *XML_Tree;\r
227     int i;\r
228 \r
229     buffer[1023] = '\0';\r
230 \r
231     fp = fdopen(fd, "r");\r
232 \r
233     XML_Tree = new XMLTree();\r
234     STARTTIMER();\r
235     // Load the tree structure\r
236     XML_Tree->Par = loadTree(fp);\r
237     STOPTIMER(Loading);\r
238     PRINTTIME("Loading parenthesis struct", Loading);\r
239     STARTTIMER();\r
240 \r
241     XML_Tree->TagName = new std::vector<std::string>();\r
242     XML_Tree->tIdMap = new std::unordered_map<std::string,int>();\r
243     std::string s;\r
244     int ntags;\r
245 \r
246     // Load the tag names\r
247     ufread(&ntags, sizeof(int), 1, fp);\r
248 \r
249     for (i=0; i<ntags;i++) {\r
250        if (fgets(buffer,1022,fp) != buffer)\r
251          throw "Cannot read tag list";\r
252        s = buffer;\r
253        // remove the trailing \n\r
254        s.erase(s.size()-1);\r
255        XML_Tree->TagName->push_back(s);\r
256        XML_Tree->tIdMap->insert(std::make_pair(s,i));\r
257 \r
258     };\r
259     STOPTIMER(Loading);\r
260     PRINTTIME("Loading tag names struct", Loading);\r
261     STARTTIMER();\r
262 \r
263     // loads the tag structure\r
264     XML_Tree->Tags = static_sequence::load(fp);\r
265     ufread(&XML_Tree->tags_blen,sizeof(uint),1,fp);\r
266     std::cerr << "tags_blen is "<< XML_Tree->tags_blen <<"\n";\r
267     ufread(&XML_Tree->tags_len,sizeof(uint),1,fp);\r
268     XML_Tree->tags_fix = new uint[uint_len(XML_Tree->tags_blen,XML_Tree->tags_len)];\r
269     ufread(XML_Tree->tags_fix,sizeof(uint),uint_len(XML_Tree->tags_blen,XML_Tree->tags_len),fp);\r
270 \r
271     // TODO ask francisco about this\r
272     /// FIXME:UGLY tests!\r
273     //uint * seq = new uint[XML_Tree->tags_len];\r
274     //for(uint i=0;i<XML_Tree->tags_len;i++)\r
275     //  seq[i] = get_field(XML_Tree->tags_fix,XML_Tree->tags_blen,i);\r
276     //cout << "Tags test: " << XML_Tree->Tags->test(seq,XML_Tree->tags_len) << endl;\r
277     //XML_Tree->Tags->test(seq,XML_Tree->tags_len);\r
278     //delete [] seq;\r
279     /// End ugly tests\r
280 \r
281     STOPTIMER(Loading);\r
282     std::cerr << (uint_len(XML_Tree->tags_blen,XML_Tree->tags_len)*sizeof(uint))/(1024*1024) << " MB for tag sequence" << std::endl;\r
283     PRINTTIME("Loading tag struct", Loading);\r
284     STARTTIMER();\r
285 \r
286     // loads the flags\r
287 \r
288     ufread(&(XML_Tree->disable_tc), sizeof(bool), 1, fp);\r
289     if (load_tc) {\r
290       XML_Tree->EBVector = static_bitsequence_rrr02::load(fp);\r
291 \r
292       STOPTIMER(Loading);\r
293       PRINTTIME("Loading text bitvector struct", Loading);\r
294       STARTTIMER();\r
295 \r
296       // Not used\r
297       // loads the texts\r
298       if (!XML_Tree->disable_tc){\r
299         ufread(&(XML_Tree->text_index_type),\r
300                sizeof(TextCollectionBuilder::index_type_t), 1, fp);\r
301         string file(name);\r
302         switch (XML_Tree->text_index_type){\r
303         case TextCollectionBuilder::index_type_default:\r
304           file.append(".default");\r
305           break;\r
306         case TextCollectionBuilder::index_type_swcsa:\r
307           file.append(".swcsa");\r
308           break;\r
309         case TextCollectionBuilder::index_type_rlcsa:\r
310           file.append(".rlcsa");\r
311           break;\r
312         };\r
313         XML_Tree->Text = TextCollection::Load(fp, file.c_str(), TextCollection::index_mode_default, sample_factor);\r
314 \r
315       }\r
316       else XML_Tree->Text = NULL;\r
317       STOPTIMER(Loading);\r
318       PRINTTIME("Loading TextCollection", Loading);\r
319       STARTTIMER();\r
320     }\r
321     else {\r
322       XML_Tree->EBVector = NULL;\r
323       XML_Tree->Text = NULL;\r
324       XML_Tree->disable_tc = true;\r
325     };\r
326 \r
327 \r
328     return XML_Tree;\r
329  }\r
330 \r
331 \r
332 \r
333 \r
334 int XMLTree::SubtreeElements(treeNode x)\r
335  {\r
336 \r
337     int size = bp_subtree_size(Par, x);\r
338     if (x == Root()){\r
339       x = bp_first_child(Par,x);\r
340       size = size - 1;\r
341     };\r
342 \r
343     int s = x + 2*size - 1;\r
344     int ntext = Tags->rank(PCDATA_TAG_ID, s) - Tags->rank(PCDATA_TAG_ID, node2tagpos(x)-1);\r
345     size = size - ntext;\r
346     treeNode fin = bp_find_close(Par,x);\r
347     treeNode y = Tags->select_next(ATTRIBUTE_TAG_ID,node2tagpos(x));\r
348     while (y != NULLT && y < fin){\r
349       size -= SubtreeSize(y);\r
350       y = Tags->select_next(ATTRIBUTE_TAG_ID, node2tagpos(y));\r
351     };\r
352     return size;\r
353  }\r
354 \r
355 // IsLeaf(x): returns whether node x is leaf or not. In the succinct representation\r
356 // this is just a bit inspection.\r
357 bool XMLTree::IsLeaf(treeNode x)\r
358  {\r
359    NULLT_IF(x==NULLT);\r
360    return bp_isleaf(Par, x);\r
361  }\r
362 \r
363 // IsAncestor(x,y): returns whether node x is ancestor of node y.\r
364 bool XMLTree::IsAncestor(treeNode x, treeNode y)\r
365  {\r
366     return bp_is_ancestor(Par, x, y);\r
367  }\r
368 \r
369 // IsChild(x,y): returns whether node x is parent of node y.\r
370 bool XMLTree::IsChild(treeNode x, treeNode y)\r
371  {\r
372     if (!bp_is_ancestor(Par, x, y)) return false;\r
373     return bp_depth(Par, x) == (bp_depth(Par, y) + 1);\r
374  }\r
375 \r
376 \r
377 // NumChildren(x): number of children of node x. Constant time with the data structure\r
378 // of Sadakane.\r
379 int XMLTree::NumChildren(treeNode x)\r
380  {\r
381     return bp_degree(Par, x);\r
382  }\r
383 \r
384 // ChildNumber(x): returns i if node x is the i-th children of its parent.\r
385 int XMLTree::ChildNumber(treeNode x)\r
386  {\r
387     return bp_child_rank(Par, x);\r
388  }\r
389 \r
390 // Depth(x): depth of node x, a simple binary rank on the parentheses sequence.\r
391 int XMLTree::Depth(treeNode x)\r
392  {\r
393     return bp_depth(Par, x);\r
394  }\r
395 \r
396 // Preorder(x): returns the preorder number of node x, just counting the tree\r
397 // nodes (i.e., tags, it disregards the texts in the tree).\r
398 int XMLTree::Preorder(treeNode x)\r
399  {\r
400     return bp_preorder_rank(Par, x);\r
401  }\r
402 \r
403 // Postorder(x): returns the postorder number of node x, just counting the tree\r
404 // nodes (i.e., tags, it disregards the texts in the tree).\r
405 int XMLTree::Postorder(treeNode x)\r
406  {\r
407     return bp_postorder_rank(Par, x);\r
408  }\r
409 \r
410 // DocIds(x): returns the range of text identifiers that descend from node x.\r
411 // returns {NULLT, NULLT} when there are no texts descending from x.\r
412 range XMLTree::DocIds(treeNode x)\r
413  {\r
414    range r;\r
415    if (x == NULLT) {\r
416      r.min = NULLT;\r
417      r.max = NULLT;\r
418      return r;\r
419    };\r
420    int min = EBVector->rank1(x-1);\r
421    int max = EBVector->rank1(x+2*bp_subtree_size(Par, x)-2);\r
422    if (min==max) { // range is empty, no texts within the subtree of x\r
423      r.min = NULLT;\r
424      r.max = NULLT;\r
425    }\r
426    else { // the range is non-empty, there are texts within the subtree of x\r
427      r.min = min+1;\r
428      r.max = max;\r
429    }\r
430    return r;\r
431  }\r
432 \r
433 \r
434 // Child(x,i): returns the i-th child of node x, assuming it exists.\r
435 treeNode XMLTree::Child(treeNode x, int i)\r
436 {\r
437     if (i <= OPTD) return bp_naive_child(Par, x, i);\r
438     else return bp_child(Par, x, i);\r
439 }\r
440 \r
441 \r
442 treeNode XMLTree::SelectChild(treeNode x, TagIdSet *tags)\r
443 {\r
444 \r
445   NULLT_IF(x==NULLT || bp_isleaf(Par,x));\r
446   int i;\r
447   treeNode child = bp_first_child(Par, x);\r
448   TagType t;\r
449   while (child != NULLT) {\r
450     t = Tag(child);\r
451     if (tags->find(t) != tags->end()) return child;\r
452     child = fast_sibling(Par, child,t);\r
453   }\r
454   return NULLT;\r
455 }\r
456 \r
457 \r
458 treeNode XMLTree::SelectFollowingSibling(treeNode x, TagIdSet *tags)\r
459 {\r
460 \r
461    NULLT_IF(x==NULLT);\r
462    int i;\r
463    TagType t;\r
464    treeNode sibling = bp_next_sibling(Par, x);\r
465    while (sibling != NULLT) {\r
466      t = Tag(sibling);\r
467      if (tags->find(t) != tags->end()) return sibling;\r
468      sibling = fast_sibling(Par, sibling,t);\r
469    }\r
470    return NULLT;\r
471  }\r
472 \r
473 \r
474 // TaggedPrec(x,tag): returns the first node tagged tag with smaller preorder than x and not an\r
475 // ancestor of x. Returns NULLT if there is none.\r
476 treeNode XMLTree::TaggedPreceding(treeNode x, TagType tag)\r
477  {\r
478     int r, s;\r
479     treeNode node_s, root;\r
480     r = (int)Tags->rank(tag, node2tagpos(x)-1);\r
481     if (r==0) return NULLT; // there is no such node.\r
482     s = (int)Tags->select(tag, r);\r
483     root = bp_root_node(Par);\r
484     node_s = tagpos2node(s);\r
485     while (bp_is_ancestor(Par, node_s, x) && (node_s!=root)) { // the one that we found is an ancestor of x\r
486        r--;\r
487        if (r==0) return NULLT; // there is no such node\r
488        s = (int)Tags->select(tag, r);  // we should use select_prev instead when provided\r
489        node_s = tagpos2node(s);\r
490     }\r
491     return NULLT; // there is no such node\r
492  }\r
493 \r
494 \r
495 // TaggedFoll(x,tag): returns the first node tagged tag with larger preorder than x and not in\r
496 // the subtree of x. Returns NULLT if there is none.\r
497 treeNode XMLTree::TaggedFollowing(treeNode x, TagType tag)\r
498  {\r
499    NULLT_IF (x ==NULLT || x == Root());\r
500    return tagpos2node(Tags->select_next(tag, bp_find_close(Par, x)));\r
501 \r
502  }\r
503 \r
504 \r
505 /* Here we inline TaggedFoll to find the min globally, and only at the end\r
506    we check if the min is below the context node */\r
507 treeNode XMLTree::SelectFollowingBelow(treeNode x, TagIdSet *tags, treeNode ancestor)\r
508  {\r
509 \r
510    NULLT_IF(x==NULLT || x==Root());\r
511 \r
512    treeNode close = bp_find_close(Par,x);\r
513    treeNode ns = close+1;\r
514    if ( (bp_inspect(Par,ns) == OP) && (tags->find(Tag(ns)) != tags->end()))\r
515      return ns;\r
516 \r
517    int i;\r
518    treeNode min = NULLT;\r
519    treeNode aux;\r
520 \r
521 \r
522    TagIdSet::const_iterator tagit;\r
523    for (tagit = tags->begin(); tagit != tags->end(); ++tagit) {\r
524 \r
525      aux = tagpos2node(Tags->select_next(*tagit, close));\r
526      if (aux == NULLT) continue;\r
527      if ((min == NULLT) || (aux < min)) min = aux;\r
528    };\r
529 \r
530    // found the smallest node in preorder which is after x.\r
531    // if ctx is the root node, just return what we found.\r
532 \r
533    if (ancestor == Root() || min == NULLT || min < bp_find_close(Par, ancestor)) return min;\r
534    else return NULLT;\r
535 \r
536  }\r
537 // TaggedAncestor(x, tag): returns the closest ancestor of x tagged tag. Return\r
538 // NULLT is there is none.\r
539 treeNode XMLTree::TaggedAncestor(treeNode x, TagType tag)\r
540  {\r
541     if (x == NULLT || x == Root())\r
542        return NULLT;\r
543 \r
544     treeNode s = bp_parent(Par, x), r = Root();\r
545     while (s != r) {\r
546       if (Tag(s) == tag) return s;\r
547        s = bp_parent(Par, s);\r
548     }\r
549     return NULLT;\r
550  }\r
551 \r
552 \r
553 \r
554 // MyText(x): returns the document identifier of the text below node x,\r
555 // or NULLT if x is not a leaf node or the text is empty. Assumes Doc\r
556 // ids start from 0.\r
557 DocID XMLTree::MyText(treeNode x)\r
558 {\r
559   TagType tag = Tag(x);\r
560   // seems faster than testing EBVector->access(x);\r
561 \r
562   if (tag == PCDATA_TAG_ID || tag == ATTRIBUTE_DATA_TAG_ID)\r
563     return (DocID) (EBVector->rank1(x)-1);\r
564   else\r
565     return (DocID) NULLT;\r
566 \r
567 }\r
568 // MyText(x): returns the document identifier of the text below node x,\r
569 // or NULLT if x is not a leaf node or the text is empty. Assumes Doc\r
570 // ids start from 0.\r
571 DocID XMLTree::MyTextUnsafe(treeNode x)\r
572 {\r
573   return (DocID) (EBVector->rank1(x)-1); //-1 because document ids start from 0\r
574 \r
575 }\r
576 // TextXMLId(d): returns the preorder of document with identifier d in the tree consisting of\r
577 // all tree nodes and all text nodes. Assumes that the tree root has preorder 1.\r
578 int XMLTree::TextXMLId(DocID d)\r
579  {\r
580    NULLT_IF(d == NULLT);\r
581      int s = EBVector->select1(d+1);\r
582    return bp_rank_open(Par, s) + d + 1; // +1 because root has preorder 1\r
583 \r
584  }\r
585 \r
586 // NodeXMLId(x): returns the preorder of node x in the tree consisting\r
587 // of all tree nodes and all text nodes. Assumes that the tree root has\r
588 // preorder 0;\r
589 int XMLTree::NodeXMLId(treeNode x)\r
590  {\r
591    NULLT_IF(x == NULLT);\r
592    if (x == Root()) return 1; // root node has preorder 1\r
593    return bp_rank_open(Par, x) + EBVector->rank1(x-1);\r
594  }\r
595 \r
596 // ParentNode(d): returns the parent node of document identifier d.\r
597 treeNode XMLTree::ParentNode(DocID d)\r
598  {\r
599    NULLT_IF (d == NULLT);\r
600    return (treeNode) EBVector->select1(d+1);\r
601  }\r
602 \r
603 // GetTagId: returns the tag identifier corresponding to a given tag name.\r
604 // Returns NULLT in case that the tag name does not exists.\r
605 TagType XMLTree::GetTagId(unsigned char *tagname)\r
606  {\r
607 \r
608    string s = (char *) tagname;\r
609    TagIdMapIT it = tIdMap->find(s);\r
610    return (TagType) ((it != tIdMap->end()) ? it->second : -1);\r
611 \r
612  }\r
613 \r
614 \r
615 // GetTagName(tagid): returns the tag name of a given tag identifier.\r
616 // Returns NULL in case that the tag identifier is not valid.\r
617 unsigned char *XMLTree::GetTagName(TagType tagid)\r
618  {\r
619     unsigned char *s;\r
620     if ( tagid < 0 || tagid >= TagName->size())\r
621       return (unsigned char *) "<INVALID TAG>";\r
622     strcpy((char *)s, (*TagName)[tagid].c_str());\r
623 \r
624     return (s == NULL ? (unsigned char*) "<INVALID TAG>" : s);\r
625  }\r
626 \r
627 \r
628 const unsigned char *XMLTree::GetTagNameByRef(TagType tagid)\r
629  {\r
630 \r
631    unsigned char *s;\r
632    if ( tagid < 0 || tagid >= TagName->size())\r
633      return (unsigned char *) "<INVALID TAG>";\r
634 \r
635    return (const unsigned char *) (*TagName)[tagid].c_str();\r
636 \r
637  }\r
638 \r
639 \r
640 \r
641 TagType XMLTree::RegisterTag(unsigned char *tagname)\r
642  {\r
643     TagType id = XMLTree::GetTagId(tagname);\r
644     if (id == NULLT) {\r
645       string s = (char *) tagname;\r
646       REGISTER_TAG(TagName,tIdMap,s);\r
647     };\r
648 \r
649     return id;\r
650  }\r
651 \r
652 \r
653 treeNode XMLTree::Closing(treeNode x) {\r
654   return bp_find_close(Par,x);\r
655 }\r
656 bool XMLTree::IsOpen(treeNode x) { return bp_inspect(Par,x); }\r
657 \r
658 //WARNING this uses directly the underlying implementation for plain text\r
659 \r
660 \r
661 void XMLTree::Print(int fd,treeNode x, bool no_text){\r
662 \r
663   if (buffer == 0) {\r
664     buffer = new string(BUFFER_ALLOC, 0);\r
665     buffer->clear();\r
666     print_stack = new std::vector<string *>();\r
667     print_stack->reserve(256);\r
668   };\r
669 \r
670   treeNode fin = bp_find_close(Par,x);\r
671   treeNode n = x;\r
672   TagType tag = Tag(n);\r
673 \r
674   range r = DocIds(x);\r
675   treeNode first_idx;\r
676   treeNode first_text = (tag == PCDATA_TAG_ID ?  x : ParentNode(r.min-1));\r
677   treeNode first_att =  NULLT;\r
678 \r
679   if (first_att  == NULLT)\r
680     first_idx = first_text;\r
681   else if (first_text == NULLT)\r
682     first_idx = first_att;\r
683   else\r
684     first_idx = min(first_att,first_text);\r
685 \r
686    uchar * current_text=NULL;\r
687 \r
688    if (first_idx != NULLT)\r
689      current_text = GetText(MyTextUnsafe(first_idx));\r
690 \r
691    size_t read = 0;\r
692 \r
693    while (n <= fin){\r
694      if (bp_inspect(Par,n)){\r
695        if (tag == PCDATA_TAG_ID) {\r
696 \r
697          if (no_text)\r
698            _dputs("<$/>", fd);\r
699          else {\r
700            read = _dprintf((const char*) current_text, fd);\r
701            current_text += (read + 1);\r
702          };\r
703          n+=2; // skip closing $\r
704          tag = Tag(n);\r
705 \r
706        } else {\r
707 \r
708          _dputc('<',fd);\r
709          _dput_str((*TagName)[tag], fd);\r
710          n++;\r
711          if (bp_inspect(Par,n)) {\r
712            print_stack->push_back(&((*TagName)[tag]));\r
713            tag = Tag(n);\r
714            if (tag == ATTRIBUTE_TAG_ID){\r
715              n++;\r
716              if (no_text) _dputs("><@@>",fd);\r
717 \r
718              while (bp_inspect(Par,n)){\r
719                if (no_text) {\r
720                  _dputc('<', fd);\r
721                  _dputs((const char*) &(GetTagNameByRef(Tag(n))[3]), fd);\r
722                  _dputc('>', fd);\r
723                  _dputs("<$@/></", fd);\r
724                  _dputs((const char*) &(GetTagNameByRef(Tag(n))[3]), fd);\r
725                  _dputc('>', fd);\r
726                  n+= 4;\r
727                } else {\r
728                  _dputc(' ', fd);\r
729                  _dputs((const char*) &(GetTagNameByRef(Tag(n))[3]), fd);\r
730                  n++;\r
731                  _dputs("=\"", fd);\r
732                  read = _dprintf((const char*) current_text, fd);\r
733                  current_text += (read + 1);\r
734                  _dputc('"', fd);\r
735                  n+=3;\r
736                }\r
737              };\r
738              if (no_text) _dputs("</@@>", fd);\r
739              else _dputc('>', fd);\r
740              n++;\r
741              tag=Tag(n);\r
742 \r
743            } else\r
744              _dputc('>', fd);\r
745 \r
746          } else {// <foo /> tag\r
747            _dputs("/>", fd);\r
748            n++;\r
749            tag=Tag(n);\r
750          };\r
751        };\r
752      } else do {\r
753          _dputs("</", fd);\r
754          _dput_str(*(print_stack->back()), fd);\r
755          _dputc('>', fd);\r
756          print_stack->pop_back();\r
757          n++;\r
758        } while (!(bp_inspect(Par,n) || print_stack->empty()));\r
759      tag = Tag(n);\r
760    };\r
761    _dputc('\n', fd);\r
762    //_flush(fd);\r
763 }\r