source: branches/1.7/src/objects/RpNumber.cc @ 6313

Last change on this file since 6313 was 5679, checked in by ldelgass, 9 years ago

Full merge 1.3 branch to uq branch to sync. Fixed partial subdirectory merge
by removing mergeinfo from lang/python/Rappture directory.

  • Property svn:eol-style set to native
File size: 18.0 KB
Line 
1/*
2 * ----------------------------------------------------------------------
3 *  Rappture 2.0 Number Object Source
4 *
5 * ======================================================================
6 *  AUTHOR:  Derrick Kearney, Purdue University
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 "RpNumber.h"
15#include "RpUnits.h"
16#include "RpSimpleBuffer.h"
17#include "RpUnitsCInterface.h"
18#include "RpPath.h"
19
20using namespace Rappture;
21
22Number::Number()
23   : Object (),
24     _minSet (0),
25     _maxSet (0),
26     _defSet (0),
27     _curSet (0),
28     _presets (NULL)
29{
30    // FIXME: empty names should be autoname'd
31    this->name("");
32    this->path("run");
33    this->label("");
34    this->desc("");
35    this->def(0.0);
36    this->cur(0.0);
37    this->min(0.0);
38    this->max(0.0);
39    // FIXME: empty units should be set to the None unit
40    // this->units(units);
41}
42
43Number::Number(const char *name, const char *units, double val)
44   : Object (),
45     _minSet (0),
46     _maxSet (0),
47     _defSet (0),
48     _curSet (0),
49     _presets (NULL)
50{
51    const RpUnits *u = NULL;
52
53    this->name(name);
54    this->path("run");
55    this->label("");
56    this->desc("");
57    this->def(val);
58    this->cur(val);
59    this->min(0.0);
60    this->max(0.0);
61
62    u = RpUnits::find(std::string(units));
63    if (!u) {
64        u = RpUnits::define(units,NULL);
65    }
66    this->units(units);
67}
68
69Number::Number(const char *name, const char *units, double val,
70               double min, double max, const char *label,
71               const char *desc)
72    : Object (),
73      _minSet (0),
74      _maxSet (0),
75      _defSet (0),
76      _curSet (0),
77      _presets (NULL)
78{
79    const RpUnits *u = NULL;
80
81    this->name(name);
82    this->path("run");
83    this->label(label);
84    this->desc(desc);
85    this->def(val);
86    this->cur(val);
87    this->min(min);
88    this->max(max);
89
90    u = RpUnits::find(std::string(units));
91    if (! u) {
92        u = RpUnits::define(units,NULL);
93    }
94    this->units(units);
95
96    if ((min == 0) && (max == 0)) {
97        _minSet = 0;
98        _maxSet = 0;
99    }
100    else {
101
102        _minSet = 1;
103        if (min > val) {
104            this->min(val);
105        }
106
107        _maxSet = 1;
108        if (max < val) {
109            this->max(val);
110        }
111    }
112}
113
114// copy constructor
115Number::Number ( const Number& o )
116    : Object (o),
117      _minSet (o._minSet),
118      _maxSet (o._maxSet),
119      _defSet (o._defSet),
120      _curSet (o._curSet)
121{
122    this->def(o.def());
123    this->cur(o.cur());
124    this->min(o.min());
125    this->max(o.max());
126    this->units(o.units());
127
128    // FIXME: need to copy _presets
129}
130
131// default destructor
132Number::~Number ()
133{
134    // clean up dynamic memory
135}
136
137const char *
138Number::units(void) const
139{
140    return propstr("units");
141}
142
143void
144Number::units(const char *p)
145{
146    propstr("units",p);
147}
148
149int
150Number::minset() const
151{
152    return _minSet;
153}
154
155int
156Number::maxset() const
157{
158    return _maxSet;
159}
160
161int
162Number::defset() const
163{
164    return _defSet;
165}
166
167int
168Number::curset() const
169{
170    return _curSet;
171}
172
173/**********************************************************************/
174// METHOD: convert()
175/// Convert the number object to another unit from string
176/**
177 * Store the result as the currentValue.
178 */
179
180Outcome&
181Number::convert(const char *to)
182{
183    const RpUnits* toUnit = NULL;
184    const RpUnits* fromUnit = NULL;
185
186    _status.addContext("Rappture::Number::convert");
187
188    if (to == NULL) {
189        return _status;
190    }
191
192    if (strcmp(units(),to) == 0) {
193        return _status;
194    }
195
196    // make sure all units functions accept char*'s
197    toUnit = RpUnits::find(std::string(to));
198    if (!toUnit) {
199        _status.addError("conversion not defined, unit \"%s\" does not exist",to);
200        return _status;
201    }
202
203    fromUnit = RpUnits::find(std::string(units()));
204    if (!fromUnit) {
205        _status.addError("conversion not defined, unit \"%s\" does not exist",to);
206        return _status;
207    }
208
209    // perform the conversion
210    int err = 0;
211    double convertedVal;
212
213    convertedVal = fromUnit->convert(toUnit,def(), &err);
214    if (err) {
215        _status.addError("undefined error while converting %s to %s",
216            (fromUnit->getUnitsName()).c_str(),
217            (toUnit->getUnitsName()).c_str());
218    } else {
219        def(convertedVal);
220    }
221
222    convertedVal = fromUnit->convert(toUnit,cur(), &err);
223    if (err) {
224        _status.addError("undefined error while converting %s to %s",
225            (fromUnit->getUnitsName()).c_str(),
226            (toUnit->getUnitsName()).c_str());
227    } else {
228        cur(convertedVal);
229    }
230
231    if (_minSet) {
232        convertedVal = fromUnit->convert(toUnit,min(), &err);
233        if (err) {
234            _status.addError("undefined error while converting %s to %s",
235                (fromUnit->getUnitsName()).c_str(),
236                (toUnit->getUnitsName()).c_str());
237        } else {
238            min(convertedVal);
239        }
240    }
241
242    if (_maxSet) {
243        convertedVal = fromUnit->convert(toUnit,max(), &err);
244        if (err) {
245            _status.addError("undefined error while converting %s to %s",
246                (fromUnit->getUnitsName()).c_str(),
247                (toUnit->getUnitsName()).c_str());
248        } else {
249            max(convertedVal);
250        }
251    }
252    return _status;
253}
254
255/**********************************************************************/
256// METHOD: value()
257/// Get the value of this object converted to specified units
258/**
259 * does not change the value of the object
260 * error code is set on trouble
261 */
262
263double
264Number::value(const char *to) const
265{
266    const RpUnits* toUnit = NULL;
267    const RpUnits* fromUnit = NULL;
268
269    _status.addContext("Rappture::Number::value");
270
271    double val = 0.0;
272
273    if (_defSet) {
274        val = def();
275    }
276
277    if (_curSet) {
278        val = cur();
279    }
280
281    if (to == NULL) {
282        return val;
283    }
284
285    if (strcmp(units(),to) == 0) {
286        return val;
287    }
288
289    // make sure all units functions accept char*'s
290    toUnit = RpUnits::find(std::string(to));
291    if (!toUnit) {
292        _status.addError("conversion not defined, unit \"%s\" does not exist",to);
293        return val;
294    }
295
296    fromUnit = RpUnits::find(std::string(units()));
297    if (!fromUnit) {
298        _status.addError("conversion not defined, unit \"%s\" does not exist",to);
299        return val;
300    }
301
302    // perform the conversion
303    int err = 0;
304    double convertedVal = fromUnit->convert(toUnit,val, &err);
305    if (err) {
306        _status.addError("undefined error while converting %s to %s",
307            (fromUnit->getUnitsName()).c_str(),
308            (toUnit->getUnitsName()).c_str());
309        return cur();
310    }
311
312    return convertedVal;
313}
314
315/**********************************************************************/
316// METHOD: vvalue()
317/// Get the value of this object based on provided hints
318/**
319 * does not change the value of the object
320 * currently recognized hints:
321 *  units
322 * error code is set on trouble
323 */
324
325void
326Number::vvalue(void *storage, size_t numHints, va_list arg) const
327{
328    char buf[1024];
329    char *hintCopy = NULL;
330    size_t hintLen = 0;
331
332    char *hint = NULL;
333    const char *hintKey = NULL;
334    const char *hintVal = NULL;
335
336    double *ret = (double *) storage;
337
338    *ret = 0.0;
339
340    if (_defSet) {
341        *ret = def();
342    }
343
344    if (_curSet) {
345        *ret = cur();
346    }
347
348    while (numHints > 0) {
349        numHints--;
350        hint = va_arg(arg, char *);
351        hintLen = strlen(hint);
352        if (hintLen < 1024) {
353            hintCopy = buf;
354        } else {
355            // buf too small, allocate some space
356            hintCopy = new char[hintLen];
357        }
358        strcpy(hintCopy,hint);
359
360        // parse the hint into a key and value
361        __hintParser(hintCopy,&hintKey,&hintVal);
362
363        // evaluate the hint key
364        if (('u' == *hintKey) &&
365            (strcmp("units",hintKey) == 0)) {
366            *ret = value(hintVal);
367
368        }
369
370        if (hintCopy != buf) {
371            // clean up memory
372            delete hintCopy;
373        }
374    }
375    return;
376}
377
378/**********************************************************************/
379// METHOD: addPreset()
380/// Add a preset / suggessted value to the object
381/**
382 * Add a preset value to the object. Currently all
383 * labels must be unique.
384 */
385
386Number&
387Number::addPreset(const char *label, const char *desc, double val,
388                  const char *units)
389{
390    preset *p = NULL;
391
392    p = new preset;
393    if (!p) {
394        // raise error and exit
395    }
396
397    p->label(label);
398    p->desc(desc);
399    p->val(val);
400    p->units(units);
401
402    if (_presets == NULL) {
403        _presets = Rp_ChainCreate();
404        if (_presets == NULL) {
405            // raise error and exit
406        }
407    }
408
409    Rp_ChainAppend(_presets,p);
410
411    return *this;
412}
413
414Number&
415Number::addPreset(const char *label, const char *desc, const char *val)
416{
417    double valval = 0.0;
418    const char *valunits = "";
419    char *endptr = NULL;
420    int result = 0;
421
422    std::string vstr = RpUnits::convert(val,"",RPUNITS_UNITS_OFF,&result);
423    if (result) {
424        // probably shouldnt trust this result
425        fprintf(stderr,"error in RpUnits::convert in addPreset\n");
426    }
427    size_t len = vstr.length();
428    valunits = val+len;
429
430    valval = strtod(val,&endptr);
431    if ( (endptr == val) || (endptr != valunits) ) {
432        // error? strtod was not able to find the same
433        // units location as RpUnits::convert
434        fprintf(stderr,"error while parsing units in addPreset\n");
435    }
436
437    return addPreset(label,desc,valval,valunits);
438}
439
440/**********************************************************************/
441// METHOD: delPreset()
442/// Delete a preset / suggessted value from the object
443/**
444 * Delete a preset value from the object.
445 */
446
447Number&
448Number::delPreset(const char *label)
449{
450    if (label == NULL) {
451        return *this;
452    }
453
454    if (_presets == NULL) {
455        return *this;
456    }
457
458    preset *p = NULL;
459    const char *plabel = NULL;
460    Rp_ChainLink *l = NULL;
461
462    // traverse the list looking for the matching preset
463    l = Rp_ChainFirstLink(_presets);
464    while (l != NULL) {
465        p = (preset *) Rp_ChainGetValue(l);
466        plabel = p->label();
467        if ((*label == *plabel) && (strcmp(plabel,label) == 0)) {
468            // we found matching entry, remove it
469            if (p) {
470                delete p;
471                Rp_ChainDeleteLink(_presets,l);
472            }
473            break;
474        }
475    }
476
477
478    return *this;
479}
480
481void
482Number::__configureFromTree(ClientData c)
483{
484    Rp_ParserXml *p = (Rp_ParserXml *)c;
485    if (p == NULL) {
486        // FIXME: setup error
487        return;
488    }
489
490    Rp_TreeNode node = Rp_ParserXmlElement(p,NULL);
491
492    Rappture::Path pathObj(Rp_ParserXmlNodePath(p,node));
493
494    path(pathObj.parent());
495    name(Rp_ParserXmlNodeId(p,node));
496
497    pathObj.clear();
498    pathObj.add("about");
499    pathObj.add("label");
500    label(Rp_ParserXmlGet(p,pathObj.path()));
501    pathObj.type("description");
502    desc(Rp_ParserXmlGet(p,pathObj.path()));
503    units(Rp_ParserXmlGet(p,"units"));
504    minFromStr(Rp_ParserXmlGet(p,"min"));
505    maxFromStr(Rp_ParserXmlGet(p,"max"));
506    defFromStr(Rp_ParserXmlGet(p,"default"));
507    curFromStr(Rp_ParserXmlGet(p,"current"));
508
509    // collect info about the preset values
510    Rp_Chain *childChain = Rp_ChainCreate();
511    Rp_ParserXmlChildren(p,NULL,"preset",childChain);
512    Rp_ChainLink *l = Rp_ChainFirstLink(childChain);
513    while (l != NULL) {
514        Rp_TreeNode presetNode = (Rp_TreeNode) Rp_ChainGetValue(l);
515        Rp_ParserXmlBaseNode(p,presetNode);
516
517        const char *presetlabel = Rp_ParserXmlGet(p,"label");
518        const char *presetdesc = Rp_ParserXmlGet(p,"description");
519        const char *presetvalue = Rp_ParserXmlGet(p,"value");
520        addPreset(presetlabel,presetdesc,presetvalue);
521
522
523        l = Rp_ChainNextLink(l);
524    }
525
526    Rp_ChainDestroy(childChain);
527
528    // return the base node to the tree root
529    Rp_ParserXmlBaseNode(p,NULL);
530
531    return;
532}
533
534/**********************************************************************/
535// METHOD: dumpToTree(ClientData p)
536/// dump the object to a Rappture1.1 based tree
537/**
538 * Dump the object to a Rappture1.1 based tree
539 */
540
541void
542Number::__dumpToTree(ClientData c)
543{
544    if (c == NULL) {
545        // FIXME: setup error
546        return;
547    }
548
549    Rp_ParserXml *parser = (Rp_ParserXml *)c;
550
551    Path p;
552
553    p.parent(path());
554    p.last();
555
556    p.add("number");
557    p.id(name());
558
559    p.add("about");
560
561    p.add("label");
562    Rp_ParserXmlPutF(parser,p.path(),"%s",label());
563
564    p.type("description");
565    Rp_ParserXmlPutF(parser,p.path(),"%s",desc());
566
567    p.del();
568    p.type("units");
569    Rp_ParserXmlPutF(parser,p.path(),"%s",units());
570
571
572    if (_minSet) {
573        p.type("min");
574        Rp_ParserXmlPutF(parser,p.path(),"%g%s",min(),units());
575    }
576
577    if (_maxSet) {
578        p.type("max");
579        Rp_ParserXmlPutF(parser,p.path(),"%g%s",max(),units());
580    }
581
582    p.type("default");
583    Rp_ParserXmlPutF(parser,p.path(),"%g%s",def(),units());
584
585    p.type("current");
586    Rp_ParserXmlPutF(parser,p.path(),"%g%s",cur(),units());
587
588    // process presets
589    p.type("preset");
590    p.add("label");
591    Rp_ChainLink *l = Rp_ChainFirstLink(_presets);
592    while (l != NULL) {
593        struct preset *presetObj = (struct preset *) Rp_ChainGetValue(l);
594
595        p.type("label");
596        Rp_ParserXmlPutF(parser,p.path(),"%s",presetObj->label());
597        //p.type("description");
598        //Rp_ParserXmlPutF(parser,p.path(),"%s",presetObj->desc());
599        // p.type("units");
600        // Rp_ParserXmlPutF(parser,p.path(),"%s",presetObj->units());
601        p.type("value");
602        Rp_ParserXmlPutF(parser,p.path(),"%g%s",presetObj->val(),presetObj->units());
603
604        p.prev();
605        p.degree(p.degree()+1);
606        p.next();
607
608        l = Rp_ChainNextLink(l);
609    }
610
611    return;
612}
613
614/**********************************************************************/
615// METHOD: is()
616/// what kind of object is this
617/**
618 * return hex value telling what kind of object this is.
619 */
620
621const int
622Number::is() const
623{
624    // return "numb" in hex
625    return 0x6E756D62;
626}
627
628
629void
630Number::__convertFromString(
631    const char *val,
632    double *ret)
633{
634    if (val == NULL) {
635        return;
636    }
637
638    if (ret == NULL) {
639        return;
640    }
641
642    double numericVal = 0.0;
643    if (units()) {
644        // convert the provided string into
645        // the objects default units
646        int err = 0;
647        std::string strVal;
648        strVal = RpUnits::convert(val,units(),RPUNITS_UNITS_OFF,&err);
649        if (err) {
650            _status.addError("Unknown error while converting units");
651        }
652
653        char *endPtr = NULL;
654        numericVal = strtod(strVal.c_str(),&endPtr);
655        if (endPtr == strVal.c_str()) {
656            // no conversion was performed
657            _status.addError("Could not convert \"%s\" into a number",
658                strVal.c_str());
659        } else if (endPtr == (strVal.c_str()+strVal.length())) {
660            *ret = numericVal;
661        } else {
662            // the whole string could not be converted to a number
663            // signal error?
664            _status.addError("Could not convert \"%s\" of \"%s\"into a number",
665                endPtr, strVal.c_str());
666        }
667    } else {
668        // try to figure out the units
669        // store units and return the numeric value to the user
670        const char *foundUnits = NULL;
671        __valUnitsSplit(val,&numericVal,&foundUnits);
672        units(foundUnits);
673        *ret = numericVal;
674    }
675
676}
677
678void
679Number::__valUnitsSplit(
680    const char *inStr,
681    double *val,
682    const char **units)
683{
684    if (inStr == NULL) {
685        return;
686    }
687
688    if (val == NULL) {
689        return;
690    }
691
692    if (units == NULL) {
693        return;
694    }
695
696    char *endPtr = NULL;
697    *val = strtod(inStr,&endPtr);
698    if (endPtr == inStr) {
699        // no conversion was performed
700        _status.addError("Could not convert \"%s\" into a number", inStr);
701    } else if (endPtr == (inStr+strlen(inStr))) {
702        // the whole string was used in the numeric conversion
703        // set units to NULL
704        *units = NULL;
705    } else {
706        // the whole string could not be converted to a number
707        // we assume the rest of the string are the units
708        *units = endPtr;
709    }
710}
711
712/**********************************************************************/
713// METHOD: minFromStr()
714/// xml helper function to receive min value as a string
715/**
716 * convert string to value and units and store as min
717 */
718
719void
720Number::minFromStr(
721    const char *val)
722{
723    double numericVal = 0;
724
725    if (val == NULL) {
726        return;
727    }
728
729    __convertFromString(val,&numericVal);
730
731    if (!_status) {
732        min(numericVal);
733        _minSet = 1;
734    }
735
736}
737
738/**********************************************************************/
739// METHOD: maxFromStr()
740/// xml helper function to receive max value as a string
741/**
742 * convert string to value and units and store as max
743 */
744
745void
746Number::maxFromStr(
747    const char *val)
748{
749    double numericVal = 0;
750
751    if (val == NULL) {
752        return;
753    }
754
755    __convertFromString(val,&numericVal);
756
757    if (!_status) {
758        max(numericVal);
759        _maxSet = 1;
760    }
761
762}
763
764/**********************************************************************/
765// METHOD: defFromStr()
766/// xml helper function to receive default value as a string
767/**
768 * convert string to value and units and store as default
769 */
770
771void
772Number::defFromStr(
773    const char *val)
774{
775    double numericVal = 0;
776
777    if (val == NULL) {
778        return;
779    }
780
781    __convertFromString(val,&numericVal);
782
783    if (!_status) {
784        def(numericVal);
785        _defSet = 1;
786    }
787
788}
789
790/**********************************************************************/
791// METHOD: currFromStr()
792/// xml helper function to receive current value as a string
793/**
794 * convert string to value and units and store as current
795 */
796
797void
798Number::curFromStr(
799    const char *val)
800{
801    double numericVal = 0;
802
803    if (val == NULL) {
804        return;
805    }
806
807    __convertFromString(val,&numericVal);
808
809    if (!_status) {
810        cur(numericVal);
811        _curSet = 1;
812    }
813
814}
815
816
817
818// -------------------------------------------------------------------- //
819
Note: See TracBrowser for help on using the repository browser.