FreeCAD

Форк
0
/
ExtensionContainer.cpp 
445 строк · 15.2 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2016 Stefan Tröger <stefantroeger@gmx.net>              *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
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.      *
10
 *                                                                         *
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.                  *
15
 *                                                                         *
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                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23

24
#include "PreCompiled.h"
25

26
#include <Base/Console.h>
27
#include <Base/Exception.h>
28
#include <Base/Reader.h>
29
#include <Base/Writer.h>
30

31
#include "Extension.h"
32
#include "ExtensionContainer.h"
33

34

35
using namespace App;
36

37
TYPESYSTEM_SOURCE(App::ExtensionContainer, App::PropertyContainer)
38

39
ExtensionContainer::ExtensionContainer() = default;
40

41
ExtensionContainer::~ExtensionContainer() {
42

43
    //we need to delete all dynamically added extensions
44
    for(const auto& entry : _extensions) {
45
        if(entry.second->isPythonExtension())
46
            delete entry.second;
47
    }
48
}
49

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");
53

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);
59
                break;
60
            }
61
        }
62
    }
63

64
    _extensions[extension] = ext;
65
}
66

67
bool ExtensionContainer::hasExtension(Base::Type t, bool derived) const {
68

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))
75
                return true;
76
        }
77
        return false;
78
    }
79
    return found;
80
}
81

82
bool ExtensionContainer::hasExtension(const std::string& name) const {
83

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)
87
            return true;
88
    }
89
    return false;
90
}
91

92

93
Extension* ExtensionContainer::getExtension(Base::Type t, bool derived, bool no_except) const {
94

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))
100
                return entry.second;
101
        }
102
        if(no_except)
103
            return nullptr;
104
        //if we arrive here we don't have anything matching
105
        throw Base::TypeError("ExtensionContainer::getExtension: No extension of given type available");
106
    }
107
    else if (result != _extensions.end()) {
108
        return result->second;
109
    }
110
    else {
111
        if(no_except)
112
            return nullptr;
113
        //if we arrive here we don't have anything matching
114
        throw Base::TypeError("ExtensionContainer::getExtension: No extension of given type available");
115
    }
116
}
117

118
bool ExtensionContainer::hasExtensions() const {
119

120
    return !_extensions.empty();
121
}
122

123
Extension* ExtensionContainer::getExtension(const std::string& name) const {
124

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)
128
            return entry.second;
129
    }
130
    return nullptr;
131
}
132

133
std::vector< Extension* > ExtensionContainer::getExtensionsDerivedFrom(Base::Type type) const {
134

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);
140
    }
141
    return vec;
142
}
143

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);
148
}
149

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);
154
}
155

156
Property* ExtensionContainer::getPropertyByName(const char* name) const {
157
    auto prop = App::PropertyContainer::getPropertyByName(name);
158
    if(prop)
159
        return prop;
160

161
    for(const auto& entry : _extensions) {
162
        auto prop = entry.second->extensionGetPropertyByName(name);
163
        if(prop)
164
            return prop;
165
    }
166

167
    return nullptr;
168
}
169

170

171
short int ExtensionContainer::getPropertyType(const Property* prop) const {
172
    short int res = App::PropertyContainer::getPropertyType(prop);
173
    if(res != 0)
174
        return res;
175

176
    for(const auto& entry : _extensions) {
177
        res = entry.second->extensionGetPropertyType(prop);
178
        if(res != 0)
179
            return res;
180
    }
181

182
    return 0;
183
}
184

185
short int ExtensionContainer::getPropertyType(const char* name) const {
186

187
    short int res = App::PropertyContainer::getPropertyType(name);
188
    if(res != 0)
189
        return res;
190

191
    for(const auto& entry : _extensions) {
192
        res = entry.second->extensionGetPropertyType(name);
193
        if(res != 0)
194
            return res;
195
    }
196

197
    return 0;
198
}
199

200

201
const char* ExtensionContainer::getPropertyName(const Property* prop) const {
202

203
    const char* res = App::PropertyContainer::getPropertyName(prop);
204
    if (res)
205
        return res;
206

207
    for (const auto& entry : _extensions) {
208
        res = entry.second->extensionGetPropertyName(prop);
209
        if (res)
210
            return res;
211
    }
212

213
    return nullptr;
214
}
215

216
const char* ExtensionContainer::getPropertyGroup(const Property* prop) const {
217

218
    const char* res = App::PropertyContainer::getPropertyGroup(prop);
219
    if (res)
220
        return res;
221

222
    for (const auto& entry : _extensions) {
223
        res = entry.second->extensionGetPropertyGroup(prop);
224
        if (res)
225
            return res;
226
    }
227

228
    return nullptr;
229
}
230

