1
/***************************************************************************
2
* Copyright (c) 2016 Stefan Tröger <stefantroeger@gmx.net> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
24
#include "PreCompiled.h"
26
#include <Base/Console.h>
27
#include <Base/Exception.h>
28
#include <Base/Reader.h>
29
#include <Base/Writer.h>
32
#include "ExtensionContainer.h"
37
TYPESYSTEM_SOURCE(App::ExtensionContainer, App::PropertyContainer)
39
ExtensionContainer::ExtensionContainer() = default;
41
ExtensionContainer::~ExtensionContainer() {
43
//we need to delete all dynamically added extensions
44
for(const auto& entry : _extensions) {
45
if(entry.second->isPythonExtension())
50
void ExtensionContainer::registerExtension(Base::Type extension, Extension* ext) {
51
if(ext->getExtendedContainer() != this)
52
throw Base::ValueError("ExtensionContainer::registerExtension: Extension has not this as base object");
54
//no duplicate extensions (including base classes)
55
if(hasExtension(extension)) {
56
for(const auto& entry : _extensions) {
57
if(entry.first == extension || entry.first.isDerivedFrom(extension)) {
58
_extensions.erase(entry.first);
64
_extensions[extension] = ext;
67
bool ExtensionContainer::hasExtension(Base::Type t, bool derived) const {
69
//check for the exact type
70
bool found = _extensions.find(t) != _extensions.end();
71
if(!found && derived) {
72
//and for types derived from it, as they can be cast to the extension
73
for(const auto& entry : _extensions) {
74
if(entry.first.isDerivedFrom(t))
82
bool ExtensionContainer::hasExtension(const std::string& name) const {
84
//and for types derived from it, as they can be cast to the extension
85
for(const auto& entry : _extensions) {
86
if(entry.second->name() == name)
93
Extension* ExtensionContainer::getExtension(Base::Type t, bool derived, bool no_except) const {
95
auto result = _extensions.find(t);
96
if((result == _extensions.end()) && derived) {
97
//we need to check for derived types
98
for(const auto& entry : _extensions) {
99
if(entry.first.isDerivedFrom(t))
104
//if we arrive here we don't have anything matching
105
throw Base::TypeError("ExtensionContainer::getExtension: No extension of given type available");
107
else if (result != _extensions.end()) {
108
return result->second;
113
//if we arrive here we don't have anything matching
114
throw Base::TypeError("ExtensionContainer::getExtension: No extension of given type available");
118
bool ExtensionContainer::hasExtensions() const {
120
return !_extensions.empty();
123
Extension* ExtensionContainer::getExtension(const std::string& name) const {
125
//and for types derived from it, as they can be cast to the extension
126
for(const auto& entry : _extensions) {
127
if(entry.second->name() == name)
133
std::vector< Extension* > ExtensionContainer::getExtensionsDerivedFrom(Base::Type type) const {
135
std::vector<Extension*> vec;
136
//and for types derived from it, as they can be cast to the extension
137
for(const auto& entry : _extensions) {
138
if(entry.first.isDerivedFrom(type))
139
vec.push_back(entry.second);
144
void ExtensionContainer::getPropertyList(std::vector< Property* >& List) const {
145
App::PropertyContainer::getPropertyList(List);
146
for(const auto& entry : _extensions)
147
entry.second->extensionGetPropertyList(List);
150
void ExtensionContainer::getPropertyMap(std::map< std::string, Property* >& Map) const {
151
App::PropertyContainer::getPropertyMap(Map);
152
for(const auto& entry : _extensions)
153
entry.second->extensionGetPropertyMap(Map);
156
Property* ExtensionContainer::getPropertyByName(const char* name) const {
157
auto prop = App::PropertyContainer::getPropertyByName(name);
161
for(const auto& entry : _extensions) {
162
auto prop = entry.second->extensionGetPropertyByName(name);
171
short int ExtensionContainer::getPropertyType(const Property* prop) const {
172
short int res = App::PropertyContainer::getPropertyType(prop);
176
for(const auto& entry : _extensions) {
177
res = entry.second->extensionGetPropertyType(prop);
185
short int ExtensionContainer::getPropertyType(const char* name) const {
187
short int res = App::PropertyContainer::getPropertyType(name);
191
for(const auto& entry : _extensions) {
192
res = entry.second->extensionGetPropertyType(name);
201
const char* ExtensionContainer::getPropertyName(const Property* prop) const {
203
const char* res = App::PropertyContainer::getPropertyName(prop);
207
for (const auto& entry : _extensions) {
208
res = entry.second->extensionGetPropertyName(prop);
216
const char* ExtensionContainer::getPropertyGroup(const Property* prop) const {
218
const char* res = App::PropertyContainer::getPropertyGroup(prop);
222
for (const auto& entry : _extensions) {
223
res = entry.second->extensionGetPropertyGroup(prop);
231
const char* ExtensionContainer::getPropertyGroup(const char* name) const {
233
const char* res = App::PropertyContainer::getPropertyGroup(name);
237
for (const auto& entry : _extensions) {
238
res = entry.second->extensionGetPropertyGroup(name);
247
const char* ExtensionContainer::getPropertyDocumentation(const Property* prop) const {
249
const char* res = App::PropertyContainer::getPropertyDocumentation(prop);
253
for (const auto& entry : _extensions) {
254
res = entry.second->extensionGetPropertyDocumentation(prop);
262
const char* ExtensionContainer::getPropertyDocumentation(const char* name) const {
264
const char* res = App::PropertyContainer::getPropertyDocumentation(name);
268
for(const auto& entry : _extensions) {
269
res = entry.second->extensionGetPropertyDocumentation(name);
277
void ExtensionContainer::onChanged(const Property* prop) {
279
//inform all extensions about changed property. This includes all properties from the
280
//extended object (this) as well as all extension properties
281
for(const auto& entry : _extensions)
282
entry.second->extensionOnChanged(prop);
284
App::PropertyContainer::onChanged(prop);
287
void ExtensionContainer::Save(Base::Writer& writer) const {
289
//Note: save extensions must be called first to ensure that the extension element is always the
290
// very first inside the object element. This is needed since extension element works together with
291
// an object attribute, and if another element would be read first the object attributes would be
293
saveExtensions(writer);
294
App::PropertyContainer::Save(writer);
297
void ExtensionContainer::Restore(Base::XMLReader& reader) {
299
//restore dynamic extensions.
300
//Note 1: The extension element must be read first, before all other object elements. That is
301
// needed as the element works together with an object element attribute, which would be
302
// cleared if another attribute is read first
303
//Note 2: This must happen before the py object of this container is used, as only in the
304
// pyobject constructor the extension methods are added to the container.
305
restoreExtensions(reader);
306
App::PropertyContainer::Restore(reader);
309
void ExtensionContainer::saveExtensions(Base::Writer& writer) const {
311
//we don't save anything if there are no dynamic extensions
315
//save dynamic extensions
316
writer.incInd(); // indentation for 'Extensions'
317
writer.Stream() << writer.ind() << "<Extensions Count=\"" << _extensions.size() << "\">" << std::endl;
318
for(const auto& entry : _extensions) {
320
auto ext = entry.second;
321
writer.incInd(); // indentation for 'Extension name'
322
writer.Stream() << writer.ind() << "<Extension"
323
<< " type=\"" << ext->getExtensionTypeId().getName() <<"\""
324
<< " name=\"" << ext->name() << "\">" << std::endl;
325
writer.incInd(); // indentation for the actual Extension
327
// We must make sure to handle all exceptions accordingly so that
328
// the project file doesn't get invalidated. In the error case this
329
// means to proceed instead of aborting the write operation.
330
ext->extensionSave(writer);
332
catch (const Base::Exception &e) {
333
Base::Console().Error("%s\n", e.what());
335
catch (const std::exception &e) {
336
Base::Console().Error("%s\n", e.what());
338
catch (const char* e) {
339
Base::Console().Error("%s\n", e);
343
Base::Console().Error("ExtensionContainer::Save: Unknown C++ exception thrown. Try to continue...\n");
346
writer.decInd(); // indentation for the actual extension
347
writer.Stream() << writer.ind() << "</Extension>" << std::endl;
348
writer.decInd(); // indentation for 'Extension name'
350
writer.Stream() << writer.ind() << "</Extensions>" << std::endl;
354
void ExtensionContainer::restoreExtensions(Base::XMLReader& reader) {
356
//Dynamic extensions are optional (also because they are introduced late into the document format)
357
//and hence it is possible that the element does not exist. As we cannot check for the existence of
358
//an element a object attribute is set if extensions are available. Here we check that
359
//attribute, and only if it exists the extensions element will be available.
360
if(!reader.hasAttribute("Extensions"))
363
reader.readElement("Extensions");
364
int Cnt = reader.getAttributeAsInteger("Count");
366
for (int i=0 ;i<Cnt ;i++) {
367
reader.readElement("Extension");
368
const char* Type = reader.getAttribute("type");
369
const char* Name = reader.getAttribute("name");
371
App::Extension* ext = getExtension(Name);
373
//get the extension type asked for
374
Base::Type extension = Base::Type::fromName(Type);
375
if (extension.isBad() || !extension.isDerivedFrom(App::Extension::getExtensionClassTypeId())) {
376
std::stringstream str;
377
str << "No extension found of type '" << Type << "'" << std::ends;
378
throw Base::TypeError(str.str());
381
//register the extension
382
ext = static_cast<App::Extension*>(extension.createInstance());
383
//check if this really is a python extension!
384
if (!ext->isPythonExtension()) {
386
std::stringstream str;
387
str << "Extension is not a python addable version: '" << Type << "'" << std::ends;
388
throw Base::TypeError(str.str());
391
ext->initExtension(this);
393
if (ext && strcmp(ext->getExtensionTypeId().getName(), Type) == 0)
394
ext->extensionRestore(reader);
396
catch (const Base::XMLParseException&) {
399
catch (const Base::Exception &e) {
400
Base::Console().Error("%s\n", e.what());
402
catch (const std::exception &e) {
403
Base::Console().Error("%s\n", e.what());
405
catch (const char* e) {
406
Base::Console().Error("%s\n", e);
410
Base::Console().Error("ExtensionContainer::Restore: Unknown C++ exception thrown\n");
414
reader.readEndElement("Extension");
416
reader.readEndElement("Extensions");
419
void ExtensionContainer::handleChangedPropertyName(Base::XMLReader &reader, const char * TypeName, const char *PropName)
421
//inform all extensions about changed property name. This includes all properties from the
422
//extended object (this) as well as all extension properties
423
for(const auto& entry : _extensions) {
424
bool handled = entry.second->extensionHandleChangedPropertyName(reader, TypeName, PropName);
427
return; // one property change needs only be handled once
430
PropertyContainer::handleChangedPropertyName(reader, TypeName, PropName);
433
void ExtensionContainer::handleChangedPropertyType(Base::XMLReader &reader, const char * TypeName, Property * prop)
435
//inform all extensions about changed property type. This includes all properties from the
436
//extended object (this) as well as all extension properties
437
for(const auto& entry : _extensions) {
438
bool handled = entry.second->extensionHandleChangedPropertyType(reader, TypeName, prop);
441
return; // one property change needs only be handled once
444
PropertyContainer::handleChangedPropertyType(reader, TypeName, prop);