src/qvcore/qvpropertycontainer.h

Go to the documentation of this file.
00001 /*
00002  *      Copyright (C) 2007, 2008. 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 
00049 class QVPropertyContainerInformer : public QObject
00050         {
00051                 Q_OBJECT
00052                 public:
00056                         void emitChange(QVPropertyContainerChange change) {
00057                                 emit changed(change);
00058                         }
00059                 signals:
00063                         void changed(QVPropertyContainerChange change);
00064         };
00065 
00066 
00067 
00082 class QVPropertyContainer
00083         {
00084 
00085         public:
00118                 typedef enum {AsynchronousLink,SynchronousLink} LinkType;
00119 
00124                 typedef enum {noInputOutputFlag=0x0,inputFlag=0x1,outputFlag=0x2, guiInvisible=0x4, internalProp=0x8} PropertyFlag;
00125                 Q_DECLARE_FLAGS(PropertyFlags, PropertyFlag)
00126 
00127                 #ifndef DOXYGEN_IGNORE_THIS
00128                 typedef enum {noLinkFlag=0x0,linkedInputFlag=0x1,linkedOutputFlag=0x2} PropertyLinkFlag;
00129                 Q_DECLARE_FLAGS(PropertyLinkFlags, PropertyLinkFlag)
00130                 #endif
00131 
00135                 QVPropertyContainer(const QString name = QString());
00136 
00140                 QVPropertyContainer(const QVPropertyContainer &cont);
00141 
00145                 QVPropertyContainer & operator=(const QVPropertyContainer &cont);
00146 
00152                 virtual ~QVPropertyContainer();
00153 
00157                 void setName(const QString name);
00158 
00161                 const QString getName() const;
00162 
00165                 const uint getId() const;
00166 
00172                 bool operator==(const QVPropertyContainer &cont) const;
00173 
00179                 QList<QString> getPropertyList() const;
00180 
00187                 template <class Type> QList<QString> getPropertyListByType() const
00188                         {
00189                         QList<QString> result;
00190                         QList<QString> names = variants.keys();
00191 
00192                         for(QList<QString>::iterator i = names.begin();i != names.end();++i)
00193                                 if(isType<Type>(*i))
00194                                         result.append(*i);
00195 
00196                         return result;
00197                         }
00198 
00207                 template <class Type> bool isType(QString name,bool *ok = NULL) const
00208                         {
00209                         if(not checkExists(name,"QVPropertyContainer::propertyIsType()"))
00210                                 {
00211                                 if(ok != NULL) *ok = FALSE;
00212                                 return FALSE;
00213                                 }
00214                         if(ok != NULL) *ok = TRUE;
00215                         QVariant::Type type = QVariant::fromValue(Type()).type();
00216                         if ((type != QVariant::UserType) && (variants[name].type() == type))
00217                                 return TRUE;
00218                         if (variants[name].userType() == QVariant::fromValue(Type()).userType())
00219                                 return TRUE;
00220                         return FALSE;
00221                         }
00222 
00227                 bool containsProperty(const QString name) const;
00228 
00236                 int getPropertyType(const QString name, bool *ok = NULL) const;
00237 
00245                 template <class Type> bool addProperty(const QString name,
00246                         const PropertyFlags flags = inputFlag,
00247                         const Type & value = Type(), const QString info = QString("(Info not available)"))
00248                         {
00249                         if (addPropertyFromQVariant(name, flags, QVariant::fromValue(value), info))
00250                                 setPropertyFromArguments<Type>(name);
00251                         else
00252                                 return FALSE;
00253 
00254                         return TRUE;
00255                         }
00256 
00266                 template <class Type> bool addProperty(const QString name,
00267                         const PropertyFlags flags,
00268                         const Type & value, const QString info,
00269                         const Type & minValue, const Type & maxValue)
00270                         {
00271                         if (addProperty<Type>(name, flags, value, info))
00272                                 setPropertyRange<Type>(name, minValue, maxValue);
00273                         else
00274                                 return FALSE;
00275 
00276                         return TRUE;
00277                         }
00278 
00286                 bool addPropertyFromQVariant(const QString &name, const PropertyFlags flags, QVariant variant, const QString info)
00287                         {
00288                         //qDebug() << "PEO adding property" << name << "to container" << getName();
00289                         if(not checkIsNewProperty(name,"QVPropertyContainer::addProperty()"))
00290                                 return FALSE;
00291                         insertion_order.push_back(name);
00292                         //qDebug() << "PEO insertion_order after new property " << name << "->" << insertion_order;
00293                         _info[name] = info;
00294                         io_flags[name] = flags;
00295                         link_flags[name] = noLinkFlag;
00296 
00297                         variants[name] = variant;
00298 
00299                         informer.emitChange(QVPropertyContainerChange(this, QVPropertyContainerChange::PropertyAdd, name));
00300                         return TRUE;
00301                         }
00302 
00307                 bool removeProperty(const QString name);
00308 
00309 /*              /// \deprecated Should set range in the constructor.
00310                 bool setPropertyRange(const QString name, const double & minimum, const double & maximum);
00311 
00313                 bool setPropertyRange(QString name, int & minimum, int & maximum); */
00314 
00319                 bool hasRange(const QString name) const;
00320 
00325                 PropertyFlags getPropertyFlags(const QString name) const { return io_flags[name]; }
00326 
00331                 bool isInput(const QString name) const;
00332 
00337                 bool isOutput(const QString name) const;
00338 
00343                 bool isGUIInvisible(const QString name) const;
00344 
00349                 bool isLinkedInput(const QString name) const;
00350 
00355                 bool isLinkedOutput(const QString name) const;
00356 
00367                 template <class Type> bool setPropertyValue(const QString name, const Type &value)
00368                         {
00369                         if(not checkExists(name,"QVPropertyContainer::setPropertyValue()"))
00370                                 return FALSE;
00371                         else if (not correctRange(name,value))
00372                                 return FALSE;
00373                         else {
00374                                 variants[name] = QVariant::fromValue<Type>(value);
00375                                 informer.emitChange(QVPropertyContainerChange(this, QVPropertyContainerChange::PropertyValue, name));
00376                                 return TRUE;
00377                                 }
00378                         }
00379 
00386                 template <class Type> Type getPropertyValue(const QString name, bool *ok = NULL) const
00387                         {
00388                         if (not checkExists(name,"QVPropertyContainer::getPropertyValue()"))
00389                                 if(ok != NULL) *ok = FALSE;
00390                         else
00391                                 if(ok != NULL) *ok = TRUE;
00392                         return variants[name].value<Type>();
00393                         }
00394 
00401                 QVariant getPropertyQVariantValue(const QString name, bool *ok = NULL) const;
00402 
00409                 template <class Type> Type getPropertyMaximum(const QString name, bool *ok = NULL) const
00410                         {
00411                         if(not checkExists(name,"QVPropertyContainer::getPropertyMaximum()"))
00412                                 if(ok != NULL) *ok = FALSE;
00413                         else if(not maximum.contains(name) and not minimum.contains(name))
00414                                 {
00415                                 QString str = QString("QVPropertyContainer::getPropertyMaximum():")
00416                                                           + QString(" property ") + name
00417                                                           + QString(" has no maximum value in ")
00418                                                           + QString("holder ") + getName() + QString(".");
00419                                 setLastError(str);
00420                                 if(qvApp->isRunning()) {
00421                                         std::cerr << qPrintable("Warning: " + str + "\n");
00422                                 } // Otherwise, qApp will show the error and won't start the program.
00423                                 if(ok != NULL) *ok = FALSE;
00424                                 }
00425                         else
00426                                 if(ok != NULL) *ok = TRUE;
00427                         return maximum[name].value<Type>();
00428                         }
00429 
00436                 template <class Type> Type getPropertyMinimum(const QString name, bool *ok = NULL) const
00437                         {
00438                         if(not checkExists(name,"QVPropertyContainer::getPropertyMinimum()"))
00439                                 if(ok != NULL) *ok = FALSE;
00440                         else if(not maximum.contains(name) and not minimum.contains(name))
00441                                 {
00442                                 QString str = QString("QVPropertyContainer::getPropertyMinimum():")
00443                                                           + QString(" property ") + name
00444                                                           + QString(" has no minimum value in ")
00445                                                           + QString("holder ") + getName() + QString(".");
00446                                 setLastError(str);
00447                                 if(qvApp->isRunning()) {
00448                                         std::cerr << qPrintable("Warning: " + str + "\n");
00449                                 } // Otherwise, qApp will show the error and won't start the program.
00450                                 if(ok != NULL) *ok = FALSE;
00451                                 }
00452                         else
00453                                 if(ok != NULL) *ok = TRUE;
00454                         return minimum[name].value<Type>();
00455                         }
00456 
00464                 QString getPropertyInfo(const QString name, bool *ok = NULL) const;
00465 
00471                 QString getLastError() const;
00472 
00480                 const QString infoInputProperties() const;
00481 
00500                 bool linkProperty(QString sourcePropertyName, QVPropertyContainer *destinyContainer, QString destinyPropertyName, LinkType linkType);
00501 
00513                 void linkProperty(QVPropertyContainer *container, LinkType linkType = SynchronousLink);
00514 
00529                 bool unlinkProperty(QString origName, QVPropertyContainer *destCont, QString destName);
00530 
00536                 void unlink();
00537 
00542                 static bool areSynchronized(const QList<QVPropertyContainer *> containers);
00543 
00549                 QVPropertyContainerInformer *getInformer() { return &informer; }
00550 
00551 /*              /// @brief Gets the global QVPropertyContainerInformer.
00557                 static QVPropertyContainerInformer *getGlobalInformer() { return &globalInformer; } */
00558 
00559         protected:
00567                 void readInputProperties();
00568 
00579                 void writeOutputProperties();
00580 
00620                 template <class Type> bool parseArgument(const QString parameter, const QString value);
00621 
00630                 void setLastError(QString str) const;
00631 
00636                 uint inputPropertyWorker(QString prop) const
00637                         {
00638                         if (inputLinks.contains(prop))  return inputLinks[prop]->qvp_orig->getId();
00639                         else                            return 0;
00640                         }
00641 
00642         private:
00643                 QString name;
00644                 uint ident;
00645                 mutable QString errorString;
00646                 QMap<QString, QVariant> variants,safelyCopiedVariants;
00647                 QMap<QString, QVariant> minimum, maximum;
00648                 QMap<QString, QString> _info;
00649                 QMap<QString, PropertyFlags> io_flags;
00650                 QMap<QString, PropertyLinkFlags> link_flags;
00651                 QList<QString> insertion_order;
00652                 QVPropertyContainerInformer informer;
00653 
00654                 static uint maxIdent;
00655                 static uint getNewIdent() { return ++maxIdent; }
00656 //              static QVPropertyContainerInformer globalInformer;
00657 
00658                 QReadWriteLock RWLock;
00659                 class QVPropertyContainerLink {
00660                   public:
00661                         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_dest(_qvp_dest), prop_dest(_prop_dest), qvp_dest_name(_qvp_dest->getName()), link_type(_link_type), markedForDeletion(FALSE) {
00662                                 // SyncSemaphoreIn value must initially be 1:
00663                                 SyncSemaphoreIn.release();
00664                                 // SyncSemaphoreOut is simply initialized with 0 (default value).
00665                         };
00666                         QVPropertyContainer *qvp_orig;
00667                         QString prop_orig, qvp_orig_name;
00668                         QVPropertyContainer *qvp_dest;
00669                         QString prop_dest, qvp_dest_name;
00670                         LinkType link_type;
00671                         QSemaphore SyncSemaphoreIn,SyncSemaphoreOut;
00672                         bool markedForDeletion;
00673                 };
00674                 QMap<QString, QVPropertyContainerLink* > inputLinks;
00675                 QMap<QString, QList<QVPropertyContainerLink*> > outputLinks;
00676 
00677                 const QMap<QString, QVPropertyContainerLink* > getInputLinks() const { return inputLinks; }
00678                 void addInputLink(QString prop_dest, QVPropertyContainerLink *link);
00679                 void toDeleteLink(QVPropertyContainerLink* link);
00680 
00681                 template <class Type> bool setPropertyRange(const QString name, const Type & minimum, const Type & maximum)
00682                         {
00683                         if(not checkExists(name,"QVPropertyContainer::setPropertyRange()"))
00684                                 return FALSE;
00685                         if(minimum <= getPropertyValue<Type>(name) and
00686                                 maximum >= getPropertyValue<Type>(name))
00687                                 {
00688                                         this->minimum[name] = QVariant::fromValue(minimum);
00689                                         this->maximum[name] = QVariant::fromValue(maximum);
00690                                         return TRUE;
00691                                 } else {
00692                                         QString str =  "QVPropertyContainer::setPropertyRange(): property " +
00693                                                                    name + " in holder " + getName() + " has value " +
00694                                                                    QString("%1").arg(getPropertyValue<Type>(name)) +
00695                                                                    ", which is not valid for the range [" +
00696                                                                    QString("%1,%2").arg(minimum).arg(maximum) + "]." ;
00697                                         setLastError(str);
00698                                         if(qvApp->isRunning()) {
00699                                                 std::cerr << qPrintable("Warning: " + str + "\n");
00700                                         } // Otherwise, qApp will show the error and won't start the program.
00701                                         return FALSE;
00702                                 }
00703                         }
00704 
00705                 template <class Type> bool setPropertyFromArguments(QString propertyName)
00706                         {
00707                         QStringList arguments = qvApp->arguments();
00708 
00709                         QMutableStringListIterator iterator(arguments);
00710                         while (iterator.hasNext())
00711                                 {
00712                                 QString argument = iterator.next();
00713                                 // Only if the name of the property holder is the same as defined in
00714                                 // the argument, in the form --name:property=value, (with name:
00715                                 // optional) the value will be intended to be applied to the property:
00716                                 if (argument.contains(QRegExp("^--")))
00717                                         {
00718                                         QString propertyContainerName(argument), parameter(argument), value(argument);
00719                                         // Checks whether in the argument string there is a name for
00720                                         // restricting the application of the value to a variable of
00721                                         // a specific property holder.
00722                                         if (argument.contains(QRegExp("^--[^=]+:")))
00723                                                 {
00724                                                 propertyContainerName.remove(QRegExp("^--")).remove(QRegExp(":.*"));
00725                                                 if(propertyContainerName != getName())
00726                                                         continue;
00727                                                 parameter.remove(QRegExp("^--[^=]+:")).remove(QRegExp("=.*$"));
00728                                                 }
00729                                         else
00730                                                 {
00731                                                 parameter.remove(QRegExp("^--")).remove(QRegExp("=.*$"));
00732                                                 }
00733                                         if(parameter != propertyName)
00734                                                 continue;
00735                                         // If we get here, then we must assign the value to the property:
00736                                         value.remove(QRegExp("^--[^=]*="));
00737                                         if(parseArgument<Type>(parameter,value))
00738                                                 {
00739                                                 if(not isInput(propertyName))
00740                                                         {
00741                                                         QString str = QString("QVPropertyContainer::setPropertyFromArguments():")
00742                                                                                 + QString(" property ") + propertyName
00743                                                                                 + QString(" in holder ") + getName()
00744                                                                                 + QString(" is not of Input type, and cannot be parsed.");
00745                                                         setLastError(str);
00746                                                         //qApp will show the error and won't start the program.
00747                                                         }
00748                                                 qvApp->setArgumentAsUsed(argument);
00749                                                 return TRUE;
00750                                                 }
00751                                         }
00752                                 }
00753                                 // If we get here, then we did not parse any value for the property:
00754                                 return FALSE;
00755                         }
00756 
00757                 bool correctRange(const QString name, const double & value) const;
00758                 // Any type inference ambiguity resolved with this:
00759                 // 1. For int values:
00760                 bool correctRange(const char *name, const int & value) const;
00761                 bool correctRange(QString name, const int & value) const;
00762 
00763                 // 2. Rest of types have no range, so we always return true:
00764                 template <typename T> bool correctRange(const QString parameter, const T & value) {
00765                         Q_UNUSED(parameter);
00766                         Q_UNUSED(value);
00767                         return TRUE;
00768                 }
00769 
00770                 bool checkExists(const QString name, const QString methodname) const;
00771                 bool checkIsNewProperty(const QString name, const QString methodname) const;
00772         };
00773 
00774 template <class Type> bool QVPropertyContainer::parseArgument(const QString parameter, const QString value)
00775         {
00776                 errorString = "QVPropertyContainer::parseArgument(): holder " + getName() +
00777                                                 ": parameter " + parameter +
00778                                                 " has an unknown type to command line parser " +
00779                                                 QString("(trying to parse value %1)").arg(value) ;
00780                 return FALSE;
00781         }
00782 
00783 template <> bool QVPropertyContainer::parseArgument<bool>(const QString parameter, const QString value);
00784 
00785 template <> bool QVPropertyContainer::parseArgument<int>(const QString parameter, const QString value);
00786 
00787 template <> bool QVPropertyContainer::parseArgument<double>(const QString parameter, const QString value);
00788 
00789 template <> bool QVPropertyContainer::parseArgument<QString>(const QString parameter, const QString value);
00790 
00791 Q_DECLARE_OPERATORS_FOR_FLAGS(QVPropertyContainer::PropertyFlags)
00792 Q_DECLARE_OPERATORS_FOR_FLAGS(QVPropertyContainer::PropertyLinkFlags)
00793 
00794 #endif