src/qvcore/qvpropertyholder.cpp

Go to the documentation of this file.
00001 /*
00002  *      Copyright (C) 2007. PARP Research Group.
00003  *      <http://perception.inf.um.es>
00004  *      University of Murcia, Spain.
00005  *
00006  *      This file is part of the QVision library.
00007  *
00008  *      QVision is free software: you can redistribute it and/or modify
00009  *      it under the terms of the GNU Lesser General Public License as
00010  *      published by the Free Software Foundation, version 3 of the License.
00011  *
00012  *      QVision is distributed in the hope that it will be useful,
00013  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *      GNU Lesser General Public License for more details.
00016  *
00017  *      You should have received a copy of the GNU Lesser General Public
00018  *      License along with QVision. If not, see <http://www.gnu.org/licenses/>.
00019  */
00020 
00024 
00025 #include "qvpropertyholder.h"
00026 
00027 QVPropertyHolder::QVPropertyHolder(const QString name):
00028         name(name), errorString(), variants(), safelyCopiedVariants(), minimum(),
00029         maximum(), _info(), io_flags(), link_flags(), insertion_order(),
00030         inputLinks(), outputLinks()
00031         {
00032         if(qvApp == NULL)
00033                 {
00034                 QString str = "QVPropertyHolder::QVPropertyHolder(): holder " + name +
00035                                           ": property holders cannot be created before the " +
00036                                           "QVApplication instance. Aborting now.";
00037                 std::cerr << qPrintable(str) << std::endl;
00038                 exit(1);
00039                 }
00040         else
00041                 qvApp->registerQVPropertyHolder(this);
00042         }
00043 
00044 QVPropertyHolder::~QVPropertyHolder()
00045 {
00046         if(qvApp != NULL)
00047                 qvApp->deregisterQVPropertyHolder(this);
00048         // Not needed now: unlink();
00049 }
00050 
00051 void QVPropertyHolder::setName(const QString name)
00052         { this->name = name; }
00053 
00054 const QString QVPropertyHolder::getName() const
00055         { return this->name; }
00056 
00057 QList<QString> QVPropertyHolder::getPropertyList() const
00058         { return variants.keys(); }
00059 
00060 bool QVPropertyHolder::containsProperty(const QString name) const
00061         { return variants.contains(name); }
00062 
00063 QVariant::Type QVPropertyHolder::getPropertyType(const QString name, bool *ok) const
00064         {
00065         if(not checkExists(name,"QVPropertyHolder::getPropertyType()"))
00066                 {
00067                 if(ok != NULL) *ok = FALSE;
00068                 return QVariant::Invalid;
00069                 }
00070         if(ok != NULL) *ok = TRUE;
00071         QVariant variant = variants.value(name);
00072         return variant.type();
00073         }
00074 
00075 bool QVPropertyHolder::removeProperty(const QString name)
00076         {
00077         if(not checkExists(name,"QVPropertyHolder::removeProperty()"))
00078                 return FALSE;
00079         this->variants.remove(name);
00080         this->safelyCopiedVariants.remove(name);
00081         this->minimum.remove(name);
00082         this->maximum.remove(name);
00083         this->_info.remove(name);
00084         this->io_flags.remove(name);
00085         int i = this->insertion_order.indexOf(name);
00086         this->insertion_order.removeAt(i);
00087         return TRUE;
00088         }
00089 
00090 bool QVPropertyHolder::setPropertyRange(const QString name, const double & minimum, const double & maximum)
00091         {
00092         if(not checkExists(name,"QVPropertyHolder::setPropertyRange()"))
00093                 return FALSE;
00094         if(minimum <= getPropertyValue<double>(name) and
00095                 maximum >= getPropertyValue<double>(name))
00096                 {
00097                         this->minimum[name] = QVariant::fromValue(minimum);
00098                         this->maximum[name] = QVariant::fromValue(maximum);
00099                         return TRUE;
00100                 } else {
00101                         QString str =  "QVPropertyHolder::setPropertyRange(): property " +
00102                                                    name + " in holder " + getName() + " has value " +
00103                                                    QString("%1").arg(getPropertyValue<double>(name)) +
00104                                                    ", which is not valid for the range [" +
00105                                                    QString("%1,%2").arg(minimum).arg(maximum) + "]." ;
00106                         setLastError(str);
00107                         if(qvApp->isRunning()) {
00108                                 std::cerr << qPrintable("Warning: " + str + "\n");
00109                         } // Otherwise, qApp will show the error and won't start the program.
00110                         return FALSE;
00111                 }
00112         }
00113 
00114 bool QVPropertyHolder::setPropertyRange(const char *name, int & minimum, int & maximum)
00115         { return setPropertyRange(QString(name), static_cast<double>(minimum), static_cast<double>(maximum)); }
00116 
00117 bool QVPropertyHolder::setPropertyRange(QString name, int & minimum, int & maximum)
00118         { return setPropertyRange(name, static_cast<double>(minimum), static_cast<double>(maximum)); }
00119 
00120 bool QVPropertyHolder::hasRange(const QString name)
00121         { return maximum.contains(name) and minimum.contains(name); }
00122 
00123 bool QVPropertyHolder::isInput(const QString name)
00124         { return (io_flags[name] & inputFlag);};
00125 
00126 bool QVPropertyHolder::isOutput(const QString name)
00127         { return (io_flags[name] & outputFlag);};
00128 
00129 bool QVPropertyHolder::isLinkedInput(const QString name)
00130         { return (link_flags[name] & linkedInputFlag);};
00131 
00132 bool QVPropertyHolder::isLinkedOutput(const QString name)
00133         { return (link_flags[name] & linkedOutputFlag);};
00134 
00135 QVariant QVPropertyHolder::getPropertyQVariantValue(const QString name, bool *ok) const
00136         {
00137         if (not checkExists(name,"QVPropertyHolder::getPropertyQVariantValue()"))
00138                 if(ok != NULL) *ok = FALSE;
00139         else
00140                 if(ok != NULL) *ok = TRUE;
00141         return variants[name];
00142         }
00143 
00144 QString QVPropertyHolder::getPropertyInfo(const QString name, bool *ok) const
00145         {
00146         if(not checkExists(name,"QVPropertyHolder::getPropertyInfo()"))
00147                 if(ok != NULL) *ok = FALSE;
00148         else
00149                 if(ok != NULL) *ok = TRUE;
00150         return this->_info[name];
00151         }
00152 
00153 QString QVPropertyHolder::getLastError() const
00154 {
00155         return errorString;
00156 }
00157 
00158 const QString QVPropertyHolder::infoInputProperties() const
00159         {
00160         QString info = QString("Input parameters for ") + getName() + QString(":\n");
00161         bool emptyInfo=TRUE;
00162 
00163         QStringList props;
00164         QListIterator<QString> i(insertion_order);
00165         while (i.hasNext())
00166                 {
00167                 QString property = i.next();
00168                 if(not (io_flags[property] & inputFlag) or (link_flags[property] & linkedInputFlag))
00169                         continue;
00170                 emptyInfo=FALSE;
00171                 info += "  --" + property + "=";
00172                 QVariant::Type type = getPropertyType(property);
00173                 switch(type)
00174                         {
00175                         case QVariant::String:
00176                                 {
00177                                 READ_PROPERTY(this, QString, property, stringValue);
00178                                 info += "[text] ";
00179                                 if(stringValue != QString())
00180                                         info += "(def. " + stringValue + ") ";
00181                                 }
00182                                 break;
00183                         case QVariant::Double:
00184                                 {
00185                                 READ_PROPERTY(this,double,property,doubleValue);
00186                                 if(maximum.contains(property) and minimum.contains(property))
00187                                         {
00188                                         READ_PROPERTY_MAX(this,double,property,doubleMaximum);
00189                                         READ_PROPERTY_MIN(this,double,property,doubleMinimum);
00190                                         info += "[" + QString().setNum(doubleMinimum) + "..."
00191                                                 + QString().setNum(doubleMaximum) + "] ";
00192                                         }
00193                                 else
00194                                         info += "[double] ";
00195                                 info += "(def. "+ QString().setNum(doubleValue) + ") ";
00196                                 }
00197                                 break;
00198                         case QVariant::Int:
00199                                 {
00200                                 READ_PROPERTY(this,int,property,intValue);
00201                                 if(maximum.contains(property) and minimum.contains(property))
00202                                         {
00203                                         READ_PROPERTY_MAX(this,int,property,intMaximum);
00204                                         READ_PROPERTY_MIN(this,int,property,intMinimum);
00205                                         info += "[" + QString().setNum(intMinimum) + "..."
00206                                                 + QString().setNum(intMaximum) + "] ";
00207                                         }
00208                                 else
00209                                         info += "[int] ";
00210                                         info += "(def. "+ QString().setNum(intValue) + ") ";
00211                                 }
00212                                 break;
00213                         case QVariant::Bool:
00214                                 {
00215                                 READ_PROPERTY(this, bool, property, boolValue);
00216                                 info += "[true,false]"
00217                                         + (boolValue ? QString(" (def. true) "):QString("(def. false) "));
00218                                 }
00219                                 break;
00220                         default:
00221                                 info += QString("[%1] ").arg(type);
00222                                 break;
00223                         }
00224                 QString propertyInfo = getPropertyInfo(property);
00225                 if (propertyInfo != QString())
00226                         propertyInfo = " " + propertyInfo + ". ";
00227                 info += propertyInfo.rightJustified(80-info.split('\n').last().length(),'.') + "\n";
00228                 }
00229         if(emptyInfo)
00230                 return QString(""); //info += "  None.\n";
00231 
00232         return info;
00233         }
00234 
00235 bool QVPropertyHolder::correctRange(const QString name, const double & value) const
00236         {
00237         if(not maximum.contains(name) and not minimum.contains(name))
00238                 return TRUE;
00239         double maximum = getPropertyMaximum<double>(name);
00240         double minimum = getPropertyMinimum<double>(name);
00241         if(minimum <= value and maximum >= value)
00242                 return TRUE;
00243         else
00244                 {
00245                 QString str =  "QVPropertyHolder::setPropertyValue(): value " +
00246                                            QString("%1").arg(value) + " for property " +
00247                                            name + " in holder " + getName() +
00248                                            "is not valid for the range [" +
00249                                            QString("%1,%2").arg(minimum).arg(maximum) + 
00250                                            "] stablished for it." ;
00251                 setLastError(str);
00252                 if(qvApp->isRunning())
00253                         {
00254                         std::cerr << qPrintable("Warning: " + str + "\n");
00255                         } // Otherwise, qApp will show the error and won't start the program.
00256                 return FALSE;
00257                 }
00258         }
00259 
00260 bool QVPropertyHolder::correctRange(const char *name, const int & value) const
00261         { return correctRange(QString(name),static_cast<double>(value)); }
00262 
00263 bool QVPropertyHolder::correctRange(QString name, const int & value) const
00264         { return correctRange(name,static_cast<double>(value)); }
00265 
00266 bool QVPropertyHolder::checkExists(const QString name, const QString methodname) const
00267         {
00268         if(not variants.contains(name))
00269                 {
00270                 QString str =  methodname + ": property " + name +
00271                                            " doesn't exists in holder " + getName() + ".";
00272                 setLastError(str);
00273                 if(qvApp->isRunning()) {
00274                         std::cerr << qPrintable("Warning: " + str + "\n");
00275                 } // Otherwise, qApp will show the error and won't start the program.
00276                 return FALSE;
00277                 } else {
00278                 return TRUE;
00279                 }
00280         }
00281 
00282 bool QVPropertyHolder::checkIsNewProperty(const QString name, const QString methodname) const
00283         {
00284         if(variants.contains(name))
00285                 {
00286                 QString str =  methodname + "(): property " + name +
00287                                            " already exists in holder " + getName() + ".";
00288                 setLastError(str);
00289                 if(qvApp->isRunning()) {
00290                         std::cerr << qPrintable("Warning: " + str + "\n");
00291                 } // Otherwise, qApp will show the error and won't start the program.
00292                 return FALSE;
00293                 } else {
00294                 return TRUE;
00295                 }
00296         }
00297 
00298 bool QVPropertyHolder::linkProperty(QString prop_orig,QVPropertyHolder *qvp_dest,QString prop_dest,LinkType link_type)
00299         {
00300         bool ok1,ok2;
00301         QString errMsg;
00302         QVariant::Type t1,t2;
00303         QVPropertyHolder *qvp_orig=this, *qvp_err=NULL;
00304         
00305         t1 = qvp_orig->getPropertyType(prop_orig,&ok1);
00306         t2 = qvp_dest->getPropertyType(prop_dest,&ok2);
00307         if(qvApp->isRunning())
00308                 {
00309                 qvp_err = qvp_orig;
00310                 errMsg = QString("QVPropertyHolder::linkProperty(): Property holder %1:"
00311                                         "Cannot link properties after launching QVApplication.\n")
00312                                         .arg(prop_orig).arg(qvp_orig->getName());
00313                 }
00314         else if(qvp_orig == qvp_dest)
00315                 {
00316                 errMsg = QString("QVPropertyHolder::linkProperty(): Property holder %1: cannot link a QVPropertyHolder with itself.\n").arg(qvp_orig->getName());
00317                 qvp_err = qvp_orig;
00318                 }
00319         else if(not ok1)
00320                 {
00321                 errMsg = QString("QVPropertyHolder::linkProperty(): Property %1 does not exist in property holder %2.\n")
00322                                 .arg(prop_orig).arg(qvp_orig->getName());
00323                 qvp_err = qvp_orig;
00324                 }
00325         else if (not ok2)
00326                 {
00327                 errMsg = QString("QVPropertyHolder::linkProperty(): Property %1 does not exist in property holder %2.\n")
00328                                 .arg(prop_dest).arg(qvp_dest->getName());
00329                 qvp_err = qvp_dest;
00330                 }
00331         else if(t1 != t2)
00332                 {
00333                 errMsg = QString("QVPropertyHolder::linkProperty(): Properties %1 and %2 of QVPropertyHolders %3 and %4 respectively are not of the same type.\n").arg(prop_orig).arg(prop_dest).arg(qvp_orig->getName()).arg(qvp_dest->getName());
00334                 qvp_err = qvp_orig;
00335                 }
00336         else if(not (qvp_orig->io_flags[prop_orig] & outputFlag))
00337                 {
00338                 errMsg = QString("QVPropertyHolder::linkProperty(): Property %1 of property holder %2 is not of Output type, and cannot be linked as such.\n").arg(prop_orig).arg(qvp_orig->getName());
00339                 qvp_err = qvp_orig;
00340                 }
00341         else if(not (qvp_dest->io_flags[prop_dest] & inputFlag))
00342                 {
00343                 errMsg = QString("QVPropertyHolder::linkProperty(): Property %1 property holder %2 is not of Input type, and cannot be linked as such.\n").arg(prop_dest).arg(qvp_dest->getName());
00344                 qvp_err = qvp_dest;
00345                 }
00346 
00347         if(errMsg != QString())
00348                 {
00349                 qvp_err->setLastError(errMsg);
00350                 if(qvApp->isRunning()) {
00351                         std::cerr << qPrintable("Warning: " + errMsg + "\n");
00352                 } // Otherwise, qApp will show the error and won't start the program.
00353                 return FALSE;
00354                 }
00355         else
00356                 {
00357                 QVPropertyHolderLink *link = new
00358                          QVPropertyHolderLink(qvp_orig,prop_orig,qvp_dest,prop_dest,link_type);
00359                                 qvp_orig->outputLinks[prop_orig].push_back(link);
00360                                 qvp_dest->inputLinks[prop_dest] = link;
00361                 qvp_dest->link_flags[prop_dest] |= linkedInputFlag;
00362                 qvp_orig->link_flags[prop_orig] |= linkedOutputFlag;
00363                 return TRUE;
00364                 }
00365         }
00366 
00367 void QVPropertyHolder::unlink()
00368         {
00369                 QMapIterator<QString, QVPropertyHolderLink*> i_in(inputLinks);
00370                 while (i_in.hasNext()) {
00371                         i_in.next();
00372                         QVPropertyHolderLink *link = i_in.value();
00373                         link->markedForDeletion = TRUE;
00374                         // Protect against a possible pending acquire() from our input
00375                         // in other holders:
00376                         link->SyncSemaphoreIn.release();
00377                 }
00378         
00379                 QMapIterator<QString, QList<QVPropertyHolderLink*> >i_out(outputLinks);
00380                 while (i_out.hasNext()) {
00381                         i_out.next();
00382                         QListIterator<QVPropertyHolderLink*> j_out(i_out.value());
00383                         while(j_out.hasNext()) {
00384                                 QVPropertyHolderLink *link = j_out.next();
00385                                 link->markedForDeletion = TRUE;
00386                                 // Protect against a possible pending acquire() for our output
00387                                 // in other holders:
00388                                 link->SyncSemaphoreOut.release();
00389                         }
00390                 }
00391         }
00392 
00393 void QVPropertyHolder::readInputProperties()
00394         {
00395         // We read every linked input property from its source, protecting
00396         // the read with a standard RWLock (that here we just lock for read).
00397         // The only caveat is that if a property is synchronously read, then
00398         // we must wait for the producer to write it first. We implement that
00399         // by waiting on the SyncSemaphoreOut of the link. Also, in this
00400         // case when we finish reading the property, we signal every possible
00401         // waiting writer that, regarding this specific link, it can write now
00402         // a new value if it needs to, because we have read the old value yet.
00403         // This is implemented by releasing the SyncSemaphoreIn associated to
00404         // the link.
00405         QMutableMapIterator<QString, QVPropertyHolderLink*> i(inputLinks);
00406         while (i.hasNext()) {
00407                 i.next();
00408                 QVPropertyHolderLink *link = i.value();
00409                 if(link->link_type == SynchronousLink and not link->markedForDeletion) {
00410                         link->SyncSemaphoreOut.acquire();
00411                 }
00412                 link->qvp_orig->RWLock.lockForRead();
00413                 //this->setPropertyValueQVariant(link->prop_dest,link->qvp_orig->safelyCopiedVariants[link->prop_orig]);
00414                 this->variants[link->prop_dest] = link->qvp_orig->safelyCopiedVariants[link->prop_orig];
00415                 link->qvp_orig->RWLock.unlock();
00416                 if(link->link_type == SynchronousLink and not link->markedForDeletion) {
00417                         link->SyncSemaphoreIn.release();
00418                 }
00419                 // Possible link deletion:
00420                 if(link->markedForDeletion) {
00421                         i.remove();
00422                         delete link;
00423                 }
00424         }
00425 }
00426 
00427 void QVPropertyHolder::writeOutputProperties()
00428         {
00429         QMutableMapIterator<QString, QList<QVPropertyHolderLink*> >i(outputLinks);
00430 
00431         // For every QVP synchronously linked to this QVP's output, we ask
00432         // for permision to write a new output (that will only be possible if
00433         // all of these QVP's have read their inputs already):
00434         while (i.hasNext()) {
00435                 i.next();
00436                 QListIterator<QVPropertyHolderLink*> j(i.value());
00437                 while(j.hasNext()) {
00438                         QVPropertyHolderLink *link = j.next();
00439                         if(link->link_type == SynchronousLink and not link->markedForDeletion) {
00440                                 link->SyncSemaphoreIn.acquire();
00441                         }
00442                 }
00443         }
00444 
00445         // Now, we write a new coherent state, simply protected by the
00446         // corresponding RWLock:
00447         i.toFront();
00448         this->RWLock.lockForWrite();
00449         while (i.hasNext()) {
00450                 i.next();
00451                 QString prop_orig = i.key();
00452                 safelyCopiedVariants[prop_orig] = variants[prop_orig];
00453         }
00454         this->RWLock.unlock();
00455 
00456         // Finally, we signal to QVP's synchronously linked to this QVP's output
00457         // that there is a new coherent output, by unlocking our SyncLockOut
00458         // lock.
00459         i.toFront();
00460         while (i.hasNext()) {
00461                 i.next();
00462                 QMutableListIterator<QVPropertyHolderLink*> j(i.value());
00463                 while(j.hasNext()) {
00464                         QVPropertyHolderLink *link = j.next();
00465                         if(link->link_type == SynchronousLink and not link->markedForDeletion) {
00466                                 link->SyncSemaphoreOut.release();
00467                         }
00468                         // Possible link deletion:
00469                         if(link->markedForDeletion) {
00470                                 j.remove();
00471                                 delete link;
00472                                 if(i.value().isEmpty()) {
00473                                         i.remove();
00474                                         break;
00475                                 }
00476                         }
00477                 }
00478         }
00479 }
00480 
00481 void QVPropertyHolder::setLastError(QString str) const
00482         { errorString = str; }
00483 
00484 template <> bool QVPropertyHolder::parseArgument<bool>(const QString parameter, const QString value)
00485         {
00486         if (value.toLower() == "true" || value.toLower() == "false")
00487                 {
00488                 //variants[parameter] = QVariant::fromValue<bool>(value.toLower() == "true");
00489                 setPropertyValue<bool>(parameter,value.toLower() == "true");
00490                 return TRUE;
00491                 }
00492         else {
00493                 errorString = "QVPropertyHolder::parseArgument(): holder " + getName() +
00494                                         ": value " + value +
00495                                         " is not a valid boolean value for parameter " +
00496                                         parameter + ".\n";
00497                 return FALSE;
00498                 }
00499         }
00500 
00501 template <> bool QVPropertyHolder::parseArgument<int>(const QString parameter, const QString value)
00502         {
00503         bool okInt;
00504         int intValue = value.toInt(&okInt);
00505         if(not okInt)
00506                 {
00507                 errorString = "QVPropertyHolder::parseArgument(): holder " + getName() +
00508                                         ": value " + value +
00509                                         " is not a valid integer value for parameter " +
00510                                         parameter + ".\n";
00511                 return FALSE;
00512                 }
00513         //variants[parameter] = QVariant::fromValue<int>(intValue);
00514         setPropertyValue<int>(parameter,intValue);
00515         return TRUE;
00516         }
00517 
00518 template <> bool QVPropertyHolder::parseArgument<double>(const QString parameter, const QString value)
00519         {
00520         bool okDouble;
00521         double doubleValue = value.toDouble(&okDouble);
00522         if(not okDouble)
00523                 {
00524                 errorString = "QVPropertyHolder::parseArgument(): holder " + getName() +
00525                                         ": value " + value +
00526                                         " is not a valid double value for parameter " +
00527                                         parameter + ".\n";
00528                 return FALSE;
00529                 }
00530         //variants[parameter] = QVariant::fromValue<double>(doubleValue);
00531         setPropertyValue<double>(parameter,doubleValue);
00532         return TRUE;
00533         }
00534 
00535 template <> bool QVPropertyHolder::parseArgument<QString>(const QString parameter, const QString value)
00536         {
00537         //variants[parameter] = QVariant::fromValue<QString>(value);
00538         setPropertyValue<QString>(parameter,value);
00539         return TRUE;
00540         }

Generated on Fri Dec 7 12:20:59 2007 for QVision by  doxygen 1.5.3