File indexing completed on 2025-01-12 06:47:31
0001 /*************************************************************************** 0002 cvariablelist.cpp - list of variables 0003 ------------------- 0004 begin : Po sep 8 2003 0005 copyright : (C) 2003-2008 by Tomas Mecir 0006 email : kmuddy@kmuddy.com 0007 ***************************************************************************/ 0008 0009 /*************************************************************************** 0010 * * 0011 * This program is free software; you can redistribute it and/or modify * 0012 * it under the terms of the GNU General Public License as published by * 0013 * the Free Software Foundation; either version 2 of the License, or * 0014 * (at your option) any later version. * 0015 * * 0016 ***************************************************************************/ 0017 0018 #include "cvariablelist.h" 0019 0020 #include "ccmdqueue.h" 0021 #include "cprofilemanager.h" 0022 #include "cvalue.h" 0023 #include "cvariable.h" 0024 0025 #include <KLocalizedString> 0026 0027 #include <QDebug> 0028 #include <QDir> 0029 #include <QFile> 0030 #include <QXmlStreamReader> 0031 #include <QXmlStreamWriter> 0032 0033 using namespace std; 0034 0035 //maximum number of passes when doing recursive variable expansion 0036 0037 #define MAXEXPANDPASSES 10 0038 0039 cVariableList::cVariableList (int sess) 0040 : cActionBase ("variables", sess) 0041 { 0042 load (); 0043 } 0044 0045 cVariableList::~cVariableList () 0046 { 0047 save (); 0048 vars.clear (); 0049 } 0050 0051 QString cVariableList::getValue (const QString &varname) 0052 { 0053 cValue *val = value (varname); 0054 //no such variable -> nothing to do 0055 if (!val) return QString(); 0056 0057 return val->asString(); 0058 } 0059 0060 int cVariableList::getIntValue (const QString &varname) 0061 { 0062 cValue *val = value (varname); 0063 //no such variable -> nothing to do 0064 if (!val) return 0; 0065 0066 return val->asInteger(); 0067 } 0068 0069 cValue *cVariableList::value (const QString &varname, cCmdQueue *queue) 0070 { 0071 if (varname.isEmpty()) return nullptr; 0072 QString vn = varname; 0073 if (varname[0] == '$') 0074 vn = vn.mid(1); 0075 0076 if (queue) { 0077 cValue *value = queue->value (vn); 0078 if (value) 0079 return value; 0080 } 0081 0082 if (!exists (vn)) 0083 return nullptr; 0084 0085 return vars[vn]->getValue(); 0086 } 0087 0088 cValue *cVariableList::valueNotEmpty (const QString &varname, cCmdQueue *queue) 0089 { 0090 cValue *val = value (varname, queue); 0091 if (val) return val; 0092 // doesn't exist - create empty ! 0093 set (varname, QString()); 0094 return value (varname, queue); 0095 } 0096 0097 bool cVariableList::exists (const QString &varname) 0098 { 0099 if (varname.isEmpty()) return false; 0100 QString vn = varname; 0101 if (varname[0] == '$') 0102 vn = vn.mid(1); 0103 return (vars.count (vn) != 0); 0104 } 0105 0106 void cVariableList::set (const QString &varname, const QString &value) 0107 { 0108 cValue v (value); 0109 set (varname, &v); 0110 } 0111 0112 void cVariableList::set (const QString &varname, cValue *value) 0113 { 0114 if (varname.isEmpty()) return; 0115 QString vn = varname; 0116 if (varname[0] == '$') 0117 vn = vn.mid(1); 0118 0119 QString oldval = QString(); 0120 QString newval = value ? value->asString() : QString(); 0121 bool changed = (oldval != newval); 0122 if (exists (vn)) 0123 { 0124 //variable already exists - we just update its value 0125 oldval = vars[vn]->value (); 0126 vars[vn]->setValue (value); 0127 } 0128 else 0129 { 0130 //good, add new variable to the list 0131 cVariable *var = new cVariable; 0132 var->setName (vn); 0133 var->setValue (value); 0134 vars[vn] = var; 0135 changed = true; // if we're adding a new value, always report the change 0136 } 0137 0138 //inform everyone about the change, if needed 0139 if (!changed) return; 0140 invokeEvent ("old-var-value", sess(), vn, oldval); 0141 invokeEvent ("var-changed", sess(), vn, value ? value->asString() : QString()); 0142 } 0143 0144 void cVariableList::unset (const QString &varname) 0145 { 0146 if (varname.isEmpty()) return; 0147 QString vn = varname; 0148 if (varname[0] == '$') 0149 vn = vn.mid(1); 0150 0151 //no such variable -> nothing to do 0152 if (!exists (vn)) 0153 return; 0154 0155 //get the variable 0156 cVariable *var = vars[vn]; 0157 QString oldval = var->value(); 0158 //and remove it 0159 vars.erase (vn); 0160 delete var; 0161 0162 invokeEvent ("old-var-value", sess(), vn, oldval); 0163 invokeEvent ("var-changed", sess(), vn, QString()); 0164 } 0165 0166 void cVariableList::inc (const QString &varname, double delta) 0167 { 0168 if (!exists (varname)) { 0169 cValue val (delta); 0170 set (varname, &val); 0171 } else { 0172 QString vn = varname; 0173 if (varname[0] == '$') 0174 vn = vn.mid(1); 0175 cValue *val = vars[vn]->getValue(); 0176 if (val) { 0177 QString oldval = val->asString(); 0178 val->setValue (val->asDouble() + delta); 0179 // we are bypassing cVariableList::set here, hence we need to invoke 0180 // this event ourselves ... 0181 invokeEvent ("old-var-value", sess(), vn, oldval); 0182 invokeEvent ("var-changed", sess(), vn, val->asString()); 0183 } 0184 else { 0185 cValue d (delta); 0186 set (varname, &d); 0187 } 0188 } 0189 } 0190 0191 void cVariableList::dec (const QString &varname, double delta) 0192 { 0193 inc (varname, -1.0 * delta); 0194 } 0195 0196 void cVariableList::provideResource (const QString &varname) 0197 { 0198 //returns a resource to available ones 0199 inc (varname, 1); 0200 } 0201 0202 bool cVariableList::requestResource (const QString &varname) 0203 { 0204 //is a resource available? 0205 if (getIntValue (varname) <= 0) 0206 //no :( 0207 return false; 0208 //yes, "allocate" one 0209 dec (varname, 1); 0210 return true; 0211 } 0212 0213 QStringList cVariableList::getList () 0214 { 0215 map<QString, cVariable *>::iterator it; 0216 QStringList list; 0217 for (it = vars.begin(); it != vars.end(); ++it) 0218 list.push_back (it->first); 0219 return list; 0220 } 0221 0222 QString cVariableList::expandVariables (const QString &string, bool recursive, cCmdQueue *queue) 0223 { 0224 return doExpandVariables (string, recursive ? MAXEXPANDPASSES : 1, queue); 0225 } 0226 0227 QString cVariableList::doExpandVariables (const QString &string, int recursionCounter, 0228 cCmdQueue *queue) 0229 { 0230 QString newstring = ""; 0231 int len = string.length (); 0232 bool hadvar = false; //whether we had some variable - used to prevent 0233 //unneeded recursion 0234 bool invar = false; 0235 bool inpar = false; //variable name is in parentheses '(' and ')' 0236 QString varname; 0237 for (int i = 0; i < len; i++) 0238 { 0239 QChar ch = string[i]; 0240 0241 //support things like $$$a correctly 0242 if (invar && (!inpar) && (ch == '$') && (varname.isEmpty())) 0243 { 0244 invar = false; 0245 newstring += ch; 0246 } 0247 0248 if (!invar) 0249 { 0250 if (ch == '$') 0251 { 0252 invar = true; 0253 inpar = false; 0254 varname = ""; 0255 } 0256 else 0257 newstring += ch; 0258 } 0259 else 0260 { 0261 if ((varname == "") && (ch == '(') && (!inpar)) 0262 inpar = true; 0263 else 0264 if ((!ch.isLetterOrNumber())&& 0265 (ch != '_') ) //end of variable name 0266 { 0267 invar = false; 0268 0269 if (inpar) 0270 { 0271 inpar = false; 0272 if (ch == ')') 0273 { 0274 QString newstr = processVariable (varname, queue); 0275 if (newstr == QString()) 0276 newstring += "$(" + varname + ")"; 0277 else 0278 { 0279 newstring += newstr; 0280 hadvar = true; 0281 } 0282 } 0283 else 0284 newstring += "$(" + varname + ch; 0285 } 0286 else 0287 { 0288 QString newstr = processVariable (varname, queue); 0289 if (newstr.isEmpty()) 0290 newstring += "$" + varname; 0291 else 0292 { 0293 newstring += newstr; 0294 hadvar = true; 0295 } 0296 newstring += ch; 0297 } 0298 } 0299 else 0300 varname += ch; 0301 } 0302 } 0303 0304 if (invar) { //if a variable ends the string 0305 if (inpar) 0306 newstring += "$(" + varname; //no ending parenthesis - no variable 0307 else 0308 { 0309 QString newstr = processVariable (varname, queue); 0310 if (newstr.isEmpty()) 0311 newstring += "$" + varname; 0312 else 0313 { 0314 newstring += newstr; 0315 hadvar = true; 0316 } 0317 } 0318 } 0319 0320 //another pass if needed, then return the result 0321 recursionCounter--; 0322 if (hadvar && (recursionCounter > 0)) 0323 return doExpandVariables (newstring, recursionCounter, queue); 0324 return newstring; 0325 } 0326 0327 QString cVariableList::processVariable (const QString &varname, cCmdQueue *queue) 0328 { 0329 if (varname.isEmpty()) return varname; // nothing if the varname is empty 0330 QString vn = varname; 0331 if (varname[0] == '$') 0332 vn = vn.mid(1); 0333 0334 if (queue && queue->varExists (vn)) 0335 return queue->getValue (vn); 0336 return getValue (vn); 0337 } 0338 0339 void cVariableList::load () 0340 { 0341 // load the list contents 0342 cProfileManager *pm = cProfileManager::self(); 0343 0344 QString path = pm->profilePath (sess()); 0345 QDir dir = QDir (path); 0346 if (!dir.exists()) QDir::root().mkpath (dir.absolutePath()); 0347 0348 QString fName = "variables.xml"; 0349 0350 QFile f (path + "/" + fName); 0351 if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { 0352 qDebug() << "No " << fName << " file - nothing to do."; 0353 return; // no profiles - nothing to do 0354 } 0355 0356 QXmlStreamReader *reader = new QXmlStreamReader (&f); 0357 0358 // we have the reader, now we actually load the list 0359 reader->readNext (); // read the document start 0360 reader->readNext (); 0361 if (reader->isStartElement ()) 0362 if (reader->name() == "variables") 0363 if (reader->attributes().value ("version") == "1.0") { 0364 // we're inside the root element, now we load the list 0365 while (!reader->atEnd()) { 0366 cValue val; 0367 QString name = val.load (reader); 0368 if (!name.isEmpty()) 0369 set (name, &val); 0370 } 0371 } 0372 else 0373 reader->raiseError (i18n ("This file was created by a newer version of KMuddy, and this version is unable to open it.")); 0374 else 0375 reader->raiseError (i18n ("This is not a KMuddy variable list file.")); 0376 else if (!reader->hasError()) 0377 reader->raiseError (i18n ("This file is corrupted.")); 0378 0379 if (reader->hasError()) { 0380 // TODO: present the error to the user 0381 QString lastError = i18n ("Error at line %1, column %2: %3", 0382 QString::number (reader->lineNumber()), 0383 QString::number (reader->columnNumber()), 0384 reader->errorString()); 0385 qWarning() << lastError; 0386 } 0387 delete reader; 0388 f.close (); 0389 } 0390 0391 void cVariableList::save () 0392 { 0393 // save the list contents 0394 cProfileManager *pm = cProfileManager::self(); 0395 0396 QString path = pm->profilePath (sess()); 0397 QDir dir = QDir (path); 0398 if (!dir.exists()) QDir::root().mkpath (dir.absolutePath()); 0399 0400 dir.remove ("variables.backup"); 0401 QString fName = "variables.xml"; 0402 if (!QFile(path + "/" + fName).copy (path + "/variables.backup")) { 0403 qDebug() << "Unable to backup " << fName; // not fatal, may simply not exist 0404 } 0405 0406 QFile f (path + "/" + fName); 0407 if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { 0408 qDebug() << "Unable to open " << (path + "/" + fName) << " for writing."; 0409 return; // problem 0410 } 0411 0412 QXmlStreamWriter *writer = new QXmlStreamWriter (&f); 0413 0414 writer->setAutoFormatting (true); // make the generated XML more readable 0415 writer->writeStartDocument (); 0416 0417 writer->writeStartElement ("variables"); 0418 writer->writeAttribute ("version", "1.0"); 0419 0420 map<QString, cVariable *>::iterator it; 0421 for (it = vars.begin(); it != vars.end(); ++it) 0422 it->second->getValue()->save (writer, it->first); 0423 0424 writer->writeEndElement (); 0425 writer->writeEndDocument (); 0426 0427 f.close (); 0428 delete writer; 0429 } 0430