PARP Research Group University of Murcia, Spain


src/qvcore/qvpropertycontainer.cpp

Go to the documentation of this file.
00001 /*
00002  *      Copyright (C) 2007, 2008, 2009. 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 <QVDisjointSet>
00026 #include <QVPropertyContainer>
00027 
00028 uint QVPropertyContainer::maxIdent = 0;
00029 // QVPropertyContainerInformer QVPropertyContainer::globalInformer;
00030 
00031 QVPropertyContainer::QVPropertyContainer(const QString name):
00032         name(name), errorString(), variants(), safelyCopiedVariants(), minimum(),
00033         maximum(), _info(), io_flags(), link_flags(), insertion_order(),
00034         inputLinks(), outputLinks(), master(this), deepLevel(0)
00035         {
00036         ident = getNewIdent();
00037         qDebug() << "QVPropertyContainer::QVPropertyContainer(" << name << ")";
00038         if(qvApp == NULL)
00039                 {
00040                 QString str = "QVPropertyContainer::QVPropertyContainer(): holder " + name +
00041                                           ": property holders cannot be created before the " +
00042                                           "QVApplication instance. Aborting now.";
00043                 std::cerr << qPrintable(str) << std::endl;
00044                 exit(1);
00045                 }
00046         else
00047                 {
00048                 qvApp->registerQVPropertyContainer(this);
00049 //              QVPropertyContainer::globalInformer.emitChange(QVPropertyContainerChange(this, QVPropertyContainerChange::ContainerAdd));
00050                 }
00051         slavesByLevel.append(QList<QVPropertyContainer *>());
00052         slavesByLevel[0].append(this);
00053         qDebug() << "QVPropertyContainer::QVPropertyContainer(" << name << ") <- return";
00054         }
00055 
00056 QVPropertyContainer::QVPropertyContainer(const QVPropertyContainer &cont):
00057         name(cont.name), ident(cont.ident), errorString(cont.errorString), variants(cont.variants),
00058         safelyCopiedVariants(cont.safelyCopiedVariants), minimum(cont.minimum), maximum(cont.maximum),
00059         _info(cont._info), io_flags(cont.io_flags), link_flags(cont.link_flags), insertion_order(cont.insertion_order),
00060         RWLock(), inputLinks(cont.inputLinks), outputLinks(cont.outputLinks), master(this), deepLevel(0)
00061         {
00062         qDebug() << "QVPropertyContainer::QVPropertyContainer(" << name << ")";
00063         if(qvApp == NULL)
00064                 {
00065                 QString str = "QVPropertyContainer::QVPropertyContainer(): holder " + name +
00066                                           ": property holders cannot be created before the " +
00067                                           "QVApplication instance. Aborting now.";
00068                 std::cerr << qPrintable(str) << std::endl;
00069                 exit(1);
00070                 }
00071         else
00072                 {
00073                 qvApp->registerQVPropertyContainer(this);
00074 //              QVPropertyContainer::globalInformer.emitChange(QVPropertyContainerChange(this, QVPropertyContainerChange::ContainerAdd));
00075                 }
00076         slavesByLevel.append(QList<QVPropertyContainer *>());
00077         slavesByLevel[0].append(this);
00078         qDebug() << "QVPropertyContainer::QVPropertyContainer(" << name << ") <- return";
00079         }
00080 
00081 QVPropertyContainer & QVPropertyContainer::operator=(const QVPropertyContainer &cont)
00082         {
00083         name = cont.name;
00084         ident = cont.ident;
00085         errorString = cont.errorString;
00086         variants = cont.variants;
00087         safelyCopiedVariants = cont.safelyCopiedVariants;
00088         minimum = cont.minimum;
00089         maximum = cont.maximum;
00090         _info = cont._info;
00091         io_flags = cont.io_flags;
00092         link_flags = cont.link_flags;
00093         insertion_order = cont.insertion_order;
00094         inputLinks = cont.inputLinks;
00095         outputLinks = cont.outputLinks;
00096         master = this;
00097         deepLevel = 0;
00098 
00099         slavesByLevel.append(QList<QVPropertyContainer *>());
00100         slavesByLevel[0].append(this);
00101         informer.emitChange(QVPropertyContainerChange(this->getName(), this->getId(), QVPropertyContainerChange::All));
00102         return *this;
00103         }
00104 
00105 QVPropertyContainer::~QVPropertyContainer()
00106         {
00107         if(qvApp != NULL)
00108                 qvApp->deregisterQVPropertyContainer(this);
00109 
00110         unlink();
00111         readInputProperties();
00112         writeOutputProperties();
00113         informer.emitChange(QVPropertyContainerChange(this->getName(), this->getId(), QVPropertyContainerChange::ContainerDel));
00114         }
00115 
00116 void QVPropertyContainer::setName(const QString name)
00117         { this->name = name; informer.emitChange(QVPropertyContainerChange(this->getName(), this->getId(), QVPropertyContainerChange::Name)); }
00118 
00119 const QString QVPropertyContainer::getName() const
00120         { return this->name; }
00121 
00122 const uint QVPropertyContainer::getId() const
00123         { return this->ident; }
00124 
00125 bool QVPropertyContainer::operator==(const QVPropertyContainer &cont) const
00126         { return (ident == cont.ident); }
00127 
00128 QList<QString> QVPropertyContainer::getPropertyList() const
00129         { return variants.keys(); }
00130 
00131 bool QVPropertyContainer::containsProperty(const QString name) const
00132         { return variants.contains(name); }
00133 
00134 int QVPropertyContainer::getPropertyType(const QString name, bool *ok) const
00135         {
00136         if(not checkExists(name,"QVPropertyContainer::getPropertyType()"))
00137                 {
00138                 if(ok != NULL) *ok = FALSE;
00139                 return QVariant::Invalid;
00140                 }
00141         if(ok != NULL) *ok = TRUE;
00142         QVariant variant = variants.value(name);
00143         return variant.userType();
00144         }
00145 
00146 bool QVPropertyContainer::removeProperty(const QString name)
00147         {
00148         if(not checkExists(name,"QVPropertyContainer::removeProperty()"))
00149                 return FALSE;
00150         this->variants.remove(name);
00151         this->safelyCopiedVariants.remove(name);
00152         this->minimum.remove(name);
00153         this->maximum.remove(name);
00154         this->_info.remove(name);
00155         this->io_flags.remove(name);
00156         int i = this->insertion_order.indexOf(name);
00157         this->insertion_order.removeAt(i);
00158         informer.emitChange(QVPropertyContainerChange(this->getName(), this->getId(), QVPropertyContainerChange::PropertyDel, name));
00159         return TRUE;
00160         }
00161 /*
00162 bool QVPropertyContainer::setPropertyRange(const QString name, const double & minimum, const double & maximum)
00163         {
00164         std::cerr << "WARNING: setPropertyRange() is deprecated. Specify range in the function addProperty instead." << std::endl;
00165         if(not checkExists(name,"QVPropertyContainer::setPropertyRange()"))
00166                 return FALSE;
00167         if(minimum <= getPropertyValue<double>(name) and
00168                 maximum >= getPropertyValue<double>(name))
00169                 {
00170                         this->minimum[name] = QVariant::fromValue(minimum);
00171                         this->maximum[name] = QVariant::fromValue(maximum);
00172                         return TRUE;
00173                 } else {
00174                         QString str =  "QVPropertyContainer::setPropertyRange(): property " +
00175                                                    name + " in holder " + getName() + " has value " +
00176                                                    QString("%1").arg(getPropertyValue<double>(name)) +
00177                                                    ", which is not valid for the range [" +
00178                                                    QString("%1,%2").arg(minimum).arg(maximum) + "]." ;
00179                         setLastError(str);
00180                         if(qvApp->isRunning()) {
00181                                 std::cerr << qPrintable("Warning: " + str + "\n");
00182                         } // Otherwise, qApp will show the error and won't start the program.
00183                         return FALSE;
00184                 }
00185         }
00186 
00187 bool QVPropertyContainer::setPropertyRange(QString name, int & minimum, int & maximum)
00188         { return setPropertyRange(name, static_cast<double>(minimum), static_cast<double>(maximum)); }
00189 */
00190 bool QVPropertyContainer::hasRange(const QString name) const
00191         { return maximum.contains(name) and minimum.contains(name); }
00192 
00193 bool QVPropertyContainer::isInput(const QString name) const
00194         { return (io_flags[name] & inputFlag);};
00195 
00196 bool QVPropertyContainer::isOutput(const QString name) const
00197         { return (io_flags[name] & outputFlag);};
00198 
00199 bool QVPropertyContainer::isGUIInvisible(const QString name) const
00200         { return (io_flags[name] & guiInvisible);};
00201 
00202 bool QVPropertyContainer::isLinkedInput(const QString name) const
00203         { return (link_flags[name] & linkedInputFlag);};
00204 
00205 bool QVPropertyContainer::isLinkedOutput(const QString name) const
00206         { return (link_flags[name] & linkedOutputFlag);};
00207 
00208 QVariant QVPropertyContainer::getPropertyQVariantValue(const QString name, bool *ok) const
00209         {
00210         if (not checkExists(name,"QVPropertyContainer::getPropertyQVariantValue()"))
00211                 if(ok != NULL) *ok = FALSE;
00212         else
00213                 if(ok != NULL) *ok = TRUE;
00214         return variants[name];
00215         }
00216 
00217 QString QVPropertyContainer::getPropertyInfo(const QString name, bool *ok) const
00218         {
00219         if(not checkExists(name,"QVPropertyContainer::getPropertyInfo()"))
00220                 if(ok != NULL) *ok = FALSE;
00221         else
00222                 if(ok != NULL) *ok = TRUE;
00223         return this->_info[name];
00224         }
00225 
00226 QString QVPropertyContainer::getLastError() const
00227         {
00228         return errorString;
00229         }
00230 
00231 const QString QVPropertyContainer::infoInputProperties() const
00232         {
00233         qDebug() << "QVPropertyContainer::infoInputProperties(" << getName() << ")";
00234 
00235         QString info = QString("Input parameters for ") + getName() + QString(":\n");
00236         bool emptyInfo=TRUE;
00237 
00238         qDebug() << "QVPropertyContainer::infoInputProperties(): Properties " << insertion_order;
00239 
00240         foreach (QString property, insertion_order)
00241         //QListIterator<QString> i(insertion_order);
00242         //while (i.hasNext())
00243                 {
00244                 //const QString property = i.next();
00245 
00246                 if( not isInput(property) )
00247                         continue;
00248 
00249                 bool printableProperty = TRUE;
00250                 QString propertyInfo("  --" + property + "=");
00251 
00252                 switch(getPropertyType(property))
00253                         {
00254                         case QVariant::String:
00255                                 propertyInfo += QString() + "[text] " + "(def. '" + getPropertyValue<QString>(property) + "') ";
00256                                 break;
00257 
00258                         case QVariant::Double:
00259                                 propertyInfo += ( (maximum.contains(property) and minimum.contains(property))?
00260                                         "[" + QString().setNum(getPropertyMinimum<double>(property)) + "..."
00261                                                 + QString().setNum(getPropertyMaximum<double>(property)) + "] ":
00262                                         "[double] " ) + "(def. "+ QString().setNum(getPropertyValue<double>(property)) + ") ";
00263                                 break;
00264 
00265                         case QVariant::Int:
00266                                 propertyInfo += ( (maximum.contains(property) and minimum.contains(property))?
00267                                         "[" + QString().setNum(getPropertyMinimum<int>(property)) + "..." +
00268                                                 QString().setNum(getPropertyMaximum<int>(property)) + "] ":
00269                                         "[int] " ) + "(def. "+ QString().setNum(getPropertyValue<int>(property)) + ") ";
00270                                 break;
00271 
00272                         case QVariant::Bool:
00273                                 propertyInfo += "[true,false]" + (getPropertyValue<bool>(property) ?
00274                                                                 QString(" (def. true) "):QString("(def. false) "));
00275                                 break;
00276 
00277                         default:
00278                                 printableProperty = FALSE;
00279                                 break;
00280                         }
00281 
00282                 if (printableProperty)
00283                         {
00284                         info += propertyInfo + getPropertyInfo(property).rightJustified(100-propertyInfo.split('\n').last().length(),'.') + ".\n";
00285                         emptyInfo=FALSE;
00286                         }
00287                 }
00288 
00289         qDebug() << "QVPropertyContainer::infoInputProperties(" << getName() << ") <~ return";
00290 
00291         if(emptyInfo)
00292                 return QString("");
00293         
00294         return info;
00295         }
00296 
00297 bool QVPropertyContainer::correctRange(const QString name, const double & value) const
00298         {
00299         if(not maximum.contains(name) and not minimum.contains(name))
00300                 return TRUE;
00301         double maximum = getPropertyMaximum<double>(name);
00302         double minimum = getPropertyMinimum<double>(name);
00303         if(minimum <= value and maximum >= value)
00304                 return TRUE;
00305         else
00306                 {
00307                 QString str =  "QVPropertyContainer::setPropertyValue(): value " +
00308                                            QString("%1").arg(value) + " for property " +
00309                                            name + " in holder " + getName() +
00310                                            "is not valid for the range [" +
00311                                            QString("%1,%2").arg(minimum).arg(maximum) + 
00312                                            "] stablished for it." ;
00313                 setLastError(str);
00314                 if(qvApp->isRunning())
00315                         {
00316                         std::cerr << qPrintable("Warning: " + str + "\n");
00317                         } // Otherwise, qApp will show the error and won't start the program.
00318                 return FALSE;
00319                 }
00320         }
00321 
00322 bool QVPropertyContainer::correctRange(const char *name, const int & value) const
00323         { return correctRange(QString(name),static_cast<double>(value)); }
00324 
00325 bool QVPropertyContainer::correctRange(QString name, const int & value) const
00326         { return correctRange(name,static_cast<double>(value)); }
00327 
00328 bool QVPropertyContainer::checkExists(const QString name, const QString methodname) const
00329         {
00330         if(not variants.contains(name))
00331                 {
00332                 QString str =  methodname + ": property " + name +
00333                                            " doesn't exists in holder " + getName() + ".";
00334                 setLastError(str);
00335                 if(qvApp->isRunning()) {
00336                         std::cerr << qPrintable("Warning: " + str + "\n");
00337                 } // Otherwise, qApp will show the error and won't start the program.
00338                 return FALSE;
00339                 } else {
00340                 return TRUE;
00341                 }
00342         }
00343 
00344 bool QVPropertyContainer::checkIsNewProperty(const QString name, const QString methodname) const
00345         {
00346         if(variants.contains(name))
00347                 {
00348                 QString str =  methodname + "(): property " + name +
00349                                            " already exists in holder " + getName() + ".";
00350                 setLastError(str);
00351                 if(qvApp->isRunning()) {
00352                         std::cerr << qPrintable("Warning: " + str + "\n");
00353                 } // Otherwise, qApp will show the error and won't start the program.
00354                 return FALSE;
00355                 } else {
00356                 return TRUE;
00357                 }
00358         }
00359 
00361 
00362 bool QVPropertyContainer::linkProperty(QString prop_orig, QVPropertyContainer *qvp_dest, QString prop_dest, LinkType link_type)
00363         {
00364         bool ok1,ok2;
00365         QString errMsg;
00366         int t1,t2;
00367         QVPropertyContainer *qvp_err=NULL;
00368         
00369         t1 = this->getPropertyType(prop_orig,&ok1);
00370         t2 = qvp_dest->getPropertyType(prop_dest,&ok2);
00371         if(qvApp->isRunning())
00372                 {
00373                 qvp_err = this;
00374                 errMsg = QString("QVPropertyContainer::linkProperty(): Property holder %1:"
00375                                         "Cannot link properties after launching QVApplication.\n")
00376                                         .arg(prop_orig).arg(this->getName());
00377                 }
00378         else if(this == qvp_dest)
00379                 {
00380                 errMsg = QString("QVPropertyContainer::linkProperty(): Property holder %1: cannot link a QVPropertyContainer with itself.\n").arg(this->getName());
00381                 qvp_err = this;
00382                 }
00383         else if(not ok1)
00384                 {
00385                 errMsg = QString("QVPropertyContainer::linkProperty(): Property %1 does not exist in property holder %2.\n")
00386                                 .arg(prop_orig).arg(this->getName());
00387                 qvp_err = this;
00388                 }
00389         else if (not ok2)
00390                 {
00391                 errMsg = QString("QVPropertyContainer::linkProperty(): Property %1 does not exist in property holder %2.\n")
00392                                 .arg(prop_dest).arg(qvp_dest->getName());
00393                 qvp_err = qvp_dest;
00394                 }
00395         else if(t1 != t2)
00396                 {
00397                 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(this->getName()).arg(qvp_dest->getName());
00398                 qvp_err = this;
00399                 }
00400         else if(not (this->io_flags[prop_orig] & outputFlag))
00401                 {
00402                 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(this->getName());
00403                 qvp_err = this;
00404                 }
00405         else if(not (qvp_dest->io_flags[prop_dest] & inputFlag))
00406                 {
00407                 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());
00408                 qvp_err = qvp_dest;
00409                 }
00410         else if( (link_type == SequentialLink) && (!ProcessPosibleSequentialLink(qvp_dest)) )
00411                 {
00412                 errMsg = QString("QVPropertyContainer::linkProperty(): A new link cannot generate a cycle of SequentialLinks.\n");
00413                 qvp_err = this;
00414                 }
00415 
00416         if(errMsg != QString())
00417                 {
00418                 qvp_err->setLastError(errMsg);
00419                 if(qvApp->isRunning()) {
00420                         std::cerr << qPrintable("Warning: " + errMsg + "\n");
00421                 } // Otherwise, qApp will show the error and won't start the program.
00422                 return FALSE;
00423                 }
00424         else
00425                 {
00426                 QVPropertyContainerLink *link = new QVPropertyContainerLink(this,prop_orig,qvp_dest,prop_dest,link_type);
00427                 this->outputLinks[prop_orig].push_back(link);
00428                 this->link_flags[prop_orig] |= linkedOutputFlag;
00429                 // We add a new method "addInputLink(prop_dest, link);" instead of doing
00430                 // "qvp_dest->inputLinks[prop_dest] = link; qvp_dest->link_flags[prop_dest] |= linkedInputFlag;", for
00431                 // qvp_dest to emit a signal saying that its links were changed.
00432                 qvp_dest->addInputLink(prop_dest, link);
00433 
00434                 // Now, we initialize the shared state, simply protected by the
00435                 // corresponding RWLock:
00436                 this->RWLock.lockForWrite();
00437                 safelyCopiedVariants[prop_orig] = variants[prop_orig];
00438                 this->RWLock.unlock();
00439 
00440                 if (link_type == QVPropertyContainer::AsynchronousLink)
00441                         informer.emitChange(QVPropertyContainerChange(this->getName(), this->getId(), QVPropertyContainerChange::LinkAdd, this->getName(), this->getId(), prop_orig, qvp_dest->getName(), qvp_dest->getId(), prop_dest,FALSE,FALSE));
00442                 else if (link_type == QVPropertyContainer::SynchronousLink)
00443                         informer.emitChange(QVPropertyContainerChange(this->getName(), this->getId(), QVPropertyContainerChange::LinkAdd, this->getName(), this->getId(), prop_orig, qvp_dest->getName(), qvp_dest->getId(), prop_dest,TRUE, FALSE));
00444                 else
00445                         informer.emitChange(QVPropertyContainerChange(this->getName(), this->getId(), QVPropertyContainerChange::LinkAdd, this->getName(), this->getId(), prop_orig, qvp_dest->getName(), qvp_dest->getId(), prop_dest,FALSE, TRUE));
00446 
00447                 return TRUE;
00448                 }
00449         }
00450 
00451 bool QVPropertyContainer::linkProperty(QVPropertyContainer *destinyContainer, QString destinyPropertyName, LinkType linkType)
00452         {
00453         if (linkUnspecifiedOutputProperty(destinyContainer, destinyPropertyName, linkType))
00454                 return true;
00455         else    {
00456                 std::cerr << "ERROR: QVPropertyContainer::linkProperty(): source container " << qPrintable(getName()) << " can't handle unspecified output properties linking" << std::endl;
00457                 return false;
00458                 }
00459         }
00460 
00461 bool QVPropertyContainer::linkProperty(QString sourcePropertyName, QVPropertyContainer *destinyContainer, LinkType linkType)
00462         {
00463         if (destinyContainer->linkUnspecifiedInputProperty(this, sourcePropertyName, linkType))
00464                 return true;
00465         else    {
00466                 std::cerr << "ERROR: QVPropertyContainer::linkProperty(): destination container " << qPrintable(destinyContainer->getName()) << " can't handle unspecified input properties linking" << std::endl;
00467                 return false;
00468                 }
00469         }
00470 
00471 void QVPropertyContainer::linkProperty(QVPropertyContainer *container, LinkType linkType)
00472         {
00473         QList<QString> localProper = getPropertyList();
00474         QList<QString> inputProper = container->getPropertyList();
00475 
00476         qSort(localProper.begin(), localProper.end());
00477         qSort(inputProper.begin(), inputProper.end());
00478 
00479         int i=0, l=0;
00480         while ( (i < inputProper.size()) && (l < localProper.size()) )
00481                 {
00482                 QString localName = localProper[l];
00483                 QString inputName = inputProper[i];
00484                 if (
00485                         (localName == inputName) && // si tienen el mismo nombre
00486                         (getPropertyType(localName) == container->getPropertyType(inputName)) && // si tienen el mismo tipo
00487                         (io_flags[localName] & outputFlag) && // si una es de entrada
00488                         (container->io_flags[inputName] & inputFlag) && // y otra de salida
00489                         (!(io_flags[localName] & internalProp)) && // y no son propiedades internas
00490                         (!(container->io_flags[inputName] & internalProp))
00491                    )
00492                         linkProperty(localProper[l], container, inputProper[i], linkType);
00493 
00494                 if(localName <= inputName) l++;
00495                 if(localName >= inputName) i++;
00496                 }
00497         }
00498 
00500 
00501 bool QVPropertyContainer::linkProperty(QString prop_orig, QVPropertyContainer &qvp_dest, QString prop_dest, LinkType link_type)
00502         {
00503         return linkProperty(prop_orig, &qvp_dest, prop_dest, link_type);
00504         }
00505 
00506 bool QVPropertyContainer::linkProperty(QVPropertyContainer &destinyContainer, QString destinyPropertyName, LinkType linkType)
00507         {
00508         return linkProperty(&destinyContainer, destinyPropertyName, linkType);
00509         }
00510 
00511 bool QVPropertyContainer::linkProperty(QString sourcePropertyName, QVPropertyContainer &destinyContainer, LinkType linkType)
00512         {
00513         return linkProperty(sourcePropertyName, &destinyContainer, linkType);
00514         }
00515 
00516 void QVPropertyContainer::linkProperty(QVPropertyContainer &container, LinkType linkType)
00517         {
00518         linkProperty(&container, linkType);
00519         }
00520 
00522 
00523 bool QVPropertyContainer::linkUnspecifiedInputProperty(QVPropertyContainer *srcCont, QString srcProp, LinkType linkType)
00524         {
00525         if (containsProperty(srcProp))
00526                 return srcCont->linkProperty(srcProp, this, srcProp, linkType);
00527         else
00528                 return false;
00529         }
00530 
00531 bool QVPropertyContainer::linkUnspecifiedOutputProperty(QVPropertyContainer *dstCont, QString dstProp, LinkType linkType)
00532         {
00533         if (containsProperty(dstProp))
00534                 return linkProperty(dstProp, dstCont, dstProp, linkType);
00535         else
00536                 return false;
00537         }
00538 
00540 
00541 void QVPropertyContainer::addInputLink(QString prop_dest, QVPropertyContainerLink *link)
00542         {
00543         inputLinks[prop_dest] = link;
00544         link_flags[prop_dest] |= linkedInputFlag;
00545         }
00546 
00547 bool QVPropertyContainer::unlinkProperty(QString origName, QVPropertyContainer *destCont, QString destName)
00548         {
00549         QList<QVPropertyContainerLink*> linkList = outputLinks[origName];
00550         foreach(QVPropertyContainerLink* link, linkList) {
00551                 if ( (link->qvp_orig == this) && (link->prop_orig == origName) &&
00552                         (link->qvp_dest == destCont) && (link->prop_dest == destName) ) {
00553                         if (!link->markedForDeletion) {
00554                                 bool isSequential = (link->link_type == SequentialLink) ? true : false;
00555                                 link->markedForDeletion = TRUE;
00556                                 // Protect against a possible pending acquire() for our output
00557                                 // in other holders:
00558                                 link->SyncSemaphoreOut.release();
00559                                 destCont->treatUnlinkInputProperty(destName, this, origName);
00560                                 // This ProcessSequentialUnlink cannot generate a core for acceding to all group nodes
00561                                 // because the two QVPropertyContainer's are in the same thread and its unlinks
00562                                 // are doing sequentialy
00563                                 if (isSequential) ProcessSequentialUnlink(this, destCont);
00564                                 informer.emitChange(QVPropertyContainerChange(this->getName(), this->getId(), QVPropertyContainerChange::LinkDel, this->getName(), this->getId(), origName, destCont->getName(), destCont->getId(), destName));
00565                         }
00566                         return TRUE;
00567                 }
00568         }
00569         return FALSE;
00570         }
00571 
00572 bool QVPropertyContainer::unlinkProperty(QString origName, QVPropertyContainer &destCont, QString destName)
00573         {
00574         return unlinkProperty(origName, &destCont, destName);
00575         }
00576 
00577 bool QVPropertyContainer::treatUnlinkInputProperty(QString, QVPropertyContainer *, QString)
00578         {
00579         return true;
00580         }
00581 
00582 void QVPropertyContainer::unlink()
00583 {
00584         foreach(QVPropertyContainerLink* link, inputLinks.values()) {
00585                 if (!link->markedForDeletion) {
00586                         bool isSequential = (link->link_type == SequentialLink) ? true : false;
00587                         link->markedForDeletion = TRUE;
00588                         // Protect against a possible pending acquire() from our input
00589                         // in other holders:
00590                         link->SyncSemaphoreIn.release();
00591                         // This ProcessSequentialUnlink cannot generate a core for acceding to all group nodes
00592                         // because the two QVPropertyContainer's are in the same thread and its unlinks
00593                         // are doing sequentialy
00594                         if (isSequential) ProcessSequentialUnlink(link->qvp_orig, link->qvp_dest);
00595                         informer.emitChange(QVPropertyContainerChange(this->getName(), this->getId(), QVPropertyContainerChange::LinkDel, link->qvp_orig_name, link->qvp_orig_id, link->prop_orig, link->qvp_dest_name, link->qvp_dest_id, link->prop_dest));
00596                 }
00597         }
00598 
00599         foreach(QList<QVPropertyContainerLink*> linkList, outputLinks.values()) {
00600                 foreach(QVPropertyContainerLink* link, linkList) {
00601                         if (!link->markedForDeletion) {
00602                                 bool isSequential = (link->link_type == SequentialLink) ? true : false;
00603                                 link->markedForDeletion = TRUE;
00604                                 // Protect against a possible pending acquire() for our output
00605                                 // in other holders:
00606                                 link->SyncSemaphoreOut.release();
00607                                 if (link->qvp_dest != NULL) link->qvp_dest->treatUnlinkInputProperty(link->prop_dest, this, link->prop_orig);
00608                                 // This ProcessSequentialUnlink cannot generate a core for acceding to all group nodes
00609                                 // because the two QVPropertyContainer's are in the same thread and its unlinks
00610                                 // are doing sequentialy
00611                                 if (isSequential) ProcessSequentialUnlink(link->qvp_orig, link->qvp_dest);
00612                                 informer.emitChange(QVPropertyContainerChange(this->getName(), this->getId(), QVPropertyContainerChange::LinkDel, link->qvp_orig_name, link->qvp_orig_id, link->prop_orig, link->qvp_dest_name, link->qvp_dest_id, link->prop_dest));
00613                         }
00614                 }
00615         }
00616 }
00617 
00618 void QVPropertyContainer::readInputProperties()
00619         {
00620         // We read every linked input property from its source, protecting
00621         // the read with a standard RWLock (that here we just lock for read).
00622         // The only caveat is that if a property is synchronously read, then
00623         // we must wait for the producer to write it first. We implement that
00624         // by waiting on the SyncSemaphoreOut of the link. Also, in this
00625         // case when we finish reading the property, we signal every possible
00626         // waiting writer that, regarding this specific link, it can write now
00627         // a new value if it needs to, because we have read the old value yet.
00628         // This is implemented by releasing the SyncSemaphoreIn associated to
00629         // the link.
00630         QMutableMapIterator<QString, QVPropertyContainerLink*> i(inputLinks);
00631         while (i.hasNext()) {
00632                 i.next();
00633                 QVPropertyContainerLink *link = i.value();
00634 
00635                 // Possible link deletion:
00636                 if(link->markedForDeletion) {
00637                         i.remove();
00638                         toDeleteLink(link);
00639                 }
00640                 else {
00641                         if(link->link_type == SynchronousLink) {
00642                                 link->SyncSemaphoreOut.acquire();
00643                         }
00644                         if (link->link_type != SequentialLink)
00645                                 link->qvp_orig->RWLock.lockForRead();
00646                         //this->setPropertyValueQVariant(link->prop_dest,link->qvp_orig->safelyCopiedVariants[link->prop_orig]);
00647                         this->variants[link->prop_dest] = link->qvp_orig->safelyCopiedVariants[link->prop_orig];
00648                         if (link->link_type != SequentialLink)
00649                                 link->qvp_orig->RWLock.unlock();
00650                         if(link->link_type == SynchronousLink) {
00651                                 link->SyncSemaphoreIn.release();
00652                         }
00653                 }
00654         }
00655         informer.emitChange(QVPropertyContainerChange(this->getName(), this->getId(), QVPropertyContainerChange::PropertiesValues));
00656 }
00657 
00658 void QVPropertyContainer::writeOutputProperties()
00659         {
00660         QMutableMapIterator<QString, QList<QVPropertyContainerLink*> >i(outputLinks);
00661 
00662         // For every QVP synchronously linked to this QVP's output, we ask
00663         // for permision to write a new output (that will only be possible if
00664         // all of these QVP's have read their inputs already):
00665         bool someSequential = false;
00666         while (i.hasNext()) {
00667                 i.next();
00668                 QListIterator<QVPropertyContainerLink*> j(i.value());
00669                 while(j.hasNext()) {
00670                         QVPropertyContainerLink *link = j.next();
00671                         if(link->link_type == SynchronousLink and not link->markedForDeletion) {
00672                                 link->SyncSemaphoreIn.acquire();
00673                         }
00674                         else if(link->link_type == SequentialLink and not link->markedForDeletion) {
00675                                 someSequential = true;
00676                         }
00677                 }
00678         }
00679 
00680         // Now, we write a new coherent state, simply protected by the
00681         // corresponding RWLock:
00682         i.toFront();
00683         if (!someSequential)
00684                 this->RWLock.lockForWrite();
00685         while (i.hasNext()) {
00686                 i.next();
00687                 QString prop_orig = i.key();
00688                 safelyCopiedVariants[prop_orig] = variants[prop_orig];
00689         }
00690         if (!someSequential)
00691                 this->RWLock.unlock();
00692 
00693         // Finally, we signal to QVP's synchronously linked to this QVP's output
00694         // that there is a new coherent output, by releasing the corresponding
00695         // semaphore.
00696         i.toFront();
00697         while (i.hasNext()) {
00698                 i.next();
00699                 QMutableListIterator<QVPropertyContainerLink*> j(i.value());
00700                 while(j.hasNext()) {
00701                         QVPropertyContainerLink *link = j.next();
00702                         if(link->link_type == SynchronousLink and not link->markedForDeletion) {
00703                                 link->SyncSemaphoreOut.release();
00704                         }
00705                         // Possible link deletion:
00706                         if(link->markedForDeletion) {
00707                                 j.remove();
00708                                 toDeleteLink(link);
00709                                 if(i.value().isEmpty()) {
00710                                         i.remove();
00711                                         break;
00712                                 }
00713                         }
00714                 }
00715         }
00716 }
00717 
00718 void QVPropertyContainer::toDeleteLink(QVPropertyContainerLink* link)
00719         {
00720                 if (link->qvp_orig == this) {
00721                         link->qvp_orig = NULL;
00722                 }
00723                 else if (link->qvp_dest == this) {
00724                         link->qvp_dest = NULL;
00725                 }
00726 
00727                 if ((link->qvp_orig == NULL) && (link->qvp_dest == NULL)) delete link;
00728         }
00729 
00730 void QVPropertyContainer::setLastError(QString str) const
00731         { errorString = str; }
00732 
00733 QVPropertyContainer *QVPropertyContainer::getSourceContainer(const QString name) const
00734         {
00735                 const QMap<QString, QVPropertyContainerLink* > inLinks = getInputLinks();
00736                 if (inLinks.contains(name))
00737                         return inLinks.value(name)->qvp_orig;
00738 
00739                 return NULL;
00740         }
00741 
00742 QList<QVPropertyContainer *> QVPropertyContainer::getDestinyContainers(const QString name) const
00743         {
00744                 QList<QVPropertyContainer *> list;
00745 
00746                 if (outputLinks.contains(name))
00747                         foreach(QVPropertyContainerLink* link, outputLinks.value(name))
00748                                 if ( (link->qvp_dest != NULL) && (!list.contains(link->qvp_dest)) ) list.append(link->qvp_dest);
00749 
00750                 return list;
00751         }
00752 
00753 QString QVPropertyContainer::getSourceProperty(const QString name) const
00754         {
00755                 const QMap<QString, QVPropertyContainerLink* > inLinks = getInputLinks();
00756                 if (inLinks.contains(name))
00757                         return inLinks.value(name)->prop_orig;
00758 
00759                 return QString();
00760         }
00761 
00762 bool QVPropertyContainer::isSynchronous(const QString name) const
00763         {
00764                 const QMap<QString, QVPropertyContainerLink* > inLinks = getInputLinks();
00765                 if (inLinks.contains(name))
00766                         return (inLinks.value(name)->link_type == SynchronousLink);
00767 
00768                 return FALSE;
00769         }
00770 
00771 bool QVPropertyContainer::isSequential(const QString name) const
00772         {
00773                 const QMap<QString, QVPropertyContainerLink* > inLinks = getInputLinks();
00774                 if (inLinks.contains(name))
00775                         return (inLinks.value(name)->link_type == SequentialLink);
00776 
00777                 return FALSE;
00778         }
00779 
00780 bool QVPropertyContainer::areSynchronized(const QList<QVPropertyContainer *> conts)
00781         {
00782         QVDisjointSet dSet(conts.size());
00783 
00784         for (int i = 0; i < conts.size(); i++)
00785                 for (int j = i+1; j < conts.size(); j++) // for each pair of container
00786                         {
00787                         bool find = false; // only need one syncrhonous property to be synchronized
00788 
00789                         if (conts.at(i)->getId() == conts.at(j)->getId()) // if they are the same container, they are synchronized
00790                                 {
00791                                 dSet.unify(i, j); // unify their sets
00792                                 find = true;
00793                                 }
00794                         if (!find)
00795                                 {
00796                                 const QMap<QString, QVPropertyContainerLink* > inLinksI = conts.at(i)->getInputLinks();
00797                                 foreach(QVPropertyContainerLink* proConLink, inLinksI.values())        // for each first container's linked input property
00798                                         if ( (proConLink->qvp_orig_id == conts.at(j)->getId()) &&         // if is linked from second container
00799                                              (proConLink->link_type != AsynchronousLink)               ) // and is a synchronous link
00800                                                 {
00801                                                 dSet.unify(i, j); // unify their sets
00802                                                 find = true;
00803                                                 break;
00804                                                 }
00805                                 }
00806                         if (!find)
00807                                 {
00808                                 const QMap<QString, QVPropertyContainerLink* > inLinksJ = conts.at(j)->getInputLinks();
00809                                 foreach(QVPropertyContainerLink* proConLink, inLinksJ.values())        // for each second container's linked input property
00810                                         if ( (proConLink->qvp_orig_id == conts.at(i)->getId()) &&         // if is linked from first container
00811                                              (proConLink->link_type != AsynchronousLink)               ) // and is a synchronous link
00812                                                 {
00813                                                 dSet.unify(i, j); // unify their sets
00814                                                 break;
00815                                                 }
00816                                 }
00817                         }
00818 
00819         return (dSet.numberOfSets() == 1);
00820         }
00821 
00822 template <> bool QVPropertyContainer::parseArgument<bool>(const QString parameter, const QString value)
00823         {
00824         if (value.toLower() == "true" || value.toLower() == "false")
00825                 {
00826                 //variants[parameter] = QVariant::fromValue<bool>(value.toLower() == "true");
00827                 setPropertyValue<bool>(parameter,value.toLower() == "true");
00828                 return TRUE;
00829                 }
00830         else {
00831                 errorString = "QVPropertyContainer::parseArgument(): holder " + getName() +
00832                                         ": value " + value +
00833                                         " is not a valid boolean value for parameter " +
00834                                         parameter + ".\n";
00835                 return FALSE;
00836                 }
00837         }
00838 
00839 template <> bool QVPropertyContainer::parseArgument<int>(const QString parameter, const QString value)
00840         {
00841         bool okInt;
00842         int intValue = value.toInt(&okInt);
00843         if(not okInt)
00844                 {
00845                 errorString = "QVPropertyContainer::parseArgument(): holder " + getName() +
00846                                         ": value " + value +
00847                                         " is not a valid integer value for parameter " +
00848                                         parameter + ".\n";
00849                 return FALSE;
00850                 }
00851         //variants[parameter] = QVariant::fromValue<int>(intValue);
00852         setPropertyValue<int>(parameter,intValue);
00853         return TRUE;
00854         }
00855 
00856 template <> bool QVPropertyContainer::parseArgument<double>(const QString parameter, const QString value)
00857         {
00858         bool okDouble;
00859         double doubleValue = value.toDouble(&okDouble);
00860         if(not okDouble)
00861                 {
00862                 errorString = "QVPropertyContainer::parseArgument(): holder " + getName() +
00863                                         ": value " + value +
00864                                         " is not a valid double value for parameter " +
00865                                         parameter + ".\n";
00866                 return FALSE;
00867                 }
00868         //variants[parameter] = QVariant::fromValue<double>(doubleValue);
00869         setPropertyValue<double>(parameter,doubleValue);
00870         return TRUE;
00871         }
00872 
00873 template <> bool QVPropertyContainer::parseArgument<QString>(const QString parameter, const QString value)
00874         {
00875         //variants[parameter] = QVariant::fromValue<QString>(value);
00876         setPropertyValue<QString>(parameter,value);
00877         return TRUE;
00878         }
00879 
00880 // the sequential subgraphs cannot have cicles
00881 bool QVPropertyContainer::ProcessPosibleSequentialLink(QVPropertyContainer *destCont)
00882         {
00883                 // Return false if the link will generate a sequential links cicle
00884                 if (haveSyncPrecursor(destCont))
00885                         return false;
00886 
00887                 // If they are sequentiely connected yet, do nothing
00888                 if (destCont->haveSyncPrecursor(this))
00889                         return true;
00890 
00891                 // Update de deeps
00892                 destCont->updateDeep(deepLevel);
00893 
00894                 // choose the new master of the join group
00895                 QVPropertyContainer *winnerMaster, *loserMaster;
00896                 if (this->master->getId() < destCont->master->getId()) {
00897                         winnerMaster = this->master;
00898                         loserMaster = destCont->master;
00899                 }
00900                 else {
00901                         winnerMaster = destCont->master;
00902                         loserMaster = this->master;
00903                 }
00904 
00905                 // change the masters of the group's items
00906                 for(int i = 0; i < loserMaster->slavesByLevel.size(); i++) {
00907                         if(winnerMaster->slavesByLevel.size() <= i)
00908                                 winnerMaster->slavesByLevel.append(QList<QVPropertyContainer *>());
00909 
00910                         QList<QVPropertyContainer *> level = loserMaster->slavesByLevel[i];
00911                         foreach(QVPropertyContainer *slave, level) {
00912                                 slave->master = winnerMaster;
00913                                 winnerMaster->slavesByLevel[i].append(slave);
00914                         }
00915                 }
00916 
00917                 // update the old master (looser master)
00918                 foreach(QList<QVPropertyContainer *> level, loserMaster->slavesByLevel) {
00919                         loserMaster->slavesByLevel.removeAll(level);
00920                 }
00921 
00922                 return true;
00923         }
00924 
00925 // the sequential subgraphs cannot have cicles
00926 void QVPropertyContainer::updateDeep(int origDeep)
00927          {
00928                 int newDeep = origDeep + 1; // sets its minim deep
00929                 foreach(QVPropertyContainerLink* inLink, getInputLinks()) { // calculate the new deep
00930                         QVPropertyContainer *cont = inLink->qvp_orig;
00931                         if ( (cont != NULL) && (inLink->markedForDeletion == FALSE) && (inLink->link_type == SequentialLink) )
00932                                 if (cont->deepLevel >= newDeep)
00933                                         newDeep = cont->deepLevel + 1;
00934                 }
00935 
00936                 if (newDeep != deepLevel) { // if the deep changes, change his level position, his deep, and his successors deep
00937                         master->slavesByLevel[deepLevel].removeAll(this);
00938                         deepLevel = newDeep;
00939         
00940                         while(master->slavesByLevel.size() <= deepLevel)
00941                                 master->slavesByLevel.append(QList<QVPropertyContainer *>());
00942                         master->slavesByLevel[deepLevel].append(this);
00943         
00944                         foreach(QString prop, getPropertyList())
00945                                 foreach(QVPropertyContainer *container, getDestinySequentialContainers(prop))
00946                                         container->updateDeep(deepLevel);
00947                 }
00948         }
00949 
00950 // the sequential subgraphs cannot have cicles
00951 void QVPropertyContainer::ProcessSequentialUnlink(QVPropertyContainer *origCont, QVPropertyContainer *destCont)
00952         {
00953                 // if the group still join do nothing
00954                 if (destCont->haveSyncPrecursor(origCont)) return;
00955 
00956                 // if origCont is in the masterless subgroup, they becomes a new master
00957                 if( (origCont->master != origCont) && !origCont->haveSyncPrecursor(origCont->master) && !origCont->master->haveSyncPrecursor(origCont) ) {
00958                         // changes all subgroup master
00959                         origCont->changeMaster(origCont);
00960                         origCont->propagateBackwardMasterChange(origCont);
00961                         origCont->propagateForwardMasterChange(origCont);
00962 
00963                         // update its (and its succesors) deep
00964                         origCont->updateDeep(-1);
00965 
00966                         // they must update other subgroup deep
00967                         destCont->updateDeep(-1);
00968                 }
00969                 // if destCont is in the masterless subgroup, they becomes a new master
00970                 else if( (destCont->master != destCont) && !destCont->haveSyncPrecursor(destCont->master) && !destCont->master->haveSyncPrecursor(destCont) ) {
00971                         // changes all subgroup master
00972                         destCont->propagateBackwardMasterChange(destCont);
00973                         destCont->propagateForwardMasterChange(destCont);
00974                         destCont->changeMaster(destCont);
00975 
00976                         // update its (and its succesors) deep
00977                         destCont->updateDeep(-1);
00978                 }
00979         }
00980 
00981 // the sequential subgraphs cannot have cicles
00982 bool QVPropertyContainer::haveSyncPrecursor(QVPropertyContainer *precursor)
00983         {
00984                 foreach(QString prop, getPropertyList()) {
00985                         if (/*isSynchronous(prop) || */isSequential(prop)) {
00986                                 const QMap<QString, QVPropertyContainerLink* > inLinks = getInputLinks();
00987                                 QVPropertyContainer *cont = inLinks.value(prop)->qvp_orig;
00988                                 if ( (cont != NULL) && (inLinks.value(prop)->markedForDeletion == FALSE) )
00989                                         if ( (precursor == cont) || (cont->haveSyncPrecursor(precursor)) ) return true;
00990                         }
00991                 }
00992                 return false;
00993         }
00994 
00995 // the sequential subgraphs cannot have cicles
00996 void QVPropertyContainer::propagateBackwardMasterChange(QVPropertyContainer *newMaster)
00997         {
00998                 foreach(QVPropertyContainerLink *link, getInputLinks()) {
00999                         QVPropertyContainer *cont = link->qvp_orig;
01000                         if ( (cont != NULL) && (link->markedForDeletion == FALSE) && (link->link_type == SequentialLink) && (cont->master != newMaster) ) {
01001                                 cont->changeMaster(newMaster);
01002                                 cont->propagateBackwardMasterChange(newMaster);
01003                                 cont->propagateForwardMasterChange(newMaster);
01004                         }
01005                 }
01006 
01007         }
01008 
01009 // the sequential subgraphs cannot have cicles
01010 void QVPropertyContainer::propagateForwardMasterChange(QVPropertyContainer *newMaster)
01011         {
01012                 foreach(QString prop, getPropertyList()) {
01013                         foreach(QVPropertyContainer *container, getDestinySequentialContainers(prop)) {
01014                                 if (container->master != newMaster) {
01015                                         container->changeMaster(newMaster);
01016                                         container->propagateForwardMasterChange(newMaster);
01017                                         container->propagateBackwardMasterChange(newMaster);
01018                                 }
01019                         }
01020                 }
01021         }
01022 
01023 // the sequential subgraphs cannot have cicles
01024 void QVPropertyContainer::changeMaster(QVPropertyContainer *newMaster)
01025         {
01026                 master->slavesByLevel[deepLevel].removeAll(this);
01027                 master = newMaster;
01028 
01029                 while(master->slavesByLevel.size() <= deepLevel)
01030                         master->slavesByLevel.append(QList<QVPropertyContainer *>());
01031                 master->slavesByLevel[deepLevel].append(this);
01032         }
01033 
01034 // the sequential subgraphs cannot have cicles
01035 QList<QVPropertyContainer *> QVPropertyContainer::getDestinySequentialContainers(const QString name) const
01036         {
01037                 QList<QVPropertyContainer *> list;
01038 
01039                 if (outputLinks.contains(name))
01040                         foreach(QVPropertyContainerLink* link, outputLinks.value(name)) {
01041                                 if ( (link->qvp_dest != NULL) &&
01042                                          (link->markedForDeletion == FALSE) &&
01043                                          (!list.contains(link->qvp_dest)) &&
01044                                          (link->link_type == SequentialLink) ) list.append(link->qvp_dest);
01045                         }
01046 
01047                 return list;
01048         }



QVision framework. PARP research group, copyright 2007, 2008.