231
const char* ExtensionContainer::getPropertyGroup(const char* name) const {
232

233
    const char* res = App::PropertyContainer::getPropertyGroup(name);
234
    if (res)
235
        return res;
236

237
    for (const auto& entry : _extensions) {
238
        res = entry.second->extensionGetPropertyGroup(name);
239
        if (res)
240
            return res;
241
    }
242

243
    return nullptr;
244
}
245

246

247
const char* ExtensionContainer::getPropertyDocumentation(const Property* prop) const {
248

249
    const char* res = App::PropertyContainer::getPropertyDocumentation(prop);
250
    if (res)
251
        return res;
252

253
    for (const auto& entry : _extensions) {
254
        res = entry.second->extensionGetPropertyDocumentation(prop);
255
        if (res)
256
            return res;
257
    }
258

259
    return nullptr;
260
}
261

262
const char* ExtensionContainer::getPropertyDocumentation(const char* name) const {
263

264
    const char* res = App::PropertyContainer::getPropertyDocumentation(name);
265
    if (res)
266
        return res;
267

268
    for(const auto& entry : _extensions) {
269
        res = entry.second->extensionGetPropertyDocumentation(name);
270
        if (res)
271
            return res;
272
    }
273

274
    return nullptr;
275
}
276

277
void ExtensionContainer::onChanged(const Property* prop) {
278

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);
283

284
    App::PropertyContainer::onChanged(prop);
285
}
286

287
void ExtensionContainer::Save(Base::Writer& writer) const {
288

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
292
    //      cleared.
293
    saveExtensions(writer);
294
    App::PropertyContainer::Save(writer);
295
}
296

297
void ExtensionContainer::Restore(Base::XMLReader& reader) {
298

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);
307
}
308

309
void ExtensionContainer::saveExtensions(Base::Writer& writer) const {
310

311
    //we don't save anything if there are no dynamic extensions
312
    if(!hasExtensions())
313
        return;
314

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) {
319

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
326
        try {
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);
331
        }
332
        catch (const Base::Exception &e) {
333
            Base::Console().Error("%s\n", e.what());
334
        }
335
        catch (const std::exception &e) {
336
            Base::Console().Error("%s\n", e.what());
337
        }
338
        catch (const char* e) {
339
            Base::Console().Error("%s\n", e);
340
        }
341
#ifndef FC_DEBUG
342
        catch (...) {
343
            Base::Console().Error("ExtensionContainer::Save: Unknown C++ exception thrown. Try to continue...\n");
344
        }
345
#endif
346
        writer.decInd(); // indentation for the actual extension
347
        writer.Stream() << writer.ind() << "</Extension>" << std::endl;
348
        writer.decInd(); // indentation for 'Extension name'
349
    }
350
    writer.Stream() << writer.ind() << "</Extensions>" << std::endl;
351
    writer.decInd();
352
}
353

354
void ExtensionContainer::restoreExtensions(Base::XMLReader& reader) {
355

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"))
361
        return;
362

363
    reader.readElement("Extensions");
364
    int Cnt = reader.getAttributeAsInteger("Count");
365

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");
370
        try {
371
            App::Extension* ext = getExtension(Name);
372
            if(!ext) {
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());
379
                }
380

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()) {
385
                    delete ext;
386
                    std::stringstream str;
387
                    str << "Extension is not a python addable version: '" << Type << "'" << std::ends;
388
                    throw Base::TypeError(str.str());
389
                }
390

391
                ext->initExtension(this);
392
            }
393
            if (ext && strcmp(ext->getExtensionTypeId().getName(), Type) == 0)
394
                ext->extensionRestore(reader);
395
        }
396
        catch (const Base::XMLParseException&) {
397
            throw; // re-throw
398
        }
399
        catch (const Base::Exception &e) {
400
            Base::Console().Error("%s\n", e.what());
401
        }
402
        catch (const std::exception &e) {
403
            Base::Console().Error("%s\n", e.what());
404
        }
405
        catch (const char* e) {
406
            Base::Console().Error("%s\n", e);
407
        }
408
#ifndef FC_DEBUG
409
        catch (...) {
410
            Base::Console().Error("ExtensionContainer::Restore: Unknown C++ exception thrown\n");
411
        }
412
#endif
413

414
        reader.readEndElement("Extension");
415
    }
416
    reader.readEndElement("Extensions");
417
}
418

419
void ExtensionContainer::handleChangedPropertyName(Base::XMLReader &reader, const char * TypeName, const char *PropName)
420
{
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);
425

426
        if(handled)
427
            return; // one property change needs only be handled once
428
    }
429

430
    PropertyContainer::handleChangedPropertyName(reader, TypeName, PropName);
431
}
432

433
void ExtensionContainer::handleChangedPropertyType(Base::XMLReader &reader, const char * TypeName, Property * prop)
434
{
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);
439

440
        if(handled)
441
            return; // one property change needs only be handled once
442
    }
443

444
    PropertyContainer::handleChangedPropertyType(reader, TypeName, prop);
445
}
446

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.