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

Generated on Fri Feb 22 18:26:55 2008 for QVision by  doxygen 1.5.3