CAF: Support class keyword aliases and a scriptable feature

* Also store class keyword in fields so they can be assigned to the correct Python object
  even if the class itself is abstract.
This commit is contained in:
Gaute Lindkvist 2020-02-21 11:01:08 +01:00
parent 83a7ceb204
commit c63c7f2eac
12 changed files with 168 additions and 67 deletions

View File

@ -67,6 +67,33 @@ bool PdmFieldHandle::matchesKeyword(const QString& keyword) const
return matchesKeywordAlias(keyword);
}
//--------------------------------------------------------------------------------------------------
/// The class of the ownerObject() can be different to ownerClass().
/// This is because the ownerClass() may be a super-class to the instantiated owner object.
//--------------------------------------------------------------------------------------------------
caf::PdmObjectHandle* PdmFieldHandle::ownerObject()
{
return m_ownerObject;
}
//--------------------------------------------------------------------------------------------------
/// Get the class in the class hierarchy the field actually belongs to.
/// This can be different to ownerObject's class, which may be a sub-class.
//--------------------------------------------------------------------------------------------------
QString PdmFieldHandle::ownerClass() const
{
return m_ownerClass;
}
//--------------------------------------------------------------------------------------------------
/// Set the class in the class hierarchy the field actually belongs to.
/// This can be different to ownerObject's class, which may be a sub-class.
//--------------------------------------------------------------------------------------------------
void PdmFieldHandle::setOwnerClass(const QString& ownerClass)
{
m_ownerClass = ownerClass;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -24,12 +24,14 @@ public:
QString keyword() const { return m_keyword; }
bool matchesKeyword(const QString& keyword) const;
PdmObjectHandle* ownerObject() { return m_ownerObject; }
PdmObjectHandle* ownerObject();
QString ownerClass() const;
// Child objects
bool hasChildObjects();
virtual void childObjects(std::vector<PdmObjectHandle*>*) { }
virtual void removeChildObject(PdmObjectHandle*) { }
void setOwnerClass(const QString& ownerClass);
// Ptr referenced objects
bool hasPtrReferencedObjects();
@ -58,6 +60,7 @@ private:
friend class PdmObjectHandle; // Give access to m_ownerObject and set Keyword
void setKeyword(const QString& keyword);
PdmObjectHandle* m_ownerObject;
QString m_ownerClass;
QString m_keyword;

View File

@ -17,6 +17,22 @@ PdmObjectHandle::~PdmObjectHandle()
this->prepareForDelete();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString PdmObjectHandle::classKeywordStatic()
{
return classKeywordAliases().front();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<QString> PdmObjectHandle::classKeywordAliases()
{
return { QString("PdmObjectHandle") };
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -3,7 +3,7 @@
#include "cafAssert.h"
#include "cafPdmBase.h"
class QString;
#include <QString>
#include <set>
#include <vector>
@ -26,6 +26,9 @@ public:
PdmObjectHandle() { m_parentField = nullptr; }
virtual ~PdmObjectHandle();
static QString classKeywordStatic(); // For PdmXmlFieldCap to be able to handle fields of PdmObjectHandle directly
static std::vector<QString> classKeywordAliases();
/// The registered fields contained in this PdmObject.
void fields(std::vector<PdmFieldHandle*>& fields) const;
PdmFieldHandle* findField(const QString& keyword) const;
@ -112,8 +115,6 @@ private:
template < class T > friend class PdmField; // For backwards compatibility layer
template < class T > friend class PdmFieldXmlCap;
static const char* classKeywordStatic() { return "PdmObjectHandle";} // For PdmXmlFieldCap to be able to handle fields of PdmObjectHandle directly
// Support system for PdmPointer
friend class PdmPointerImpl;

View File

@ -79,7 +79,7 @@ void PdmDocument::readFile(QIODevice* xmlFile)
xmlStream.readNext();
if (xmlStream.isStartElement())
{
if (xmlStream.name() != classKeyword())
if (!matchesClassKeyword(xmlStream.name().toString()))
{
// Error: This is not a Ceetron Pdm based xml document
return;

View File

@ -2,6 +2,17 @@
using namespace caf;
CAF_PDM_ABSTRACT_SOURCE_INIT(PdmObject, "PdmObject");
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::PdmObject::PdmObject() : PdmObjectHandle(), PdmXmlObjectHandle(this, false), PdmUiObjectHandle(this, false)
{
CAF_PDM_InitObject("Base PDM Object", "", "", "The Abstract Base Class for the Project Data Model");
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -89,35 +100,10 @@ void PdmObject::childrenFromClassKeyword(
for (auto childObject : childObjects)
{
PdmObject* pdmObjectChild = dynamic_cast<PdmObject*>(childObject);
if (pdmObjectChild && pdmObjectChild->classKeyword() == classKeyword)
if (pdmObjectChild && pdmObjectChild->matchesClassKeyword(classKeyword))
{
children.push_back(pdmObjectChild);
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::PdmObject::PdmObject() : PdmObjectHandle(), PdmXmlObjectHandle(this, false), PdmUiObjectHandle(this, false)
, m_scriptable(false)
{
registerClassKeyword(classKeywordStatic());
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString caf::PdmObject::classKeywordStatic()
{
return classKeywordAliases().front();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<QString> caf::PdmObject::classKeywordAliases()
{
return { QString("PdmObject") };
}

View File

@ -77,9 +77,12 @@ class PdmObjectCapability;
#define CAF_PDM_SOURCE_INIT CAF_PDM_XML_SOURCE_INIT
#define CAF_PDM_ABSTRACT_SOURCE_INIT CAF_PDM_XML_ABSTRACT_SOURCE_INIT
#define CAF_PDM_SCRIPTABLE_SOURCE_INIT CAF_PDM_XML_SCRIPTABLE_SOURCE_INIT
/// InitObject sets up the user interface related information for the object
/// Placed in the constructor of your PdmObject
/// Note that classKeyword() is not virtual in the constructor of the PdmObject
/// This is expected and fine.
#define CAF_PDM_InitObject(uiName, iconResourceName, toolTip, whatsThis) \
{ \
@ -95,6 +98,8 @@ class PdmObjectCapability;
/// adds the field to the internal data structure in the PdmObject,
/// sets the default value for the field,
/// and sets up the static user interface related information for the field
/// Note that classKeyword() is not virtual in the constructor of the PdmObject
/// This is expected and fine.
#define CAF_PDM_InitField(field, keyword, default, uiName, iconResourceName, toolTip, whatsThis) \
{ \
@ -107,12 +112,15 @@ class PdmObjectCapability;
\
AddXmlCapabilityToField(field); \
AddUiCapabilityToField(field); \
RegisterClassWithField(classKeyword(), field); \
\
static caf::PdmUiItemInfo objDescr(uiName, QString(iconResourceName), toolTip, whatsThis, keyword); \
addFieldUi(field, keyword, default, &objDescr); \
}
/// InitFieldNoDefault does the same as InitField but omits the default value.
/// Note that classKeyword() is not virtual in the constructor of the PdmObject
/// This is expected and fine.
#define CAF_PDM_InitFieldNoDefault(field, keyword, uiName, iconResourceName, toolTip, whatsThis) \
{ \
@ -125,6 +133,7 @@ class PdmObjectCapability;
\
AddXmlCapabilityToField(field); \
AddUiCapabilityToField(field); \
RegisterClassWithField(classKeyword(), field); \
\
static caf::PdmUiItemInfo objDescr(uiName, QString(iconResourceName), toolTip, whatsThis, keyword); \
addFieldUiNoDefault(field, keyword, &objDescr); \
@ -141,12 +150,11 @@ namespace caf
class PdmObject : public PdmObjectHandle, public PdmXmlObjectHandle, public PdmUiObjectHandle
{
public:
CAF_PDM_HEADER_INIT;
PdmObject();
~PdmObject() override {}
static QString classKeywordStatic();
static std::vector<QString> classKeywordAliases();
/// Adds field to the internal data structure and sets the file keyword and Ui information
/// Consider this method private. Please use the CAF_PDM_InitField() macro instead
template< typename FieldDataType >
@ -186,6 +194,7 @@ public:
void childrenFromClassKeyword(
const QString& classKeyword,
std::vector<PdmObject*>& children) const;
};
} // End of namespace caf

View File

@ -134,6 +134,13 @@ void AddXmlCapabilityToField(FieldType* field)
}
template<typename FieldType>
void RegisterClassWithField(const QString& classKeyword, FieldType* field)
{
field->setOwnerClass(classKeyword);
}
} // End of namespace caf
#include "cafInternalPdmXmlFieldCapability.inl"

View File

@ -247,7 +247,7 @@ void caf::PdmFieldXmlCap< caf::PdmChildField<DataType*> >::readFieldData(QXmlStr
else
{
PdmXmlObjectHandle* xmlObject = xmlObj(obj);
if (!xmlObject || xmlObject->classKeyword() != className)
if (!xmlObject || !xmlObject->matchesClassKeyword(className))
{
CAF_ASSERT(false); // Inconsistency in the factory. It creates objects of wrong type from the ClassKeyword
@ -267,7 +267,7 @@ void caf::PdmFieldXmlCap< caf::PdmChildField<DataType*> >::readFieldData(QXmlStr
}
PdmXmlObjectHandle* xmlObject = xmlObj(obj);
if (!xmlObject || xmlObject->classKeyword() != className)
if (!xmlObject || !xmlObject->matchesClassKeyword(className))
{
// Error: Field contains different class type than on file
std::cout << "Line " << xmlStream.lineNumber() << ": Warning: Unknown object type with class name: " << className.toLatin1().data() << " found while reading the field : " << m_field->keyword().toLatin1().data() << std::endl;
@ -382,7 +382,7 @@ void caf::PdmFieldXmlCap< caf::PdmChildArrayField<DataType*> >::readFieldData(QX
}
PdmXmlObjectHandle* xmlObject = xmlObj(obj);
if (!xmlObject || xmlObject->classKeyword() != className)
if (!xmlObject || !xmlObject->matchesClassKeyword(className))
{
CAF_ASSERT(false); // There is an inconsistency in the factory. It creates objects of type not matching the ClassKeyword

View File

@ -67,22 +67,24 @@ public:
template< typename PdmObjectBaseDerivative >
bool registerCreator()
{
std::map<QString, PdmObjectCreatorBase*>::iterator entryIt;
std::vector<QString> classNameKeywords = PdmObjectBaseDerivative::classKeywordAliases();
QString classNameKeyword = PdmObjectBaseDerivative::classKeywordStatic();
entryIt = m_factoryMap.find(classNameKeyword);
if (entryIt == m_factoryMap.end())
for (QString classNameKeyword : classNameKeywords)
{
m_factoryMap[classNameKeyword] = new PdmObjectCreator<PdmObjectBaseDerivative>();
return true;
auto entryIt = m_factoryMap.find(classNameKeyword);
if (entryIt != m_factoryMap.end())
{
CAF_ASSERT(classNameKeyword != entryIt->first); // classNameKeyword has already been used
CAF_ASSERT(false); // To be sure ..
return false; // never hit;
}
}
else
auto object = new PdmObjectCreator<PdmObjectBaseDerivative>();
for (QString classNameKeyword : classNameKeywords)
{
CAF_ASSERT(classNameKeyword != entryIt->first); // classNameKeyword has already been used
CAF_ASSERT(false); // To be sure ..
return false; // never hit;
m_factoryMap[classNameKeyword] = object;
}
return true;
}
std::vector<QString> classKeywords() const;

View File

@ -35,6 +35,7 @@ public:
/// The classKeyword method is overridden in subclasses by the CAF_PDM_XML_HEADER_INIT macro
virtual QString classKeyword() const = 0;
virtual bool matchesClassKeyword(const QString& classKeyword) const = 0;
/// Convenience methods to serialize/de-serialize this particular object (with children)
void readObjectFromXmlString(const QString& xmlString, PdmObjectFactory* objectFactory);

View File

@ -27,35 +27,84 @@
// To be renamed CAF_PDM_XML_HEADER_INIT
#define CAF_PDM_XML_HEADER_INIT \
private: \
static bool classIsScriptable(); \
public: \
virtual QString classKeyword() const { return classKeywordStatic(); } \
static QString classKeywordStatic(); \
virtual QString classKeyword() const; \
static QString classKeywordStatic(); \
static std::vector<QString> classKeywordAliases(); \
virtual bool isScriptable() const; \
virtual bool matchesClassKeyword(const QString& keyword) const; \
\
static bool Error_You_forgot_to_add_the_macro_CAF_PDM_XML_HEADER_INIT_and_or_CAF_PDM_XML_SOURCE_INIT_to_your_cpp_file_for_this_class()
#define CAF_PDM_XML_NON_SCRIPTABLE_INIT(ClassName) \
bool ClassName::classIsScriptable() \
{ \
return false; \
} \
#define CAF_PDM_XML_SCRIPTABLE_INIT(ClassName) \
bool ClassName::classIsScriptable() \
{ \
return true; \
} \
#define CAF_PDM_XML_ABSTRACT_BASE_SOURCE_INIT(ClassName, keyword, ...) \
bool ClassName::Error_You_forgot_to_add_the_macro_CAF_PDM_XML_HEADER_INIT_and_or_CAF_PDM_XML_SOURCE_INIT_to_your_cpp_file_for_this_class() { return false;} \
\
QString ClassName::classKeyword() const \
{ \
return classKeywordStatic(); \
} \
QString ClassName::classKeywordStatic() \
{ \
return classKeywordAliases().front(); \
} \
std::vector<QString> ClassName::classKeywordAliases() \
{ \
CAF_PDM_VERIFY_XML_KEYWORD(keyword) \
return {keyword, ##__VA_ARGS__}; \
} \
bool ClassName::isScriptable() const \
{ \
return ClassName::classIsScriptable(); \
} \
bool ClassName::matchesClassKeyword(const QString& matchKeyword) const\
{ \
auto aliases = classKeywordAliases(); \
for (auto alias : aliases) \
{ \
if (alias == matchKeyword) return true; \
} \
return false; \
} \
/// CAF_PDM_XML_ABSTRACT_SOURCE_INIT associates the file keyword used for storage with the class
/// Place this in the cpp file, preferably above the constructor
#define CAF_PDM_XML_ABSTRACT_SOURCE_INIT(ClassName, keyword, ...) \
CAF_PDM_XML_ABSTRACT_BASE_SOURCE_INIT(ClassName, keyword, ##__VA_ARGS__) \
CAF_PDM_XML_NON_SCRIPTABLE_INIT(ClassName) \
/// CAF_PDM_XML_ABSTRACT_SOURCE_INIT associates the file keyword used for storage with the *scriptable* class
/// Place this in the cpp file, preferably above the constructor
#define CAF_PDM_XML_ABSTRACT_SCRIPTABLE_SOURCE_INIT(ClassName, keyword, ...) \
CAF_PDM_XML_ABSTRACT_BASE_SOURCE_INIT(ClassName, keyword, ##__VA_ARGS__) \
CAF_PDM_XML_SCRIPTABLE_INIT(ClassName) \
/// CAF_PDM_XML_SOURCE_INIT associates the file keyword used for storage with the class and
// initializes the factory
/// Place this in the cpp file, preferably above the constructor
#define CAF_PDM_XML_SOURCE_INIT(ClassName, keyword) \
bool ClassName::Error_You_forgot_to_add_the_macro_CAF_PDM_XML_HEADER_INIT_and_or_CAF_PDM_XML_SOURCE_INIT_to_your_cpp_file_for_this_class() { return false;} \
\
QString ClassName::classKeywordStatic() \
{ \
CAF_PDM_VERIFY_XML_KEYWORD(keyword) \
return keyword; \
} \
#define CAF_PDM_XML_SOURCE_INIT(ClassName, keyword, ...) \
CAF_PDM_XML_ABSTRACT_SOURCE_INIT(ClassName, keyword, ##__VA_ARGS__) \
static bool PDM_OBJECT_STRING_CONCATENATE(my##ClassName, __LINE__) = caf::PdmDefaultObjectFactory::instance()->registerCreator<ClassName>()
#define CAF_PDM_XML_ABSTRACT_SOURCE_INIT(ClassName, keyword) \
bool ClassName::Error_You_forgot_to_add_the_macro_CAF_PDM_XML_HEADER_INIT_and_or_CAF_PDM_XML_SOURCE_INIT_to_your_cpp_file_for_this_class() { return false;} \
\
QString ClassName::classKeywordStatic() \
{ \
CAF_PDM_VERIFY_XML_KEYWORD(keyword) \
return keyword; \
} \
/// CAF_PDM_XML_SCRIPTABLE_SOURCE_INIT associates the file keyword used for storage with the *scriptable* class and
// initializes the factory
/// Place this in the cpp file, preferably above the constructor
#define CAF_PDM_XML_SCRIPTABLE_SOURCE_INIT(ClassName, keyword, ...) \
CAF_PDM_XML_ABSTRACT_SCRIPTABLE_SOURCE_INIT(ClassName, keyword, ##__VA_ARGS__) \
static bool PDM_OBJECT_STRING_CONCATENATE(my##ClassName, __LINE__) = caf::PdmDefaultObjectFactory::instance()->registerCreator<ClassName>()
#define CAF_PDM_XML_InitField(field, keyword) \
{ \