diff --git a/src/App/ExtensionContainer.cpp b/src/App/ExtensionContainer.cpp index b29644da01..528cd5ee4a 100644 --- a/src/App/ExtensionContainer.cpp +++ b/src/App/ExtensionContainer.cpp @@ -32,7 +32,7 @@ #include "DocumentObject.h" #include "Base/Exception.h" #include - + using namespace App; TYPESYSTEM_SOURCE(App::ExtensionContainer, App::PropertyContainer) @@ -44,7 +44,7 @@ ExtensionContainer::ExtensionContainer() { ExtensionContainer::~ExtensionContainer() { //we need to delete all dynamically added extensions - for(auto entry : _extensions) { + for(auto entry : _extensions) { if(entry.second->isPythonExtension()) delete entry.second; } @@ -54,17 +54,17 @@ void ExtensionContainer::registerExtension(Base::Type extension, Extension* ext) if(ext->getExtendedContainer() != this) throw Base::ValueError("ExtensionContainer::registerExtension: Extension has not this as base object"); - + //no duplicate extensions (including base classes) if(hasExtension(extension)) { - for(auto entry : _extensions) { + for(auto entry : _extensions) { if(entry.first == extension || entry.first.isDerivedFrom(extension)) { _extensions.erase(entry.first); break; } } } - + _extensions[extension] = ext; } @@ -74,7 +74,7 @@ bool ExtensionContainer::hasExtension(Base::Type t, bool derived) const { bool found = _extensions.find(t) != _extensions.end(); if(!found && derived) { //and for types derived from it, as they can be cast to the extension - for(auto entry : _extensions) { + for(auto entry : _extensions) { if(entry.first.isDerivedFrom(t)) return true; } @@ -86,7 +86,7 @@ bool ExtensionContainer::hasExtension(Base::Type t, bool derived) const { bool ExtensionContainer::hasExtension(const std::string& name) const { //and for types derived from it, as they can be cast to the extension - for(auto entry : _extensions) { + for(auto entry : _extensions) { if(entry.second->name() == name) return true; } @@ -95,7 +95,7 @@ bool ExtensionContainer::hasExtension(const std::string& name) const { Extension* ExtensionContainer::getExtension(Base::Type t, bool derived, bool no_except) const { - + auto result = _extensions.find(t); if((result == _extensions.end()) && derived) { //we need to check for derived types @@ -118,14 +118,14 @@ Extension* ExtensionContainer::getExtension(Base::Type t, bool derived, bool no_ } bool ExtensionContainer::hasExtensions() const { - + return !_extensions.empty(); } Extension* ExtensionContainer::getExtension(const std::string& name) const { //and for types derived from it, as they can be cast to the extension - for(auto entry : _extensions) { + for(auto entry : _extensions) { if(entry.second->name() == name) return entry.second; } @@ -136,7 +136,7 @@ std::vector< Extension* > ExtensionContainer::getExtensionsDerivedFrom(Base::Typ std::vector vec; //and for types derived from it, as they can be cast to the extension - for(auto entry : _extensions) { + for(auto entry : _extensions) { if(entry.first.isDerivedFrom(type)) vec.push_back(entry.second); } @@ -145,13 +145,13 @@ std::vector< Extension* > ExtensionContainer::getExtensionsDerivedFrom(Base::Typ void ExtensionContainer::getPropertyList(std::vector< Property* >& List) const { App::PropertyContainer::getPropertyList(List); - for(auto entry : _extensions) + for(auto entry : _extensions) entry.second->extensionGetPropertyList(List); } void ExtensionContainer::getPropertyMap(std::map< std::string, Property* >& Map) const { App::PropertyContainer::getPropertyMap(Map); - for(auto entry : _extensions) + for(auto entry : _extensions) entry.second->extensionGetPropertyMap(Map); } @@ -159,13 +159,13 @@ Property* ExtensionContainer::getPropertyByName(const char* name) const { auto prop = App::PropertyContainer::getPropertyByName(name); if(prop) return prop; - - for(auto entry : _extensions) { + + for(auto entry : _extensions) { auto prop = entry.second->extensionGetPropertyByName(name); if(prop) return prop; } - + return nullptr; } @@ -174,122 +174,122 @@ short int ExtensionContainer::getPropertyType(const Property* prop) const { short int res = App::PropertyContainer::getPropertyType(prop); if(res != 0) return res; - - for(auto entry : _extensions) { + + for(auto entry : _extensions) { res = entry.second->extensionGetPropertyType(prop); if(res != 0) - return res; + return res; } - + return 0; } short int ExtensionContainer::getPropertyType(const char* name) const { - + short int res = App::PropertyContainer::getPropertyType(name); if(res != 0) return res; - - for(auto entry : _extensions) { + + for(auto entry : _extensions) { res = entry.second->extensionGetPropertyType(name); if(res != 0) - return res; + return res; } - + return 0; } const char* ExtensionContainer::getPropertyName(const Property* prop) const { - + const char* res = App::PropertyContainer::getPropertyName(prop); if(res != 0) return res; - - for(auto entry : _extensions) { + + for(auto entry : _extensions) { res = entry.second->extensionGetPropertyName(prop); if(res != 0) - return res; + return res; } - + return 0; } const char* ExtensionContainer::getPropertyGroup(const Property* prop) const { - + const char* res = App::PropertyContainer::getPropertyGroup(prop); if(res != 0) return res; - - for(auto entry : _extensions) { + + for(auto entry : _extensions) { res = entry.second->extensionGetPropertyGroup(prop); if(res != 0) - return res; + return res; } - + return 0; } const char* ExtensionContainer::getPropertyGroup(const char* name) const { - + const char* res = App::PropertyContainer::getPropertyGroup(name); if(res != 0) return res; - - for(auto entry : _extensions) { + + for(auto entry : _extensions) { res = entry.second->extensionGetPropertyGroup(name); if(res != 0) - return res; + return res; } - + return 0; } const char* ExtensionContainer::getPropertyDocumentation(const Property* prop) const { - + const char* res = App::PropertyContainer::getPropertyDocumentation(prop); if(res != 0) return res; - - for(auto entry : _extensions) { + + for(auto entry : _extensions) { res = entry.second->extensionGetPropertyDocumentation(prop); if(res != 0) - return res; + return res; } - + return 0; } const char* ExtensionContainer::getPropertyDocumentation(const char* name) const { - + const char* res = App::PropertyContainer::getPropertyDocumentation(name); if(res != 0) return res; - - for(auto entry : _extensions) { + + for(auto entry : _extensions) { res = entry.second->extensionGetPropertyDocumentation(name); if(res != 0) - return res; + return res; } - + return 0; } void ExtensionContainer::onChanged(const Property* prop) { - - //inform all extensions about changed property. This includes all properties from the + + //inform all extensions about changed property. This includes all properties from the //extended object (this) as well as all extension properties for(auto entry : _extensions) entry.second->extensionOnChanged(prop); - + App::PropertyContainer::onChanged(prop); } void ExtensionContainer::Save(Base::Writer& writer) const { - //Note: save extensions must be called first to ensure that the extension element is always the - // very first inside the object element. This is needed since extension element works together with + //Note: save extensions must be called first to ensure that the extension element is always the + // very first inside the object element. This is needed since extension element works together with // an object attribute, and if another element would be read first the object attributes would be // cleared. saveExtensions(writer); @@ -297,28 +297,28 @@ void ExtensionContainer::Save(Base::Writer& writer) const { } void ExtensionContainer::Restore(Base::XMLReader& reader) { - - //restore dynamic extensions. - //Note 1: The extension element must be read first, before all other object elements. That is - // needed as the element works together with an object element attribute, which would be + + //restore dynamic extensions. + //Note 1: The extension element must be read first, before all other object elements. That is + // needed as the element works together with an object element attribute, which would be // cleared if another attribute is read first - //Note 2: This must happen before the py object of this container is used, as only in the + //Note 2: This must happen before the py object of this container is used, as only in the // pyobject constructor the extension methods are added to the container. restoreExtensions(reader); App::PropertyContainer::Restore(reader); } void ExtensionContainer::saveExtensions(Base::Writer& writer) const { - + //we don't save anything if there are no dynamic extensions if(!hasExtensions()) return; - + //save dynamic extensions writer.incInd(); // indentation for 'Extensions' writer.Stream() << writer.ind() << "" << std::endl; for(auto entry : _extensions) { - + auto ext = entry.second; writer.incInd(); // indentation for 'Extension name' writer.Stream() << writer.ind() << "" << std::endl; + writer.Stream() << writer.ind() << "" << std::endl; writer.decInd(); // indentation for 'Extension name' } writer.Stream() << writer.ind() << "" << std::endl; @@ -356,12 +356,12 @@ void ExtensionContainer::saveExtensions(Base::Writer& writer) const { void ExtensionContainer::restoreExtensions(Base::XMLReader& reader) { //Dynamic extensions are optional (also because they are introduced late into the document format) - //and hence it is possible that the element does not exist. As we cannot check for the existence of - //an element a object attribute is set if extensions are available. Here we check that + //and hence it is possible that the element does not exist. As we cannot check for the existence of + //an element a object attribute is set if extensions are available. Here we check that //attribute, and only if it exists the extensions element will be available. if(!reader.hasAttribute("Extensions")) return; - + reader.readElement("Extensions"); int Cnt = reader.getAttributeAsInteger("Count"); diff --git a/src/App/ExtensionContainer.h b/src/App/ExtensionContainer.h index 008a952076..cc7de2946e 100644 --- a/src/App/ExtensionContainer.h +++ b/src/App/ExtensionContainer.h @@ -33,54 +33,54 @@ #include namespace App { - + /** * @brief Container which can hold extensions - * - * In FreeCAD normally inheritance is a chain, it is not possible to use multiple inheritance. - * The reason for this is that all objects need to be exposed to python, and it is basically + * + * In FreeCAD normally inheritance is a chain, it is not possible to use multiple inheritance. + * The reason for this is that all objects need to be exposed to python, and it is basically * impossible to handle multiple inheritance in the C-API for python extensions. Also using multiple * parent classes in python is currently not possible with the default object approach. - * - * The concept of extensions allow to circumvent those problems. Extensions are FreeCAD objects - * which work like normal objects in the sense that they use properties and class methods to define - * their functionality. However, they are not exposed as individual usable entities but are used to - * extend other objects. A extended object gets all the properties and methods of the extension. - * Therefore it is like c++ multiple inheritance, which is indeed used to achieve this on c++ side, + * + * The concept of extensions allow to circumvent those problems. Extensions are FreeCAD objects + * which work like normal objects in the sense that they use properties and class methods to define + * their functionality. However, they are not exposed as individual usable entities but are used to + * extend other objects. A extended object gets all the properties and methods of the extension. + * Therefore it is like c++ multiple inheritance, which is indeed used to achieve this on c++ side, * but provides a few important additional functionalities: * - Property persistence is handled, save and restore work out of the box * - The objects python API gets extended too with the extension python API * - Extensions can be added from c++ and python, even from both together * - * The interoperability with python is highly important, as in FreeCAD all functionality should be - * as easily accessible from python as from c++. To ensure this, and as already noted, extensions can + * The interoperability with python is highly important, as in FreeCAD all functionality should be + * as easily accessible from python as from c++. To ensure this, and as already noted, extensions can * be added to a object from python. However, this means that it is not clear from the c++ object type * if an extension was added or not. If added from c++ it becomes clear in the type due to the use of - * multiple inheritance. If added from python it is a runtime extension and not visible from type. - * Hence querying existing extensions of an object and accessing its methods works not by type - * casting but by the interface provided in ExtensionContainer. The default workflow is to query if - * an extension exists and then get the extension object. No matter if added from python or c++ this - * interface works always the same. + * multiple inheritance. If added from python it is a runtime extension and not visible from type. + * Hence querying existing extensions of an object and accessing its methods works not by type + * casting but by the interface provided in ExtensionContainer. The default workflow is to query if + * an extension exists and then get the extension object. No matter if added from python or c++ this + * interface works always the same. * @code * if (object->hasExtension(GroupExtension::getClassTypeId())) { * App::GroupExtension* group = object->getExtensionByType(); - * group->hasObject(...); + * group->hasObject(...); * } * @endcode - * - * To add a extension to an object, it must comply to a single restriction: it must be derived - * from ExtensionContainer. This is important to allow adding extensions from python and also to - * access the universal extension API. As DocumentObject itself derives from ExtensionContainer this - * should be the case automatically in most circumstances. - * - * Note that two small boilerplate changes are needed next to the multiple inheritance when adding + * + * To add a extension to an object, it must comply to a single restriction: it must be derived + * from ExtensionContainer. This is important to allow adding extensions from python and also to + * access the universal extension API. As DocumentObject itself derives from ExtensionContainer this + * should be the case automatically in most circumstances. + * + * Note that two small boilerplate changes are needed next to the multiple inheritance when adding * extensions from c++. - * 1. It must be ensured that the property and type registration is aware of the extensions by using + * 1. It must be ensured that the property and type registration is aware of the extensions by using * special macros. * 2. The extensions need to be initialised in the constructor - * + * * Here a working example: - * @code + * @code * class AppExport Part : public App::DocumentObject, public App::FirstExtension, public App::SecondExtension { * PROPERTY_HEADER_WITH_EXTENSIONS(App::Part); * }; @@ -90,10 +90,10 @@ namespace App { * SecondExtension::initExtension(this); * } * @endcode - * - * From python adding an extension is easier, it must be simply registered to a document object + * + * From python adding an extension is easier, it must be simply registered to a document object * at object initialisation like done with properties. Note that the special python extension objects - * need to be added, not the c++ objects. Normally the only difference in name is the additional + * need to be added, not the c++ objects. Normally the only difference in name is the additional * "Python" at the end of the extension name. * @code{.py} * class Test(): @@ -101,14 +101,14 @@ namespace App { * registerExtension("App::FirstExtensionPython", self) * registerExtension("App::SecondExtensionPython", self) * @endcode - * + * * Extensions can provide methods that should be overridden by the extended object for customisation * of the extension behaviour. In c++ this is as simple as overriding the provided virtual functions. - * In python a class method must be provided which has the same name as the method to override. This - * method must not necessarily be in the object that is extended, it must be in the object which is - * provided to the "registerExtension" call as second argument. This second argument is used as a - * proxy and enqueired if the method to override exists in this proxy before calling it. - * + * In python a class method must be provided which has the same name as the method to override. This + * method must not necessarily be in the object that is extended, it must be in the object which is + * provided to the "registerExtension" call as second argument. This second argument is used as a + * proxy and enqueired if the method to override exists in this proxy before calling it. + * * For information on howto create extension see the documentation of Extension */ class AppExport ExtensionContainer : public App::PropertyContainer @@ -117,7 +117,7 @@ class AppExport ExtensionContainer : public App::PropertyContainer TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: - + typedef std::map::iterator ExtensionIterator; ExtensionContainer(); @@ -127,31 +127,31 @@ public: bool hasExtension(Base::Type, bool derived=true) const; //returns first of type (or derived from if set to true) and throws otherwise bool hasExtension(const std::string& name) const; //this version does not check derived classes bool hasExtensions() const; - App::Extension* getExtension(Base::Type, bool derived = true, bool no_except=false) const; + App::Extension* getExtension(Base::Type, bool derived = true, bool no_except=false) const; App::Extension* getExtension(const std::string& name) const; //this version does not check derived classes - + //returns first of type (or derived from) and throws otherwise template ExtensionT* getExtensionByType(bool no_except=false, bool derived=true) const { return static_cast(getExtension(ExtensionT::getExtensionClassTypeId(),derived,no_except)); } - + //get all extensions which have the given base class std::vector getExtensionsDerivedFrom(Base::Type type) const; template std::vector getExtensionsDerivedFromType() const { std::vector typevec; - for(auto entry : _extensions) { + for(auto entry : _extensions) { if(entry.first.isDerivedFrom(ExtensionT::getExtensionClassTypeId())) typevec.push_back(static_cast(entry.second)); } return typevec; } - - ExtensionIterator extensionBegin() {return _extensions.begin();}; - ExtensionIterator extensionEnd() {return _extensions.end();}; - - + + ExtensionIterator extensionBegin() {return _extensions.begin();} + ExtensionIterator extensionEnd() {return _extensions.end();} + + /** @name Access properties */ //@{ /// find a property by its name @@ -176,17 +176,17 @@ public: /// get the Group of a named Property virtual const char* getPropertyDocumentation(const char *name) const override; //@} - + virtual void onChanged(const Property*) override; - + virtual void Save(Base::Writer& writer) const override; virtual void Restore(Base::XMLReader& reader) override; - + //those methods save/restore the dynamic extensions without handling properties, which is something //done by the default Save/Restore methods. void saveExtensions(Base::Writer& writer) const; void restoreExtensions(Base::XMLReader& reader); - + private: //stored extensions std::map _extensions;