PARP Research Group University of Murcia, Spain


src/qvcore/qvpropertycontainer.h

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 #ifndef QVPROPERTYHOLDER_H
00026 #define QVPROPERTYHOLDER_H
00027 
00028 #include <QStringList>
00029 #include <QVariant>
00030 #include <QRegExp>
00031 #include <QSet>
00032 #include <QReadWriteLock>
00033 #include <QSemaphore>
00034 #include <QDebug>
00035 #include <QVPropertyContainerChange>
00036 
00037 #include <iostream>
00038 #include <QVApplication>
00039 
00040 
00050 class QVPropertyContainerInformer : public QObject
00051         {
00052                 Q_OBJECT
00053                 public:
00057                         void emitChange(QVPropertyContainerChange change) {
00058                                 emit changed(change);
00059                         }
00060                 signals:
00064                         void changed(QVPropertyContainerChange change);
00065         };
00066 
00081 class QVPropertyContainer
00082         {
00083 
00084         public:
00126                 typedef enum {AsynchronousLink,SynchronousLink,SequentialLink} LinkType;
00127 
00132                 typedef enum {noInputOutputFlag=0x0,inputFlag=0x1,outputFlag=0x2, guiInvisible=0x4, internalProp=0x8} PropertyFlag;
00133                 Q_DECLARE_FLAGS(PropertyFlags, PropertyFlag)
00134 
00135                 #ifndef DOXYGEN_IGNORE_THIS
00136                 typedef enum {noLinkFlag=0x0,linkedInputFlag=0x1,linkedOutputFlag=0x2} PropertyLinkFlag;
00137                 Q_DECLARE_FLAGS(PropertyLinkFlags, PropertyLinkFlag)
00138                 #endif
00139 
00143                 QVPropertyContainer(const QString name = QString());
00144 
00148                 QVPropertyContainer(const QVPropertyContainer &cont);
00149 
00153                 QVPropertyContainer & operator=(const QVPropertyContainer &cont);
00154 
00160                 virtual ~QVPropertyContainer();
00161 
00165                 void setName(const QString name);
00166 
00169                 const QString getName() const;
00170 
00173                 const uint getId() const;
00174 
00180                 bool operator==(const QVPropertyContainer &cont) const;
00181 
00187                 QList<QString> getPropertyList() const;
00188 
00195                 template <class Type> QList<QString> getPropertyListByType() const
00196                         {
00197                         QList<QString> result;
00198                         QList<QString> names = variants.keys();
00199 
00200                         for(QList<QString>::iterator i = names.begin();i != names.end();++i)
00201                                 if(isType<Type>(*i))
00202                                         result.append(*i);
00203 
00204                         return result;
00205                         }
00206 
00215                 template <class Type> bool isType(QString name,bool *ok = NULL) const
00216                         {
00217                         if(not checkExists(name,"QVPropertyContainer::propertyIsType()"))
00218                                 {
00219                                 if(ok != NULL) *ok = FALSE;
00220                                 return FALSE;
00221                                 }
00222                         if(ok != NULL) *ok = TRUE;
00223                         QVariant::Type type = QVariant::fromValue(Type()).type();
00224                         if ((type != QVariant::UserType) && (variants[name].type() == type))
00225                                 return TRUE;
00226                         if (variants[name].userType() == QVariant::fromValue(Type()).userType())
00227                                 return TRUE;
00228                         return FALSE;
00229                         }
00230 
00235                 bool containsProperty(const QString name) const;
00236 
00244                 int getPropertyType(const QString name, bool *ok = NULL) const;
00245 
00253                 template <class Type> bool addProperty(const QString name,
00254                         const PropertyFlags flags = inputFlag,
00255                         const Type & value = Type(), const QString info = QString("(Info not available)"))
00256                         {
00257                         if (addPropertyFromQVariant(name, flags, QVariant::fromValue(value), info))
00258                                 setPropertyFromArguments<Type>(name);
00259                         else
00260                                 return FALSE;
00261 
00262                         return TRUE;
00263                         }
00264 
00274                 template <class Type> bool addProperty(const QString name,
00275                         const PropertyFlags flags,
00276                         const Type & value, const QString info,
00277                         const Type & minValue, const Type & maxValue)
00278                         {
00279                         if (addProperty<Type>(name, flags, value, info))
00280                                 setPropertyRange<Type>(name, minValue, maxValue);
00281                         else
00282                                 return FALSE;
00283 
00284                         return TRUE;
00285                         }
00286 
00294                 bool addPropertyFromQVariant(const QString &name, const PropertyFlags flags, QVariant variant, const QString info)
00295                         {
00296                         //qDebug() << "PEO adding property" << name << "to container" << getName();
00297                         if(not checkIsNewProperty(name,"QVPropertyContainer::addProperty()"))
00298                                 return FALSE;
00299                         insertion_order.push_back(name);
00300                         //qDebug() << "PEO insertion_order after new property " << name << "->" << insertion_order;
00301                         _info[name] = info;
00302                         io_flags[name] = flags;
00303                         link_flags[name] = noLinkFlag;
00304 
00305                         variants[name] = variant;
00306 
00307                         informer.emitChange(QVPropertyContainerChange(this->getName(), this->getId(), QVPropertyContainerChange::PropertyAdd, name));
00308                         return TRUE;
00309                         }
00310 
00315                 bool removeProperty(const QString name);
00316 
00317 /*              /// \deprecated Should set range in the constructor.
00318                 bool setPropertyRange(const QString name, const double & minimum, const double & maximum);
00319 
00321                 bool setPropertyRange(QString name, int & minimum, int & maximum); */
00322 
00327                 bool hasRange(const QString name) const;
00328 
00333                 PropertyFlags getPropertyFlags(const QString name) const { return io_flags[name]; }
00334 
00339                 bool isInput(const QString name) const;
00340 
00345                 bool isOutput(const QString name) const;
00346 
00351                 bool isGUIInvisible(const QString name) const;
00352 
00357                 bool isLinkedInput(const QString name) const;
00358 
00363                 bool isLinkedOutput(const QString name) const;
00364 
00375                 template <class Type> bool setPropertyValue(const QString name, const Type &value)
00376                         {
00377                         if(not checkExists(name,"QVPropertyContainer::setPropertyValue()"))
00378                                 return FALSE;
00379                         else if (not correctRange(name,value))
00380                                 return FALSE;
00381                         else {
00382                                 QVariant variant =  QVariant::fromValue<Type>(value);
00383                                 variants[name] = variant;
00384 
00385                                 informer.emitChange(QVPropertyContainerChange(this->getName(), this->getId(), QVPropertyContainerChange::PropertyValue, name, variant));
00386                                 return TRUE;
00387                                 }
00388                         }
00389 
00396                 template <class Type> Type getPropertyValue(const QString name, bool *ok = NULL) const
00397                         {
00398                         if (not checkExists(name,"QVPropertyContainer::getPropertyValue()"))
00399                                 if(ok != NULL) *ok = FALSE;
00400                         else
00401                                 if(ok != NULL) *ok = TRUE;
00402                         return variants[name].value<Type>();
00403                         }
00404 
00411                 QVariant getPropertyQVariantValue(const QString name, bool *ok = NULL) const;
00412 
00419                 template <class Type> Type getPropertyMaximum(const QString name, bool *ok = NULL) const
00420                         {
00421                         if(not checkExists(name,"QVPropertyContainer::getPropertyMaximum()"))
00422                                 if(ok != NULL) *ok = FALSE;
00423                         else if(not maximum.contains(name) and not minimum.contains(name))
00424                                 {
00425                                 QString str = QString("QVPropertyContainer::getPropertyMaximum():")
00426                                                           + QString(" property ") + name
00427                                                           + QString(" has no maximum value in ")
00428                                                           + QString("holder ") + getName() + QString(".");
00429                                 setLastError(str);
00430                                 if(qvApp->isRunning()) {
00431                                         std::cerr << qPrintable("Warning: " + str + "\n");
00432                                 } // Otherwise, qApp will show the error and won't start the program.
00433                                 if(ok != NULL) *ok = FALSE;
00434                                 }
00435                         else
00436                                 if(ok != NULL) *ok = TRUE;
00437                         return maximum[name].value<Type>();
00438                         }
00439 
00446                 template <class Type> Type getPropertyMinimum(const QString name, bool *ok = NULL) const
00447                         {
00448                         if(not checkExists(name,"QVPropertyContainer::getPropertyMinimum()"))
00449                                 if(ok != NULL) *ok = FALSE;
00450                         else if(not maximum.contains(name) and not minimum.contains(name))
00451                                 {
00452                                 QString str = QString("QVPropertyContainer::getPropertyMinimum():")
00453                                                           + QString(" property ") + name
00454                                                           + QString(" has no minimum value in ")
00455                                                           + QString("holder ") + getName() + QString(".");
00456                                 setLastError(str);
00457                                 if(qvApp->isRunning()) {
00458                                         std::cerr << qPrintable("Warning: " + str + "\n");
00459                                 } // Otherwise, qApp will show the error and won't start the program.
00460                                 if(ok != NULL) *ok = FALSE;
00461                                 }
00462                         else
00463                                 if(ok != NULL) *ok = TRUE;
00464                         return minimum[name].value<Type>();
00465                         }
00466 
00474                 QString getPropertyInfo(const QString name, bool *ok = NULL) const;
00475 
00481                 QString getLastError() const;
00482 
00490                 const QString infoInputProperties() const;
00491 
00510                 virtual bool linkProperty(QString sourcePropName, QVPropertyContainer *destinyContainer, QString destinyPropName, LinkType linkType = AsynchronousLink);
00511                 virtual bool linkProperty(QString sourcePropName, QVPropertyContainer &destinyContainer, QString destinyPropName, LinkType linkType = AsynchronousLink);
00512                 virtual bool linkProperty(QVPropertyContainer *destinyContainer, QString destinyPropName, LinkType linkType = AsynchronousLink);
00513                 virtual bool linkProperty(QVPropertyContainer &destinyContainer, QString destinyPropName, LinkType linkType = AsynchronousLink);
00514                 virtual bool linkProperty(QString sourcePropName, QVPropertyContainer *destinyContainer, LinkType linkType = AsynchronousLink);
00515                 virtual bool linkProperty(QString sourcePropName, QVPropertyContainer &destinyContainer, LinkType linkType = AsynchronousLink);
00516 
00528                 virtual void linkProperty(QVPropertyContainer *container, LinkType linkType = AsynchronousLink);
00529                 virtual void linkProperty(QVPropertyContainer &container, LinkType linkType = AsynchronousLink);
00530 
00545                 virtual bool unlinkProperty(QString origName, QVPropertyContainer *destCont, QString destName);
00546                 virtual bool unlinkProperty(QString origName, QVPropertyContainer &destCont, QString destName);
00547 
00553                 virtual void unlink();
00554 
00559                 static bool areSynchronized(const QList<QVPropertyContainer *> containers);
00560 
00566                 QVPropertyContainerInformer *getInformer() { return &informer; }
00567 
00568 /*              /// @brief Gets the global QVPropertyContainerInformer.
00574                 static QVPropertyContainerInformer *getGlobalInformer() { return &globalInformer; } */
00575 
00580                 QVPropertyContainer *getSourceContainer(const QString name) const;
00581 
00586                 QList<QVPropertyContainer *> getDestinyContainers(const QString name) const;
00587 
00592                 QString getSourceProperty(const QString name) const;
00593 
00598                 bool isSynchronous(const QString name) const;
00599 
00604                 bool isSequential(const QString name) const;
00605 
00606                 #ifndef DOXYGEN_IGNORE_THIS
00607                 virtual bool linkUnspecifiedInputProperty(QVPropertyContainer *sourceContainer, QString sourcePropName, LinkType linkType = AsynchronousLink);
00608                 virtual bool linkUnspecifiedOutputProperty(QVPropertyContainer *destContainer, QString destPropName, LinkType linkType = AsynchronousLink);
00609                 virtual bool treatUnlinkInputProperty(QString destPropName, QVPropertyContainer *sourceCont, QString sourcePropName);
00610                 #endif
00611 
00612                 const bool isSequentialGroupMaster() const      { return master == this; }
00613                 QVPropertyContainer *getMaster() const { return master; }
00614 
00615         protected:
00623                 void readInputProperties();
00624 
00635                 void writeOutputProperties();
00636 
00676                 template <class Type> bool parseArgument(const QString parameter, const QString value);
00677 
00686                 void setLastError(QString str) const;
00687 
00692                 uint inputPropertyWorker(QString prop) const
00693                         {
00694                         if (inputLinks.contains(prop))
00695                                 return inputLinks[prop]->qvp_orig->getId();
00696                         else
00697                                 return 0;
00698                         }
00699 
00700 
00701                 QVPropertyContainerInformer informer;
00702 
00703                 // the group of slaves QVPropertyContainer, sorts by level of deep, in witch it is its master
00704                 QList<QList<QVPropertyContainer *> > slavesByLevel;
00705 
00706         //protected:
00707 
00708         private:
00709                 QString name;
00710                 uint ident;
00711                 mutable QString errorString;
00712                 QMap<QString, QVariant> variants,safelyCopiedVariants;
00713                 QMap<QString, QVariant> minimum, maximum;
00714                 QMap<QString, QString> _info;
00715                 QMap<QString, PropertyFlags> io_flags;
00716                 QMap<QString, PropertyLinkFlags> link_flags;
00717                 QList<QString> insertion_order;
00718 
00719                 static uint maxIdent;
00720                 static uint getNewIdent() { return ++maxIdent; }
00721 //              static QVPropertyContainerInformer globalInformer;
00722 
00723                 QReadWriteLock RWLock;
00724                 class QVPropertyContainerLink {
00725                   public:
00726                         QVPropertyContainerLink(QVPropertyContainer *_qvp_orig,QString _prop_orig,QVPropertyContainer *_qvp_dest,QString _prop_dest,LinkType _link_type) : qvp_orig(_qvp_orig), prop_orig(_prop_orig), qvp_orig_name(_qvp_orig->getName()), qvp_orig_id(_qvp_orig->getId()), qvp_dest(_qvp_dest), prop_dest(_prop_dest), qvp_dest_name(_qvp_dest->getName()), qvp_dest_id(_qvp_dest->getId()), link_type(_link_type), markedForDeletion(FALSE) {
00727                                 // SyncSemaphoreIn value must initially be 1:
00728                                 SyncSemaphoreIn.release();
00729                                 // SyncSemaphoreOut is simply initialized with 0 (default value).
00730                         };
00731                         QVPropertyContainer *qvp_orig;
00732                         QString prop_orig, qvp_orig_name;
00733                         uint qvp_orig_id;
00734                         QVPropertyContainer *qvp_dest;
00735                         QString prop_dest, qvp_dest_name;
00736                         uint qvp_dest_id;
00737                         LinkType link_type;
00738                         QSemaphore SyncSemaphoreIn,SyncSemaphoreOut;
00739                         bool markedForDeletion;
00740                 };
00741                 QMap<QString, QVPropertyContainerLink* > inputLinks;
00742                 QMap<QString, QList<QVPropertyContainerLink*> > outputLinks;
00743 
00744                 const QMap<QString, QVPropertyContainerLink* > getInputLinks() const { return inputLinks; }
00745                 void addInputLink(QString prop_dest, QVPropertyContainerLink *link);
00746                 void toDeleteLink(QVPropertyContainerLink* link);
00747 
00748 
00749 
00750                 QVPropertyContainer *master; // the QVPropertyContainer that have been choose has his master for sequential links (if haven't sequential links itselve)
00751                 int deepLevel; // the sequential links subgraph deep level, 0 for root QVPropertyContainer
00752 
00755                 // process a posible sequential link, they must be done after the link process. Return false if the link will generate a sequential links cicle
00756                 bool ProcessPosibleSequentialLink(QVPropertyContainer *destCont);
00757                 // Update the current deep level to the maxim of origDeep and its predecessors deep level plus 1. It update its successors too
00758                 void updateDeep(int origDeep);
00759                 // process a sequential unlink, they must be done before the unlink process. They update the masters and deep levels
00760                 void ProcessSequentialUnlink(QVPropertyContainer *origCont, QVPropertyContainer *destCont);
00761                 // Indicate if they have destCont has a sequential precursor (not only a direct sequential preursor)
00762                 bool haveSyncPrecursor(QVPropertyContainer *precursor);
00763                 // Propagate resourcefuly a master change to his predecesors (and they propagate it to his predecesors and successors). The change must be done previously
00764                 void propagateBackwardMasterChange(QVPropertyContainer *newMaster);
00765                 // Propagate resourcefuly a master change to his successors (and they propagate it to his predecesors and successors). The change must be done previously
00766                 void propagateForwardMasterChange(QVPropertyContainer *newMaster);
00767                 // change its master
00768                 void changeMaster(QVPropertyContainer *newMaster);
00769                 // get the list of QVPropertyContainers that has successors of it and have been linked sequentialy
00770                 QList<QVPropertyContainer *> getDestinySequentialContainers(const QString name) const;
00773 
00774 
00775                 template <class Type> bool setPropertyRange(const QString name, const Type & minimum, const Type & maximum)
00776                         {
00777                         if(not checkExists(name,"QVPropertyContainer::setPropertyRange()"))
00778                                 return FALSE;
00779                         if(minimum <= getPropertyValue<Type>(name) and
00780                                 maximum >= getPropertyValue<Type>(name))
00781                                 {
00782                                         this->minimum[name] = QVariant::fromValue(minimum);
00783                                         this->maximum[name] = QVariant::fromValue(maximum);
00784                                         return TRUE;
00785                                 } else {
00786                                         QString str =  "QVPropertyContainer::setPropertyRange(): property " +
00787                                                                    name + " in holder " + getName() + " has value " +
00788                                                                    QString("%1").arg(getPropertyValue<Type>(name)) +
00789                                                                    ", which is not valid for the range [" +
00790                                                                    QString("%1,%2").arg(minimum).arg(maximum) + "]." ;
00791                                         setLastError(str);
00792                                         if(qvApp->isRunning()) {
00793                                                 std::cerr << qPrintable("Warning: " + str + "\n");
00794                                         } // Otherwise, qApp will show the error and won't start the program.
00795                                         return FALSE;
00796                                 }
00797                         }
00798 
00799                 template <class Type> bool setPropertyFromArguments(QString propertyName)
00800                         {
00801                         QStringList arguments = qvApp->arguments();
00802 
00803                         QMutableStringListIterator iterator(arguments);
00804                         while (iterator.hasNext())
00805                                 {
00806                                 QString argument = iterator.next();
00807                                 // Only if the name of the property holder is the same as defined in
00808                                 // the argument, in the form --name:property=value, (with name:
00809                                 // optional) the value will be intended to be applied to the property:
00810                                 if (argument.contains(QRegExp("^--")))
00811                                         {
00812                                         QString propertyContainerName(argument), parameter(argument), value(argument);
00813                                         // Checks whether in the argument string there is a name for
00814                                         // restricting the application of the value to a variable of
00815                                         // a specific property holder.
00816                                         if (argument.contains(QRegExp("^--[^=]+:")))
00817                                                 {
00818                                                 propertyContainerName.remove(QRegExp("^--")).remove(QRegExp(":.*"));
00819                                                 if(propertyContainerName != getName())
00820                                                         continue;
00821                                                 parameter.remove(QRegExp("^--[^=]+:")).remove(QRegExp("=.*$"));
00822                                                 }
00823                                         else
00824                                                 {
00825                                                 parameter.remove(QRegExp("^--")).remove(QRegExp("=.*$"));
00826                                                 }
00827                                         if(parameter != propertyName)
00828                                                 continue;
00829                                         // If we get here, then we must assign the value to the property:
00830                                         value.remove(QRegExp("^--[^=]*="));
00831                                         if(parseArgument<Type>(parameter,value))
00832                                                 {
00833                                                 if(not isInput(propertyName))
00834                                                         {
00835                                                         QString str = QString("QVPropertyContainer::setPropertyFromArguments():")
00836                                                                                 + QString(" property ") + propertyName
00837                                                                                 + QString(" in holder ") + getName()
00838                                                                                 + QString(" is not of Input type, and cannot be parsed.");
00839                                                         setLastError(str);
00840                                                         //qApp will show the error and won't start the program.
00841                                                         }
00842                                                 qvApp->setArgumentAsUsed(argument);
00843                                                 return TRUE;
00844                                                 }
00845                                         }
00846                                 }
00847                                 // If we get here, then we did not parse any value for the property:
00848                                 return FALSE;
00849                         }
00850 
00851                 bool correctRange(const QString name, const double & value) const;
00852                 // Any type inference ambiguity resolved with this:
00853                 // 1. For int values:
00854                 bool correctRange(const char *name, const int & value) const;
00855                 bool correctRange(QString name, const int & value) const;
00856 
00857                 // 2. Rest of types have no range, so we always return true:
00858                 template <typename T> bool correctRange(const QString parameter, const T & value) {
00859                         Q_UNUSED(parameter);
00860                         Q_UNUSED(value);
00861                         return TRUE;
00862                 }
00863 
00864                 bool checkExists(const QString name, const QString methodname) const;
00865                 bool checkIsNewProperty(const QString name, const QString methodname) const;
00866         };
00867 
00868 template <class Type> bool QVPropertyContainer::parseArgument(const QString parameter, const QString value)
00869         {
00870                 errorString = "QVPropertyContainer::parseArgument(): holder " + getName() +
00871                                                 ": parameter " + parameter +
00872                                                 " has an unknown type to command line parser " +
00873                                                 QString("(trying to parse value %1)").arg(value) ;
00874                 return FALSE;
00875         }
00876 
00877 template <> bool QVPropertyContainer::parseArgument<bool>(const QString parameter, const QString value);
00878 
00879 template <> bool QVPropertyContainer::parseArgument<int>(const QString parameter, const QString value);
00880 
00881 template <> bool QVPropertyContainer::parseArgument<double>(const QString parameter, const QString value);
00882 
00883 template <> bool QVPropertyContainer::parseArgument<QString>(const QString parameter, const QString value);
00884 
00885 Q_DECLARE_OPERATORS_FOR_FLAGS(QVPropertyContainer::PropertyFlags)
00886 Q_DECLARE_OPERATORS_FOR_FLAGS(QVPropertyContainer::PropertyLinkFlags)
00887 
00888 #endif



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