source: trunk/src/objects/RpPath.cc

Last change on this file was 5673, checked in by ldelgass, 9 years ago

Fix line endings, set eol-style to native on all C/C++ sources.

  • Property svn:eol-style set to native
File size: 15.6 KB
Line 
1/*
2 * ======================================================================
3 *  Rappture::Path
4 *
5 *  AUTHOR:  Derrick Kearney, Purdue University
6 *
7 *  Copyright (c) 2004-2012  HUBzero Foundation, LLC
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 "RpPath.h"
15
16#include <fstream>
17#include <assert.h>
18
19#ifdef __cplusplus
20    extern "C" {
21#endif // ifdef __cplusplus
22
23#include "RpChainHelper.h"
24#include <cstring>
25#include <cstdlib>
26
27using namespace Rappture;
28
29/**
30 * Construct an empty Path.
31 */
32Path::Path() :
33    _ifs(0),
34    _pathList(NULL)
35{
36    __pathInit();
37}
38
39
40/**
41 * Construct a Path loaded with initial data.
42 *
43 * @param bytes pointer to bytes being stored.
44 */
45Path::Path(const char* bytes) :
46    _ifs(0),
47    _pathList(NULL)
48{
49    __pathInit();
50    path(bytes);
51}
52
53/**
54 * Copy constructor
55 * @param Path object to copy
56 */
57Path::Path(const Path& b)
58{
59    __pathInit();
60
61    _ifs = b._ifs;
62
63    //FIXME: i think this is broken
64    Rp_ChainCopy (_pathList, b._pathList, Rp_ChainCharCpyFxn);
65}
66
67/**
68 * Assignment operator
69 * @param Path object to copy
70 */
71/*
72Path&
73Path::operator=(const Path& b)
74{
75    b = bSimpleCharBuffer::operator=(b);
76    return *this;
77}
78
79
80Path
81Path::operator+(const Path& b) const
82{
83    Path newBuffer(*this);
84    newBuffer.operator+=(b);
85    return newBuffer;
86}
87
88
89Path&
90Path::operator+=(const Path& b)
91{
92    SimpleCharBuffer::operator+=(b);
93    return *this;
94}
95*/
96
97
98Path::~Path()
99{
100    __pathFree();
101}
102
103
104void
105Path::__pathInit()
106{
107    _ifs = '.';
108    _pathList = Rp_ChainCreate();
109    _currLink = Rp_ChainFirstLink(_pathList);
110    return;
111}
112
113void
114Path::__pathFree()
115{
116    if (_pathList) {
117        Rp_ChainLink *l = NULL;
118        componentStruct *compVal = NULL;
119        l = Rp_ChainFirstLink(_pathList);
120        while(l) {
121            compVal = (componentStruct *) Rp_ChainGetValue(l);
122            __deleteComponent(compVal);
123            compVal = NULL;;
124            l = Rp_ChainNextLink(l);
125        }
126        Rp_ChainDestroy(_pathList);
127        _pathList = NULL;
128    }
129    b.clear();
130    return;
131}
132
133Path::componentStruct *
134Path::__createComponent(
135    const char *p,
136    int typeStart,
137    int typeEnd,
138    int idOpenParen,
139    int idCloseParen,
140    size_t degree)
141{
142    componentStruct *c = new componentStruct();
143    int typeLen = -1;
144    int idLen = -1;
145    char *tmp = NULL;
146
147    c->type = NULL;
148    c->id = NULL;
149    c->degree = degree;
150
151    typeLen = typeEnd - typeStart;
152    if (idOpenParen < idCloseParen) {
153        // user specified an id
154        idLen = idCloseParen - idOpenParen - 1;
155    }
156
157    /*
158        typeLen and idLen are initialized to -1
159        if the user specified a value, the length
160        is set to the length of the specified value.
161        if the user specified a blank string, the
162        length is set to 0 and we still create a char array
163        for the type or id field.
164        we differentiate a blank entry from no entry so we
165        can accept paths like this:
166        input.group(tabs).().current
167        input.group(tabs).()
168        and correctly return the id,type,component, and parent
169        versions of the path. these paths are corner cases that
170        should never be used, but we handle them incase they are.
171        if an id is not specified (no value, not even blank),
172        the id pointer is set to NULL. in the above example paths
173        the type and id of the third component are both blank.
174        for the above example paths, the id field of the first
175        components, the input component, is NULL.
176        i don't think there is a way to get a NULL type right now.
177    */
178
179
180    if (typeLen >= 0) {
181        tmp = new char[typeLen+1];
182        strncpy(tmp,(p+typeStart),typeLen);
183        tmp[typeLen] = '\0';
184        c->type = tmp;
185    }
186
187    if (idLen >= 0) {
188        tmp = new char[idLen+1];
189        strncpy(tmp,(p+idOpenParen+1),idLen);
190        tmp[idLen] = '\0';
191        c->id = tmp;
192    }
193
194    return c;
195}
196
197void
198Path::__deleteComponent(componentStruct *c)
199{
200    if (c) {
201        if (c->type) {
202            delete[] c->type;
203        }
204        if (c->id) {
205            delete[] c->id;
206        }
207        delete c;
208    }
209}
210
211Rp_Chain *
212Path::__parse(const char *p)
213{
214    int typeStart = 0;
215    int typeEnd = -1;       // typeEnd must be less than typeStart
216                            // so we can check if there was a type
217    int curr = 0;
218    int idOpenParen = -1;
219    int idCloseParen = -1;
220    componentStruct *c = NULL;
221    size_t degree = 1;
222    char *newEnd = NULL;
223
224    Rp_Chain *compList = Rp_ChainCreate();
225
226    if (p == NULL) {
227        return 0;
228    }
229
230    while (p[curr] != '\0') {
231        if (p[curr] == '(') {
232            idOpenParen = curr;
233            typeEnd = curr;
234        } else if (p[curr] == ')') {
235            idCloseParen = curr;
236        } else if ( (idOpenParen <= idCloseParen) &&
237                    (curr != 0) &&
238                    (p[curr] >= '0') &&
239                    (p[curr] <= '9') ) {
240            // we are not inside the parens
241            // we are not at the start of the string
242            // the value is a digit
243            if (idOpenParen == idCloseParen) {
244                // we are before any possible parens
245                // and after the type
246                typeEnd = curr;
247            }
248            degree = (size_t) strtod(p+curr, &newEnd);
249            if (degree == 0) {
250                // interpret degree of 0 same as degree of 1
251                degree = 1;
252            }
253            // check if we consumed the _ifs
254            if (*(newEnd-1) == _ifs) {
255                newEnd -= 2;
256            }
257            curr += newEnd - (p + curr);
258        } else if ( (p[curr] == _ifs) &&
259                    ((idOpenParen == idCloseParen) ||
260                     (idOpenParen < idCloseParen)) ) {
261            // we see the _ifs
262            // we are not inside of the parens for the id tag
263            if (typeEnd < typeStart) {
264                // we didn't come across the end
265                // of the type string, so set it here.
266                typeEnd = curr;
267            }
268            c = __createComponent(p,typeStart,typeEnd,idOpenParen,idCloseParen,degree);
269            if (c != NULL) {
270                Rp_ChainAppend(compList,c);
271            }
272
273            typeStart = curr+1;
274            idOpenParen = -1;
275            idCloseParen = -1;
276            degree = 1;
277        }
278        curr++;
279    }
280
281    if (typeEnd < typeStart) {
282        // < takes care of cases where there is no
283        // id and no degree, but there is still a type
284        typeEnd = curr;
285    }
286    c = __createComponent(p,typeStart,typeEnd,idOpenParen,idCloseParen,degree);
287    if (c != NULL) {
288        Rp_ChainAppend(compList,c);
289    }
290
291    return compList;
292}
293
294void
295Path::__updateBuffer()
296{
297    Rp_ChainLink *l = NULL;
298    size_t len = 0;
299
300    b.clear();
301
302    l = Rp_ChainFirstLink(_pathList);
303    while (l != NULL) {
304
305        if (b.nmemb() != 0) {
306            b.append(&_ifs,1);
307        }
308
309        componentStruct *c = (componentStruct *) Rp_ChainGetValue(l);
310
311        if (c->type != NULL) {
312            len = strlen(c->type);
313            if (len > 0) {
314                b.append(c->type,len);
315            }
316        }
317
318        if (c->degree > 1) {
319            b.appendf("%i",c->degree);
320        }
321
322        if (c->id != NULL) {
323            b.append("(",1);
324            len = strlen(c->id);
325            if (len > 0) {
326                b.append(c->id,len);
327            }
328            b.append(")",1);
329        }
330
331        l = Rp_ChainNextLink(l);
332    }
333
334    b.append("\0",1);
335}
336
337const char *
338Path::ifs (const char *el)
339{
340    // ifs cannot be NULL, \0, (, or )
341
342    if ((el != NULL) &&
343        (*el != '\0') &&
344        (*el != '(') &&
345        (*el != ')')) {
346        _ifs = *el;
347        __updateBuffer();
348    }
349
350    return (const char *)&_ifs;
351}
352
353Path&
354Path::add (const char *el)
355{
356    Rp_Chain *addList = NULL;
357    Rp_ChainLink *tmpLink = NULL;
358
359    addList = __parse(el);
360    tmpLink = Rp_ChainLastLink(addList);
361    Rp_ChainInsertChainAfter(_pathList,addList,_currLink);
362    _currLink = tmpLink;
363
364    __updateBuffer();
365
366    return *this;
367}
368
369Path&
370Path::del ()
371{
372    Rp_ChainLink *l = NULL;
373
374    // l = Rp_ChainLastLink(_pathList);
375
376    if (_currLink != NULL) {
377        l = _currLink;
378    } else {
379        l = Rp_ChainLastLink(_pathList);
380    }
381
382    if (l != NULL) {
383        componentStruct *c = (componentStruct *) Rp_ChainGetValue(l);
384        __deleteComponent(c);
385        c = NULL;
386        _currLink = Rp_ChainPrevLink(l);
387        if (_currLink == NULL) {
388            // don't go off the beginning of the list
389            _currLink = Rp_ChainNextLink(l);
390            // if _currLink is still NULL, it means the list is blank
391        }
392        Rp_ChainDeleteLink(_pathList,l);
393    }
394
395    __updateBuffer();
396
397    return *this;
398}
399
400bool
401Path::eof ()
402{
403    return (_currLink == NULL);
404}
405
406Path&
407Path::first ()
408{
409    _currLink = Rp_ChainFirstLink(_pathList);
410    return *this;
411}
412
413Path&
414Path::prev ()
415{
416    if (_currLink) {
417        _currLink = Rp_ChainPrevLink(_currLink);
418    }
419    return *this;
420}
421
422Path&
423Path::next ()
424{
425    if (_currLink) {
426        _currLink = Rp_ChainNextLink(_currLink);
427    }
428    return *this;
429}
430
431Path&
432Path::last ()
433{
434    _currLink = Rp_ChainLastLink(_pathList);
435    return *this;
436}
437
438Path&
439Path::clear ()
440{
441    __pathFree();
442    __pathInit();
443    return *this;
444}
445
446size_t
447Path::count ()
448{
449    return Rp_ChainGetLength(_pathList);
450}
451
452const char *
453Path::component(void)
454{
455    Rp_ChainLink *l = NULL;
456
457    if (_pathList == NULL) {
458        return NULL;
459    }
460
461    l = _currLink;
462
463    if (l == NULL) {
464        return NULL;
465    }
466
467    componentStruct *c = (componentStruct *) Rp_ChainGetValue(l);
468
469    if (c == NULL) {
470        return NULL;
471    }
472
473    tmpBuf.clear();
474
475    if (c->type != NULL) {
476        tmpBuf.appendf("%s",c->type);
477    }
478
479    if (c->degree != 1) {
480        tmpBuf.appendf("%zu",c->degree);
481    }
482
483    if (c->id != NULL) {
484        tmpBuf.appendf("(%s)",c->id);
485    }
486
487    // incase none of the above if statements are hit.
488    tmpBuf.append("\0",1);
489
490    return tmpBuf.bytes();
491}
492
493// if you give more than a component,
494// we only take the last component
495// the whole component (type and id)
496// are used to replace the current component.
497
498void
499Path::component(const char *p)
500{
501    if (p == NULL) {
502        return;
503    }
504
505    Rp_Chain *addList = NULL;
506    Rp_ChainLink *l = NULL;
507
508    addList = __parse(p);
509
510    if (addList == NULL) {
511        // nothing to add
512        return;
513    }
514
515    // get the last component of addList
516    l = Rp_ChainLastLink(addList);
517
518    if (l == NULL) {
519        // nothing to add
520        Rp_ChainDestroy(addList);
521        return;
522    }
523
524    componentStruct *aLcomp = (componentStruct *) Rp_ChainGetValue(l);
525    Rp_ChainDeleteLink(_pathList,l);
526
527    // delete addList
528    l = Rp_ChainFirstLink(addList);
529    while (l != NULL) {
530        componentStruct *c = (componentStruct *) Rp_ChainGetValue(l);
531        delete c;
532        l = Rp_ChainNextLink(l);
533    }
534    Rp_ChainDestroy(addList);
535
536    // swap the current component from the current path
537    // with the component from the provided path.
538    l = _currLink;
539
540    if (l != NULL) {
541        componentStruct *last = (componentStruct *) Rp_ChainGetValue(l);
542        delete last;
543        Rp_ChainSetValue(l,aLcomp);
544    } else {
545        // add a new component as a link
546        // point _currLink to the new component
547        _currLink = Rp_ChainAppend(_pathList,aLcomp);
548    }
549
550    __updateBuffer();
551
552    return;
553}
554
555const char *
556Path::id()
557{
558    Rp_ChainLink *l = NULL;
559
560    if (_pathList == NULL) {
561        return NULL;
562    }
563
564    l = _currLink;
565
566    if (l == NULL) {
567        return NULL;
568    }
569
570    componentStruct *c = (componentStruct *) Rp_ChainGetValue(l);
571
572    if (c == NULL) {
573        return NULL;
574    }
575
576    return c->id;
577}
578
579void
580Path::id(const char *p)
581{
582    if (p == NULL) {
583        return;
584    }
585
586    Rp_ChainLink *l = NULL;
587    char *tmp = NULL;
588    size_t len = strlen(p);
589    componentStruct *last = NULL;
590
591    // update the current component from the current path
592    l = _currLink;
593
594    if (l != NULL) {
595        last = (componentStruct *) Rp_ChainGetValue(l);
596        delete[] last->id;
597    } else {
598        // append the new component onto the current path
599        last = new componentStruct;
600        _currLink = Rp_ChainAppend(_pathList,last);
601        tmp = new char[1];
602        *tmp = '\0';
603        last->type = tmp;
604    }
605
606    // adjust the id field
607    tmp = new char[len+1];
608    strncpy(tmp,p,len+1);
609    last->id = tmp;
610
611    __updateBuffer();
612
613    return;
614}
615
616const char *
617Path::type()
618{
619    Rp_ChainLink *l = NULL;
620
621    if (_pathList == NULL) {
622        return NULL;
623    }
624
625    l = _currLink;
626
627    if (l == NULL) {
628        return NULL;
629    }
630
631    componentStruct *c = (componentStruct *) Rp_ChainGetValue(l);
632
633    if (c == NULL) {
634        return NULL;
635    }
636
637    return c->type;
638}
639
640void
641Path::type(const char *p)
642{
643    if (p == NULL) {
644        return;
645    }
646
647    Rp_ChainLink *l = NULL;
648    char *tmp = NULL;
649    size_t len = strlen(p);
650    componentStruct *last = NULL;
651
652    // update the last component from the current path
653    l = _currLink;
654
655    if (l != NULL) {
656        last = (componentStruct *) Rp_ChainGetValue(l);
657        delete[] last->type;
658    } else {
659        // append the new component onto the current path
660        last = new componentStruct;
661        _currLink = Rp_ChainAppend(_pathList,last);
662        last->id = NULL;
663    }
664
665    // adjust the type field
666    tmp = new char[len+1];
667    strncpy(tmp,p,len+1);
668    last->type = tmp;
669
670    __updateBuffer();
671
672    return;
673}
674
675const char *
676Path::parent()
677{
678    Rp_ChainLink *l = NULL;
679    Rp_ChainLink *last = NULL;
680
681    tmpBuf.clear();
682
683    last = _currLink;
684    l = Rp_ChainFirstLink(_pathList);
685    while (l != last) {
686
687        if (tmpBuf.nmemb() != 0) {
688            tmpBuf.append(&_ifs,1);
689        }
690
691        componentStruct *c = (componentStruct *) Rp_ChainGetValue(l);
692
693        if (c->type) {
694            tmpBuf.append(c->type);
695        }
696
697        if (c->id) {
698            tmpBuf.append("(",1);
699            tmpBuf.append(c->id);
700            tmpBuf.append(")",1);
701        }
702
703        // what happens if type and id are both null?
704
705        l = Rp_ChainNextLink(l);
706    }
707
708    tmpBuf.append("\0",1);
709
710    return tmpBuf.bytes();
711}
712
713void
714Path::parent(const char *p)
715{
716    Rp_Chain *addList = NULL;
717    Rp_ChainLink *tmpLink = NULL;
718
719    addList = __parse(p);
720    // maybe make this an input flag, FirstLink (default) or LastLink
721    tmpLink = Rp_ChainFirstLink(addList);
722    Rp_ChainInsertChainBefore(_pathList,addList,_currLink);
723    _currLink = tmpLink;
724
725    __updateBuffer();
726
727    return;
728}
729
730size_t
731Path::degree()
732{
733    Rp_ChainLink *l = NULL;
734
735    l = _currLink;
736
737    if (l == NULL) {
738        return 0;
739    }
740
741    componentStruct *c = (componentStruct *) Rp_ChainGetValue(l);
742
743    if (c == NULL) {
744        return 0;
745    }
746
747    return c->degree;
748}
749
750void
751Path::degree(size_t d)
752{
753    if (d == 0) {
754        d = 1;
755    }
756
757    Rp_ChainLink *l = NULL;
758    componentStruct *last = NULL;
759
760    // update the current component from the current path
761    l = _currLink;
762
763    if (l != NULL) {
764        last = (componentStruct *) Rp_ChainGetValue(l);
765    } else {
766        // append the new component onto the current path
767        last = new componentStruct;
768        _currLink = Rp_ChainAppend(_pathList,last);
769    }
770
771    // adjust the degree field
772    last->degree = d;
773
774    __updateBuffer();
775
776    return;
777}
778
779const char *
780Path::path(void)
781{
782    return b.bytes();
783}
784
785void
786Path::path(const char *p)
787{
788    if (p != NULL) {
789        _pathList = __parse(p);
790        _currLink = Rp_ChainLastLink(_pathList);
791        __updateBuffer();
792    }
793    return;
794}
795
796#ifdef __cplusplus
797    }
798#endif // ifdef __cplusplus
799
Note: See TracBrowser for help on using the repository browser.