source: trunk/src/objects/RpParserXML.cc @ 1560

Last change on this file since 1560 was 1560, checked in by dkearney, 15 years ago

updates to the object system, fixed up tree and xml parser objects, added some basic tests for them and adopted number object to accept xml text and configure itself from the parsed xml. added fermi3.cc example program which contains suggested interface from apps meeting.

File size: 17.0 KB
Line 
1/*
2 * ----------------------------------------------------------------------
3 *  Rappture 2.0 XML Parser Object Source
4 *
5 * ======================================================================
6 *  AUTHOR:  Derrick Kearney, Purdue University
7 *  Copyright (c) 2005-2009  Purdue Research Foundation
8 *
9 *  See the file "license.terms" for information on usage and
10 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 * ======================================================================
12 */
13
14#include "RpParserXML.h"
15#include "RpSimpleBuffer.h"
16#include "RpPath.h"
17
18static const char *Rp_ParserXml_Field_ID = "id";
19static const char *Rp_ParserXml_Field_VALUE = "value";
20static const char *Rp_ParserXml_Field_VISITED = "visited";
21
22struct Rp_ParserXmlStruct {
23    Rp_Tree tree;
24    Rp_TreeNode curr;
25    Rappture::Path *path;
26    Rappture::SimpleCharBuffer *buf;
27};
28
29
30static void XMLCALL
31Rp_ParserXmlStartHandler(
32    void *data,
33    const char *el,
34    const char **attr)
35{
36    Rp_ParserXml *inf = (Rp_ParserXml *) data;
37    size_t i = 0;
38
39    inf->curr = Rp_TreeCreateNode(inf->tree, inf->curr, el, -1);
40
41    // store the attributes in the node
42    while (attr[i] != NULL) {
43        const char *attrName = attr[i++];
44        size_t attrValueLen = strlen(attr[i]);
45        // FIXME: memory leak originating from this new call
46        // I'm not sure where/when the attrValue is deleted yet
47        char *attrValue = new char[attrValueLen+1];
48        strcpy(attrValue,attr[i++]);
49        Rp_TreeSetValue(inf->tree,inf->curr,attrName,(void *)attrValue);
50    }
51    inf->path->add(el);
52}
53
54static void XMLCALL
55Rp_ParserXmlEndHandler(
56    void *data,
57    const char *el)
58{
59    Rp_ParserXml *inf = (Rp_ParserXml *) data;
60
61    if (inf == NULL) {
62        return;
63    }
64
65    char *value = NULL;
66    Rp_TreeGetValue(inf->tree,inf->curr,Rp_ParserXml_Field_VALUE,(void **)&value);
67
68    // strip trailing spaces
69    int i = 0;
70    for (i = strlen(value)-1; i >= 0; i--) {
71        if (isspace(value[i])) {
72            value[i] = '\0';
73        } else {
74            break;
75        }
76    }
77
78    // strip leading spaces
79    int j = 0;
80    for (j = 0; j < i; j++) {
81        if (isspace(value[j])) {
82            value[j] = '\0';
83        } else {
84            break;
85        }
86    }
87
88    if (j > 0) {
89        // reallocate the trimmed string
90        char *newValue = new char[i-j+1+1];
91        strcpy(newValue,value+j);
92        Rp_TreeSetValue(inf->tree,inf->curr,Rp_ParserXml_Field_VALUE,(void *)newValue);
93        delete value;
94        value = NULL;
95    }
96
97    inf->path->del();
98    inf->curr = Rp_TreeNodeParent(inf->curr);
99}
100
101void
102Rp_ParserXmlDefaultCharHandler(
103    void* data,
104    XML_Char const* s,
105    int len)
106{
107    Rp_ParserXml *inf = (Rp_ParserXml *) data;
108
109    if (inf == NULL) {
110        return;
111    }
112
113    // FIXME: where/when is this deallocated
114    char *d = new char[len+1];
115
116    if ( s != NULL) {
117        strncpy(d,s,len);
118    }
119
120    d[len] = '\0';
121    Rp_TreeSetValue(inf->tree,inf->curr,Rp_ParserXml_Field_VALUE,(void *)d);
122}
123
124Rp_ParserXml *
125Rp_ParserXmlCreate()
126{
127    Rp_ParserXml *p = new Rp_ParserXml();
128
129    Rp_TreeCreate("rapptureTree",&(p->tree));
130    p->curr = Rp_TreeRootNode(p->tree);
131    p->path = new Rappture::Path();
132    p->buf = new Rappture::SimpleCharBuffer();
133
134
135    return p;
136}
137
138void
139Rp_ParserXmlDestroy(Rp_ParserXml **p)
140{
141    if (p == NULL) {
142        return;
143    }
144
145    if (*p == NULL) {
146        return;
147    }
148
149    Rp_TreeReleaseToken((*p)->tree);
150    delete (*p)->buf;
151    delete (*p)->path;
152    delete *p;
153    p = NULL;
154}
155
156void
157Rp_ParserXmlParse(
158    Rp_ParserXml *p,
159    const char *xml)
160{
161    if (xml == NULL) {
162        return;
163    }
164
165    XML_Parser parser = XML_ParserCreate(NULL);
166    XML_SetUserData(parser,p);
167    XML_SetElementHandler(parser, Rp_ParserXmlStartHandler,
168        Rp_ParserXmlEndHandler);
169    XML_SetDefaultHandlerExpand(parser, Rp_ParserXmlDefaultCharHandler);
170
171    int done = 1;
172    int len = 0;
173
174    len = strlen(xml);
175    if (XML_Parse(parser, xml, len, done) == XML_STATUS_ERROR) {
176        fprintf(stderr, "Parse error at line %lu:\n%s\n",
177                XML_GetCurrentLineNumber(parser),
178                XML_ErrorString(XML_GetErrorCode(parser)));
179        exit(-1);
180    }
181
182    XML_ParserFree(parser);
183
184    // reset the root node
185    p->curr = Rp_TreeFirstChild(Rp_TreeRootNode(p->tree));
186
187    return;
188}
189
190Rp_TreeNode
191Rp_ParserXmlCreateNode(
192    Rp_ParserXml *p,
193    Rp_TreeNode parent,
194    const char *type,
195    const char *id)
196{
197    if (!p->tree || !parent || !type) {
198        fprintf(stderr,"failed to create node because of invalid data\n");
199        return NULL;
200    }
201
202    Rp_TreeNode child = Rp_TreeCreateNode(p->tree, parent, type, -1);
203
204    if (id != NULL) {
205        size_t attrValueLen = strlen(id);
206        // FIXME: memory leak originating from this new call
207        // I'm not sure where/when the attrValue is deleted yet
208        char *attrValue = new char[attrValueLen+1];
209        strcpy(attrValue,id);
210        Rp_TreeSetValue(p->tree,child,Rp_ParserXml_Field_ID,(void *)attrValue);
211    }
212
213    return child;
214}
215
216Rp_TreeNode
217Rp_ParserXmlSearch(
218    Rp_ParserXml *p,
219    const char *path,
220    int create)
221{
222    Rappture::Path pathObj(path);
223    Rp_TreeNode parent = NULL;
224    Rp_TreeNode child = NULL;
225    size_t degree = 0;
226
227    if (p == NULL) {
228        return NULL;
229    }
230
231    // start searching from the base node
232    parent = p->curr;
233
234    // if (path == NULL) return the base node
235    child = p->curr;
236
237    pathObj.first();
238    while ( (!pathObj.eof()) &&
239            (parent != NULL) ) {
240
241        const char *childName = pathObj.type();
242        const char *searchChildId = pathObj.id();
243
244        child = Rp_TreeFindChild(parent,childName);
245        if (child == NULL) {
246            // no nodes with the name childName exist
247            // FIXME: use the RPXML_CREATE flag
248            if (create) {
249                for (size_t i = 1; i <= pathObj.degree(); i++) {
250                    child = Rp_ParserXmlCreateNode(p, parent,
251                                childName, searchChildId);
252                    if (child == NULL) {
253                        break;
254                    }
255                }
256            } else {
257                fprintf(stderr,"invalid path %s at %s\n",path,childName);
258                break;
259            }
260        }
261
262        //FIXME: rewrite the child id search logic
263        const char *id = NULL;
264        Rp_TreeGetValue(p->tree,child, Rp_ParserXml_Field_ID,(void **)&id);
265        while (searchChildId && id) {
266
267            // check for matching child id and degree
268            if ((*searchChildId == *id) &&
269                (strcmp(searchChildId,id) == 0)) {
270                // found child with matching id
271                // check degree
272                degree++;
273                if (degree == pathObj.degree()) {
274                    // degrees match, return result
275                    break;
276                }
277            }
278
279            // check the next child to see if it has the correct id
280            child = Rp_TreeNextSibling(child);
281            if (child == NULL) {
282                // end of the sibling list
283                // create new node? or return NULL pointer
284                // FIXME: use the RPXML_CREATE flag
285                if (create) {
286                    child = Rp_ParserXmlCreateNode(p, parent,
287                                childName, searchChildId);
288                    if (child == NULL) {
289                        break;
290                    }
291                } else {
292                    // exit, child not found
293                    break;
294                }
295            }
296            id = NULL;
297            Rp_TreeGetValue(p->tree,child,Rp_ParserXml_Field_ID,(void **)&id);
298        }
299
300        parent = child;
301        pathObj.next();
302    }
303
304    return child;
305}
306
307const char *
308Rp_ParserXmlGet(
309    Rp_ParserXml *p,
310    const char *path)
311{
312    const char *value = NULL;
313    Rp_TreeNode child = NULL;
314
315    child = Rp_ParserXmlSearch(p, path, 0);
316    if (child != NULL) {
317        Rp_TreeGetValue(p->tree,child,Rp_ParserXml_Field_VALUE,(void **)&value);
318    }
319
320    return value;
321}
322
323void
324Rp_ParserXmlPut(
325    Rp_ParserXml *p,
326    const char *path,
327    const char *val,
328    int append)
329{
330    const char *oldval = NULL;
331    char *newval = NULL;
332    size_t oldval_len = 0;
333    size_t val_len = 0;
334
335    if (val == NULL) {
336        // no value, do nothing
337        return;
338    }
339
340    Rp_TreeNode child = Rp_ParserXmlSearch(p, path, 1);
341
342    if (child != NULL) {
343        // check to see if there is already a value
344        if (RP_OK == Rp_TreeGetValue(p->tree,child,
345                        Rp_ParserXml_Field_VALUE,
346                        (void **)&oldval)) {
347            if (oldval != NULL) {
348                // FIXME: use the RPXML_APPEND flag
349                if (append) {
350                    oldval_len = strlen(oldval);
351                    val_len = strlen(val);
352                    newval = new char[oldval_len + val_len + 1];
353                    strncpy(newval,oldval,oldval_len);
354                }
355                // free the old data
356                delete(oldval);
357                oldval = NULL;
358            }
359        }
360
361        // allocate space for our new value if needed
362        if (newval == NULL) {
363            // set the new value for the node
364            val_len = strlen(val);
365            newval = new char[val_len + 1];
366        }
367
368        strcpy(newval+oldval_len,val);
369
370        // set the value of the child node
371        if (RP_ERROR == Rp_TreeSetValue(p->tree,child,
372                            Rp_ParserXml_Field_VALUE,
373                            (void *)newval)) {
374            fprintf(stderr,"error while setting value of %s\n",path);
375        }
376    }
377    return;
378}
379
380Rp_Tree
381Rp_ParserXmlTreeClient(
382    Rp_ParserXml *p)
383{
384    if (p == NULL) {
385        return NULL;
386    }
387    Rp_Tree newTree = NULL;
388    // create a new token for the tree and return it to the caller
389    Rp_TreeGetTokenFromToken(p->tree,&newTree);
390    return newTree;
391}
392
393int
394printXmlData(
395    Rp_TreeNode node,
396    ClientData clientData,
397    int order)
398{
399    Rp_ParserXml *p = (Rp_ParserXml *) clientData;
400
401    Rappture::Path labelComp(Rp_TreeNodeLabel(node));
402    const char *label = labelComp.type();
403    const char *value = NULL;
404
405    size_t width = (Rp_TreeNodeDepth(p->tree,node)-1)*PARSERXML_LEVEL_WIDTH;
406    const char *sp = "";
407    int *visited = NULL;
408
409    Rp_TreeGetValue(p->tree,node,Rp_ParserXml_Field_VALUE,(void **)&value);
410    size_t valLen = strlen(value);
411
412    if (!Rp_TreeValueExists(p->tree,node,Rp_ParserXml_Field_VISITED)) {
413        visited = new int();
414        *visited = 0;
415
416        p->buf->appendf("%3$*2$s<%1$s",label,width,sp);
417
418        // go through all attributes and place them in the tag
419        Rp_TreeKey attrName = NULL;
420        Rp_TreeKeySearch search;
421        attrName = Rp_TreeFirstKey (p->tree, node, &search);
422        while (attrName) {
423            if ((*Rp_ParserXml_Field_VALUE != *attrName) &&
424                (strcmp(Rp_ParserXml_Field_VALUE,attrName) != 0)) {
425                const char *attrValue = NULL;
426                Rp_TreeGetValueByKey(p->tree,node,attrName,(void**)&attrValue);
427                p->buf->appendf(" %s=\"%s\"",attrName,attrValue);
428            }
429            attrName = Rp_TreeNextKey(p->tree, &search);
430        }
431
432        // close the opening tag
433        p->buf->appendf(">");
434
435        // append the value of the tag if any
436        if ( (value != NULL ) &&
437             (valLen != 0) ) {
438            p->buf->appendf("%s",value);
439        } else {
440            p->buf->appendf("\n");
441        }
442
443        // set the "visited" temporary flag so the next time
444        // we encounter this node, we know it is as a closing node
445        Rp_TreeSetValue(p->tree,node,Rp_ParserXml_Field_VISITED,(void *)visited);
446
447    } else {
448        Rp_TreeGetValue(p->tree,node,Rp_ParserXml_Field_VISITED,(void **)&visited);
449        delete visited;
450        Rp_TreeUnsetValue(p->tree,node,Rp_ParserXml_Field_VISITED);
451        if ( (value != NULL ) &&
452             (valLen != 0) ) {
453            p->buf->appendf("</%s>\n",label);
454        } else {
455            p->buf->appendf("%3$*2$s</%1$s>\n",label,width,sp);
456        }
457
458    }
459
460    return RP_OK;
461}
462
463const char *
464Rp_ParserXmlXml(
465    Rp_ParserXml *p)
466{
467    p->buf->clear();
468    p->buf->appendf("<?xml version=\"1.0\"?>\n");
469
470    Rp_TreeNode root = p->curr;
471
472    Rp_TreeApplyDFS(root, printXmlData, (ClientData)p, TREE_PREORDER|TREE_POSTORDER);
473
474    return p->buf->bytes();
475}
476
477int
478printPathVal(
479    Rp_TreeNode node,
480    ClientData clientData,
481    int order)
482{
483    Rp_ParserXml *p = (Rp_ParserXml *) clientData;
484
485    int *visited = NULL;
486
487    if (!Rp_TreeValueExists(p->tree,node,Rp_ParserXml_Field_VISITED)) {
488        // update path
489        p->path->add(Rp_TreeNodeLabel(node));
490
491        const char *id = NULL;
492        if (RP_OK == Rp_TreeGetValue(p->tree,node,Rp_ParserXml_Field_ID,(void **)&id)) {
493            p->path->last();
494            p->path->id(id);
495        }
496
497        const char *value = NULL;
498        if (RP_ERROR == Rp_TreeGetValue(p->tree,node,Rp_ParserXml_Field_VALUE,(void **)&value)) {
499            // error while getting value, exit fxn gracefully?
500            // FIXME: add error code here
501        }
502
503        // append the path and value of the node to our result text
504        if (value && (strlen(value) != 0)) {
505            p->buf->appendf("%s %s\n",p->path->path(),value);
506        }
507
508        // set the "visited" temporary flag so the next time
509        // we encounter this node, we know it is as a closing node
510        visited = new int();
511        *visited = 0;
512        if (RP_ERROR == Rp_TreeSetValue(p->tree,node,Rp_ParserXml_Field_VISITED,(void *)visited)) {
513            // FIXME: error while setting value
514        }
515    } else {
516        // update path
517        p->path->del();
518
519        // delete the "visited" temporary flag from the node
520        if (RP_ERROR == Rp_TreeGetValue(p->tree,node,Rp_ParserXml_Field_VISITED,(void **)&visited)) {
521            // FIXME: error while getting value
522        }
523        delete visited;
524        if (RP_ERROR == Rp_TreeUnsetValue(p->tree,node,Rp_ParserXml_Field_VISITED)) {
525            // FIXME: error while unsetting value
526        }
527    }
528
529    return RP_OK;
530}
531
532const char *
533Rp_ParserXmlPathVal(
534    Rp_ParserXml *p)
535{
536    p->buf->clear();
537
538    // temporarily store p->path so we can use p as ClientData
539    Rappture::Path *tmpPath = p->path;
540    p->path = new Rappture::Path();
541
542    Rp_TreeNode root = p->curr;
543
544    Rp_TreeApplyDFS(root, printPathVal, (ClientData)p, TREE_PREORDER|TREE_POSTORDER);
545
546    // restore the original path
547    delete p->path;
548    p->path = tmpPath;
549
550    return p->buf->bytes();
551}
552
553Rp_TreeNode
554Rp_ParserXmlElement(
555    Rp_ParserXml *p,
556    const char *path)
557{
558    if (p == NULL) {
559        return NULL;
560    }
561
562    return Rp_ParserXmlSearch(p,path,!RPXML_CREATE);
563}
564
565Rp_TreeNode
566Rp_ParserXmlParent(
567    Rp_ParserXml *p,
568    const char *path)
569{
570    if (p == NULL) {
571        return NULL;
572    }
573
574    Rp_TreeNode node = Rp_ParserXmlSearch(p,path,!RPXML_CREATE);
575    if (node == NULL) {
576        return NULL;
577    }
578
579    return Rp_TreeNodeParent(node);
580}
581
582size_t
583Rp_ParserXmlChildren(
584    Rp_ParserXml *p,
585    const char *path,
586    const char *type,
587    Rp_Chain *children)
588{
589    size_t count = 0;
590
591    if (p == NULL) {
592        return count;
593    }
594
595    if (children == NULL) {
596        return count;
597    }
598
599    Rp_TreeNode node = Rp_ParserXmlSearch(p,path,!RPXML_CREATE);
600
601    if (node != NULL) {
602        node = Rp_TreeFindChild(node,type);
603        while (node != NULL) {
604            count++;
605            Rp_ChainAppend(children,(void*)node);
606            node = Rp_TreeFindChildNext(node,type);
607        }
608    }
609    return count;
610}
611
612size_t
613Rp_ParserXmlNumberChildren(
614    Rp_ParserXml *p,
615    const char *path,
616    const char *type)
617{
618    size_t count = 0;
619
620    if (p == NULL) {
621        return count;
622    }
623
624    Rp_TreeNode node = Rp_ParserXmlSearch(p,path,!RPXML_CREATE);
625    if (node != NULL) {
626        node = Rp_TreeFindChild(node,type);
627        while (node != NULL) {
628            count++;
629            node = Rp_TreeFindChildNext(node,type);
630        }
631    }
632
633    return count;
634}
635
636void
637Rp_ParserXmlBaseNode(
638    Rp_ParserXml *p,
639    Rp_TreeNode node)
640{
641    if (p != NULL) {
642        if (node == NULL) {
643            p->curr = Rp_TreeFirstChild(Rp_TreeRootNode(p->tree));
644        } else {
645            p->curr = node;
646        }
647    }
648}
649
650/*
651const char *
652Rp_ParserXmlNodePath(
653    Rp_ParserXml *p,
654    Rp_TreeNode node)
655{
656    Path pathObj("junk");
657
658    const char *type = NULL;
659    const char *id = NULL;
660
661    // FIXME: path's add and del don't work on the current node
662    if (p != NULL) {
663        while (node != p->curr) {
664            type = Rp_TreeNodeLabel(node)
665            Rp_TreeGetValue(p->tree,child, Rp_ParserXml_Field_ID,(void **)&id);
666            pathObj.first();
667            pathObj.add(type);
668            pathObj.id(id);
669            node = Rp_TreeNodeParent(node);
670        }
671    }
672
673    pathObj.first();
674    pathObj.del();
675    return path
676}
677*/
678
679const char *
680Rp_ParserXmlNodeId(
681    Rp_ParserXml *p,
682    Rp_TreeNode node)
683{
684    const char *id = NULL;
685    if (p != NULL) {
686        if (node != NULL) {
687            Rp_TreeGetValue(p->tree,node, Rp_ParserXml_Field_ID,(void **)&id);
688        }
689    }
690    return id;
691}
692
693// -------------------------------------------------------------------- //
694
Note: See TracBrowser for help on using the repository browser.