File indexing completed on 2024-04-21 15:08:25

0001 //
0002 // C++ Implementation: cValue, cValueList
0003 //
0004 /*
0005 Copyright 2005-2011 Tomas Mecir <kmuddy@kmuddy.com>
0006 
0007 This program is free software; you can redistribute it and/or
0008 modify it under the terms of the GNU General Public License as
0009 published by the Free Software Foundation; either version 2 of 
0010 the License, or (at your option) any later version.
0011 
0012 This program is distributed in the hope that it will be useful,
0013 but WITHOUT ANY WARRANTY; without even the implied warranty of
0014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015 GNU General Public License for more details.
0016 
0017 You should have received a copy of the GNU General Public License
0018 along with this program.  If not, see <http://www.gnu.org/licenses/>.
0019 */
0020 
0021 #include "cvalue.h"
0022 
0023 #include <QStringList>
0024 #include <QXmlStreamReader>
0025 #include <QXmlStreamWriter>
0026 
0027 #include <map>
0028 #include <set>
0029 
0030 using namespace std;
0031 
0032 class cValueData {
0033  private:
0034   cValueData ();
0035   ~cValueData ();
0036   /** copy the value */
0037   void copyTo (cValueData *data);
0038   /** clear data */
0039   void clear ();
0040 
0041   enum ValueType { ValueNone, ValueString, ValueInt, ValueDouble, ValueArray, ValueList, ValueMarker };
0042   
0043   /** return type of currently held value */
0044   ValueType valueType () const { return valType; };
0045   bool isEmpty() const { return (valType==ValueNone); };
0046   bool isString() const { return (valType==ValueString); };
0047   bool isInteger() const { return (valType==ValueInt); };
0048   bool isDouble() const { return (valType==ValueDouble); };
0049   bool isArray() const { return (valType==ValueArray); };
0050   bool isList() const { return (valType==ValueList); };
0051   bool isMarker() const { return (valType==ValueMarker); };
0052 
0053   QString asString () const;
0054   int asInteger () const;
0055   double asDouble () const;
0056 
0057   QString listJoin (const QString &sep) const;
0058 
0059   int usage;
0060   ValueType valType;
0061   union {
0062     int int_val;
0063     double dbl_val;
0064   };
0065   // these cannot be in the union:
0066   map<int, QString> array_val;
0067   set<QString> list_val;
0068   QString str_val;
0069   
0070   /** no implementation here - we don't want to create copies */
0071   cValueData (const cValueData &val);
0072   /** no implementation here - we don't want to create copies */
0073   cValueData &operator= (const cValueData &val);
0074   
0075   friend class cValue;
0076 };
0077 
0078 cValueData::cValueData ()
0079 {
0080   usage = 1;
0081   valType = ValueNone;
0082 }
0083 
0084 cValueData::~cValueData ()
0085 {
0086   clear ();
0087 }
0088 
0089 void cValueData::clear ()
0090 {
0091   valType = ValueNone;
0092   str_val = QString();
0093   list_val.clear ();
0094   array_val.clear ();
0095 }
0096 
0097 
0098 void cValueData::copyTo (cValueData *data)
0099 {
0100   if (data == this) return;
0101   
0102   data->clear ();
0103   data->valType = valType;
0104   switch (valType) {
0105     case ValueNone:
0106     case ValueMarker:
0107     break;
0108     case ValueString:
0109       data->str_val = str_val;
0110     break;
0111     case ValueInt:
0112       data->int_val = int_val;
0113     break;
0114     case ValueDouble:
0115       data->dbl_val = dbl_val;
0116     break;
0117     case ValueArray:
0118       data->array_val = array_val;
0119     break;
0120     case ValueList:
0121       data->list_val = list_val;
0122     break;
0123   };
0124 }
0125 
0126 QString cValueData::asString () const
0127 {
0128   QString ret = QString();
0129   map<int, QString>::const_iterator ita;
0130   set<QString>::const_iterator itl;
0131   switch (valType) {
0132     case ValueNone:
0133     case ValueMarker:
0134       ret = QString();
0135     break;
0136     case ValueString:
0137       ret = str_val;
0138     break;
0139     case ValueInt:
0140       ret.setNum (int_val);
0141     break;
0142     case ValueDouble:
0143       ret.setNum (dbl_val);
0144     break;
0145     case ValueArray:
0146     case ValueList:
0147       ret = listJoin ("|");
0148     break;
0149   }
0150   return ret;
0151 }
0152 
0153 int cValueData::asInteger () const
0154 {
0155   bool ok = false;
0156   int ret = 0;
0157   switch (valType) {
0158     case ValueNone:
0159     case ValueMarker:
0160       ret = 0;
0161       break;
0162     case ValueString:
0163       ret = str_val.toInt (&ok);
0164       if (!ok) ret = 0;
0165       break;
0166     case ValueInt:
0167       ret = int_val;
0168       break;
0169     case ValueDouble:
0170       ret = (int) dbl_val;  //should auto-trim digits after decimal separator
0171       break;
0172     case ValueArray:
0173       ret = array_val.size();
0174       break;
0175     case ValueList:
0176       ret = list_val.size();
0177       break;
0178   }
0179   return ret;
0180 }
0181 
0182 double cValueData::asDouble () const
0183 {
0184   double ret = 0.0;
0185   bool ok;
0186   switch (valType) {
0187     case ValueNone:
0188     case ValueMarker:
0189       ret = 0.0;
0190       break;
0191     case ValueString:
0192       ret = str_val.toDouble (&ok);
0193       if (!ok) ret = 0.0;
0194       break;
0195     case ValueInt:
0196       ret = (double) int_val;
0197       break;
0198     case ValueDouble:
0199       ret = dbl_val;
0200       break;
0201     case ValueArray:
0202       ret = array_val.size();
0203       break;
0204     case ValueList:
0205       ret = list_val.size();
0206       break;
0207   }
0208   return ret;
0209 }
0210 
0211 QString cValueData::listJoin (const QString &sep) const
0212 {
0213   QString ret;
0214   map<int, QString>::const_iterator ita;
0215   set<QString>::const_iterator itl;
0216   if (valType == ValueArray) {
0217     ita = array_val.begin();
0218     if (ita == array_val.end())  // empty array ?
0219       return ret;;
0220     ret = ita->second;
0221     ++ita;
0222     for (; ita != array_val.end(); ++ita) ret += sep + ita->second;
0223   }
0224   if (valType == ValueList) {
0225     itl = list_val.begin();
0226     if (itl == list_val.end())  // empty list ?
0227       return ret;
0228     ret = (*itl);
0229     ++itl;
0230     for (; itl != list_val.end(); ++itl) ret += sep + (*itl);
0231   }
0232   return ret;
0233 }
0234 
0235 
0236 // *********************************
0237 // cValue implementation starts here
0238 // *********************************
0239 
0240 cValue cValue::_empty;
0241 
0242 cValue::cValue ()
0243 {
0244   d = nullptr;
0245 }
0246 
0247 cValue::cValue (const cValue &val)
0248 {
0249   d = val.d;
0250   if (d) d->usage++;
0251 }
0252 
0253 cValue::cValue (const QString &val)
0254 {
0255   d = nullptr;
0256   setValue (val);
0257 }
0258 
0259 cValue::cValue (int val)
0260 {
0261   d = nullptr;
0262   setValue (val);
0263 }
0264 
0265 cValue::cValue (double val)
0266 {
0267   d = nullptr;
0268   setValue (val);
0269 }
0270 
0271 cValue::cValue (bool val)
0272 {
0273   d = nullptr;
0274   setValue (val ? 1 : 0);
0275 }
0276 
0277 cValue::~cValue ()
0278 {
0279   // detach from value, deleting it if needed
0280   detachValue ();
0281 }
0282 
0283 QString cValue::load (QXmlStreamReader *reader)
0284 {
0285   reader->readNext ();
0286   if (!reader->isStartElement ()) return QString();  // something is wrong
0287   if (reader->name() != "variable") return QString();  // likewise
0288 
0289   int type = reader->attributes().value ("type").toString().toInt();
0290   if (type < 0) type = 0;
0291   if (type > 4) type = 0;
0292   QString name = reader->attributes().value ("name").toString();
0293   QString value = reader->attributes().value ("value").toString();
0294   switch (type) {
0295     case 0: // string
0296       setValue (value);
0297     break;
0298     case 1: // integer
0299       setValue (value.toInt());
0300     break;
0301     case 2: // double
0302       setValue (value.toDouble());
0303     break;
0304     case 3: // array
0305       while (!reader->atEnd()) {
0306         reader->readNext ();
0307         // are we at the end of this section ?
0308         if (reader->isEndElement() && (reader->name() == "value")) break;
0309 
0310         if (!reader->isStartElement()) continue;
0311         if (reader->name() != "element") continue;
0312         int index = reader->attributes().value ("index").toString().toInt();
0313         QString v = reader->attributes().value ("value").toString();
0314         setItem (index, v);
0315         reader->readNext();  // read the end element
0316       }
0317     break;
0318     case 4: // list
0319       while (!reader->atEnd()) {
0320         reader->readNext ();
0321         // are we at the end of this section ?
0322         if (reader->isEndElement() && (reader->name() == "value")) break;
0323 
0324         if (!reader->isStartElement()) continue;
0325         if (reader->name() != "element") continue;
0326 
0327         QString v = reader->attributes().value ("value").toString();
0328         addToList (v);
0329         reader->readNext();  // read the end element
0330       }
0331     break;
0332   }
0333   if (type <= 2) reader->readNext();  // for types other than list/array, we must read the closing element
0334 
0335   return name;
0336 }
0337 
0338 void cValue::save (QXmlStreamWriter *writer, const QString &name)
0339 {
0340   writer->writeStartElement ("variable");
0341   if (!name.isEmpty()) writer->writeAttribute ("name", name);
0342   
0343   int type = 0;
0344   if (d) switch (d->valType) {
0345     case cValueData::ValueNone: type = 0; break;
0346     // markers are only used in function evaluation, no need to save them
0347     case cValueData::ValueMarker: type = 0; break;
0348     case cValueData::ValueString: type = 0; break;
0349     case cValueData::ValueInt: type = 1; break;
0350     case cValueData::ValueDouble: type = 2; break;
0351     case cValueData::ValueArray: type = 3; break;
0352     case cValueData::ValueList: type = 4; break;
0353     default: type = 0; break;  // should never happen
0354   };
0355   writer->writeAttribute ("type", QString::number (type));
0356   map<int, QString>::iterator it1;
0357   set<QString>::iterator it2;
0358   switch (type) {
0359     case 0: {  // string
0360       writer->writeAttribute ("value", asString());
0361     } break;
0362     case 1: {  // integer
0363       writer->writeAttribute ("value", QString::number (asInteger()));
0364     } break;
0365     case 2: {  // double
0366       writer->writeAttribute ("value", QString::number (asDouble(), 'g', 15));
0367     } break;
0368     case 3: {  // array
0369       for (it1 = d->array_val.begin(); it1 != d->array_val.end(); ++it1) {
0370         writer->writeStartElement ("element");
0371         writer->writeAttribute ("index", QString::number (it1->first));
0372         writer->writeAttribute ("value", it1->second);
0373         writer->writeEndElement ();
0374       }
0375     } break;
0376     case 4: {  // list
0377       for (it2 = d->list_val.begin(); it2 != d->list_val.end(); ++it2) {
0378         writer->writeStartElement ("element");
0379         writer->writeAttribute ("value", *it2);
0380         writer->writeEndElement ();
0381       }
0382     } break;
0383   }
0384   writer->writeEndElement ();
0385 }
0386 
0387 cValue &cValue::operator= (const cValue &a)
0388 {
0389   // handle the a=a; type of assignments correctly
0390   if (a.d == d) return *this;
0391   
0392   detachValue ();
0393   d = a.d;
0394   if (d) d->usage++;
0395   return *this;
0396 }
0397 
0398 void cValue::setValue (const cValue &val)
0399 {
0400   operator= (val);
0401 }
0402 
0403 void cValue::setValue ()
0404 {
0405   detachValue ();
0406   d = nullptr;
0407 }
0408 
0409 void cValue::setValue (const QString &val)
0410 {
0411   // first of all, try to convert the string to a number ...
0412   bool ok = false;
0413   double value = val.toDouble (&ok);
0414   if (ok) {
0415     // success - set the value as a number
0416     setValue (value);
0417     return;
0418   }  
0419   detachValue ();
0420   d = new cValueData;
0421   d->valType = cValueData::ValueString;
0422   d->str_val = val;
0423 }
0424 
0425 void cValue::setValue (int val)
0426 {
0427   detachValue ();
0428   d = new cValueData;
0429   d->valType = cValueData::ValueInt;
0430   d->int_val = val;
0431 }
0432 
0433 void cValue::setValue (double val)
0434 {
0435   detachValue ();
0436   d = new cValueData;
0437   d->valType = cValueData::ValueDouble;
0438   d->dbl_val = val;
0439 }
0440 
0441 void cValue::setAsMarker ()
0442 {
0443   detachValue ();
0444   d = new cValueData;
0445   d->valType = cValueData::ValueMarker;
0446 }
0447 
0448 bool cValue::isEmpty() const
0449 {
0450   if (d) return d->isEmpty();
0451   return true;
0452 }
0453 
0454 bool cValue::isString() const
0455 {
0456   if (d) return d->isString();
0457   return false;
0458 }
0459 
0460 bool cValue::isInteger() const
0461 {
0462   if (d) return d->isInteger();
0463   return false;
0464 }
0465 
0466 bool cValue::isDouble() const
0467 {
0468   if (d) return d->isDouble();
0469   return false;
0470 }
0471 
0472 bool cValue::isArray() const
0473 {
0474   if (d) return d->isArray();
0475   return false;
0476 }
0477 
0478 bool cValue::isList() const
0479 {
0480   if (d) return d->isList();
0481   return false;
0482 }
0483 
0484 bool cValue::isMarker() const
0485 {
0486   if (d) return d->isMarker();
0487   return false;
0488 }
0489 
0490 cValue cValue::operator[] (int index) const
0491 {
0492   return item (index);
0493 }
0494 
0495 void cValue::setItem (int index, const QString &value)
0496 {
0497   if (!isArray()) {
0498     detachValue ();  // not an array - get rid of the old value
0499     d = new cValueData;
0500     d->valType = cValueData::ValueArray;
0501   } else
0502     removeItem (index);
0503   d->array_val[index] = value;
0504 }
0505 
0506 void cValue::removeItem (int index)
0507 {
0508   if (!isArray()) return;
0509   if (d->array_val.count (index) != 0)
0510     d->array_val.erase (index);
0511 }
0512 
0513 void cValue::addToList (const QString &item)
0514 {
0515   if (!isList()) {
0516     detachValue ();  // not a list - get rid of the old value
0517     d = new cValueData;
0518     d->valType = cValueData::ValueList;
0519   }
0520   d->list_val.insert (item);
0521 }
0522 
0523 void cValue::removeFromList (const QString &item)
0524 {
0525   if (!isList()) return;
0526   d->list_val.erase (item);
0527 }
0528 
0529 bool cValue::listContains (const QString &item)
0530 {
0531   if (!isList()) return false;
0532   return (d->list_val.count(item) != 0);
0533 }
0534 
0535 
0536 QString cValue::asString () const
0537 {
0538   if (d) return d->asString ();
0539   return QString();
0540 }
0541 
0542 int cValue::asInteger () const
0543 {
0544   if (d) return d->asInteger ();
0545   return 0;
0546 }
0547 
0548 double cValue::asDouble () const
0549 {
0550   if (d) return d->asDouble ();
0551   return 0.0;
0552 }
0553 
0554 QString cValue::item (int index) const
0555 {
0556   if (!isArray()) return QString();
0557   if (d->array_val.count (index) != 0)
0558     return d->array_val[index];
0559   return QString();
0560 }
0561 
0562 bool cValue::contains (const QString &item) const
0563 {
0564   if (!isList()) return false;
0565   return (d->list_val.count (item) != 0);
0566 }
0567 
0568 int cValue::size () const
0569 {
0570   if (isArray ())
0571     return d->array_val.size();
0572   if (isList ())
0573     return d->list_val.size();
0574   if (isEmpty ())
0575     return 0;
0576   return 1;
0577 }
0578 
0579 QString cValue::listJoin (const QString &sep) const
0580 {
0581   if ((!isList()) && (!isArray())) return QString();
0582   return d->listJoin (sep);
0583 }
0584 
0585 cValue cValue::toList (const QString &sep) const
0586 {
0587   QStringList list = asString().split (sep);
0588   QStringList::iterator it;
0589   cValue val;
0590   for (it = list.begin(); it != list.end(); ++it)
0591     val.addToList (*it);
0592   return val;
0593 }
0594 
0595 void cValue::detachValue ()
0596 {
0597   if (!d) return;
0598   if (d->usage > 0) d->usage--;
0599   if (d->usage == 0) delete d;
0600   d = nullptr;
0601 }
0602 
0603 void cValue::unique ()
0604 {
0605   if (!d) return;
0606   if (d->usage <= 1) return;
0607   cValueData *vd = new cValueData;
0608   d->copyTo (vd);
0609   detachValue ();
0610   d = vd;
0611 }
0612 
0613 cValue operator+ (const cValue &a, const cValue &b)
0614 {
0615   if (a.isInteger() && b.isInteger())
0616     return cValue (a.asInteger() + b.asInteger());
0617   return cValue (a.asDouble() + b.asDouble());
0618 }
0619 
0620 cValue operator- (const cValue &a, const cValue &b)
0621 {
0622   if (a.isInteger() && b.isInteger())
0623     return cValue (a.asInteger() - b.asInteger());
0624   return cValue (a.asDouble() - b.asDouble());
0625 }
0626 
0627 cValue operator* (const cValue &a, const cValue &b)
0628 {
0629   return cValue (a.asDouble() * b.asDouble());
0630 }
0631 
0632 cValue operator/ (const cValue &a, const cValue &b)
0633 {
0634   double bb = b.asDouble ();
0635   if (bb != 0)
0636     return cValue (a.asDouble() / bb);
0637   return cValue (0);
0638 }
0639