File indexing completed on 2024-12-08 12:27:57

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