FreeCAD

Форк
0
/
PropertyContainer.cpp 
670 строк · 21.1 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de>              *
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 "Property.h"
32
#include "PropertyContainer.h"
33

34

35
FC_LOG_LEVEL_INIT("App",true,true)
36

37
using namespace App;
38
using namespace Base;
39
using namespace std;
40

41
TYPESYSTEM_SOURCE(App::PropertyContainer,Base::Persistence)
42

43

44
//**************************************************************************
45
// Construction/Destruction
46

47
// Here's the implementation! Description should take place in the header file!
48
PropertyContainer::PropertyContainer()
49
{
50
    propertyData.parentPropertyData = nullptr;
51
}
52

53
PropertyContainer::~PropertyContainer() = default;
54

55
unsigned int PropertyContainer::getMemSize () const
56
{
57
    std::map<std::string,Property*> Map;
58
    getPropertyMap(Map);
59
    std::map<std::string,Property*>::const_iterator It;
60
    unsigned int size = 0;
61
    for (It = Map.begin(); It != Map.end();++It)
62
        size += It->second->getMemSize();
63
    return size;
64
}
65

66

67
App::Property* PropertyContainer::addDynamicProperty(
68
    const char* type, const char* name, const char* group, const char* doc,
69
    short attr, bool ro, bool hidden)
70
{
71
    return dynamicProps.addDynamicProperty(*this,type,name,group,doc,attr,ro,hidden);
72
}
73

74
Property *PropertyContainer::getPropertyByName(const char* name) const
75
{
76
    auto prop = dynamicProps.getDynamicPropertyByName(name);
77
    if (prop) {
78
        return prop;
79
    }
80
    return getPropertyData().getPropertyByName(this,name);
81
}
82

83
void PropertyContainer::getPropertyMap(std::map<std::string,Property*> &Map) const
84
{
85
    dynamicProps.getPropertyMap(Map);
86
    getPropertyData().getPropertyMap(this,Map);
87
}
88

89
void PropertyContainer::getPropertyList(std::vector<Property*> &List) const
90
{
91
    dynamicProps.getPropertyList(List);
92
    getPropertyData().getPropertyList(this,List);
93
}
94

95
void PropertyContainer::getPropertyNamedList(std::vector<std::pair<const char*, Property*> > &List) const
96
{
97
    dynamicProps.getPropertyNamedList(List);
98
    getPropertyData().getPropertyNamedList(this,List);
99
}
100

101
void PropertyContainer::setPropertyStatus(unsigned char bit,bool value)
102
{
103
    std::vector<Property*> List;
104
    getPropertyList(List);
105
    for(auto it : List)
106
        it->StatusBits.set(bit,value);
107
}
108

109
short PropertyContainer::getPropertyType(const Property* prop) const
110
{
111
    return prop?prop->getType():0;
112
}
113

114
short PropertyContainer::getPropertyType(const char *name) const
115
{
116
    return getPropertyType(getPropertyByName(name));
117
}
118

119
const char* PropertyContainer::getPropertyGroup(const Property* prop) const
120
{
121
    auto group = dynamicProps.getPropertyGroup(prop);
122
    if(group)
123
        return group;
124
    return getPropertyData().getGroup(this,prop);
125
}
126

127
const char* PropertyContainer::getPropertyGroup(const char *name) const
128
{
129
    auto group = dynamicProps.getPropertyGroup(name);
130
    if(group)
131
        return group;
132
    return getPropertyData().getGroup(this,name);
133
}
134

135
const char* PropertyContainer::getPropertyDocumentation(const Property* prop) const
136
{
137
    auto doc = dynamicProps.getPropertyDocumentation(prop);
138
    if(doc)
139
        return doc;
140
    return getPropertyData().getDocumentation(this,prop);
141
}
142

143
const char* PropertyContainer::getPropertyDocumentation(const char *name) const
144
{
145
    auto doc = dynamicProps.getPropertyDocumentation(name);
146
    if(doc)
147
        return doc;
148
    return getPropertyData().getDocumentation(this,name);
149
}
150

151
bool PropertyContainer::isReadOnly(const Property* prop) const
152
{
153
    return (getPropertyType(prop) & Prop_ReadOnly) == Prop_ReadOnly;
154
}
155

156
bool PropertyContainer::isReadOnly(const char *name) const
157
{
158
    return (getPropertyType(name) & Prop_ReadOnly) == Prop_ReadOnly;
159
}
160

161
bool PropertyContainer::isHidden(const Property* prop) const
162
{
163
    return (getPropertyType(prop) & Prop_Hidden) == Prop_Hidden;
164
}
165

166
bool PropertyContainer::isHidden(const char *name) const
167
{
168
    return (getPropertyType(name) & Prop_Hidden) == Prop_Hidden;
169
}
170

171
const char* PropertyContainer::getPropertyName(const Property* prop)const
172
{
173
    auto res = dynamicProps.getPropertyName(prop);
174
    if(!res)
175
        res = getPropertyData().getName(this,prop);
176
    return res;
177
}
178

179
const PropertyData * PropertyContainer::getPropertyDataPtr(){return &propertyData;}
180
const PropertyData & PropertyContainer::getPropertyData() const{return propertyData;}
181

182

183
/**
184
 * @brief PropertyContainer::handleChangedPropertyName is called during restore to possibly
185
 * fix reading of older versions of this property container. This method is typically called
186
 * if the property on file has changed its name in more recent versions.
187
 *
188
 * The default implementation does nothing.
189
 *
190
 * @param reader The XML stream to read from.
191
 * @param TypeName Name of property type on file.
192
 * @param PropName Name of property on file that does not exist in the container anymore.
193
 */
194

195
void PropertyContainer::handleChangedPropertyName(Base::XMLReader &reader, const char * TypeName, const char *PropName)
196
{
197
    (void)reader;
198
    (void)TypeName;
199
    (void)PropName;
200
}
201

202
/**
203
 * @brief PropertyContainer::handleChangedPropertyType is called during restore to possibly
204
 * fix reading of older versions of the property container. This method is typically called
205
 * if the property on file has changed its type in more recent versions.
206
 *
207
 * The default implementation does nothing.
208
 *
209
 * @param reader The XML stream to read from.
210
 * @param TypeName Name of property type on file.
211
 * @param prop Pointer to property to restore. Its type differs from TypeName.
212
 */
213

214
void PropertyContainer::handleChangedPropertyType(XMLReader &reader, const char *TypeName, Property *prop)
215
{
216
    (void)reader;
217
    (void)TypeName;
218
    (void)prop;
219
}
220

221
PropertyData PropertyContainer::propertyData;
222

223
void PropertyContainer::beforeSave() const
224
{
225
    std::map<std::string, Property*> Map;
226
    getPropertyMap(Map);
227
    for (auto& entry : Map) {
228
        auto prop = entry.second;
229
        if (!prop->testStatus(Property::PropDynamic)
230
            && (prop->testStatus(Property::Transient)
231
                || ((getPropertyType(prop) & Prop_Transient) != 0))) {
232
            // Nothing
233
        }
234
        else {
235
            prop->beforeSave();
236
        }
237
    }
238
}
239

240
void PropertyContainer::Save (Base::Writer &writer) const
241
{
242
    std::map<std::string,Property*> Map;
243
    getPropertyMap(Map);
244

245
    std::vector<Property*> transients;
246
    for(auto it=Map.begin();it!=Map.end();) {
247
        auto prop = it->second;
248
        if(prop->testStatus(Property::PropNoPersist)) {
249
            it = Map.erase(it);
250
            continue;
251
        }
252
        if(!prop->testStatus(Property::PropDynamic)
253
                && (prop->testStatus(Property::Transient) ||
254
                    getPropertyType(prop) & Prop_Transient))
255
        {
256
            transients.push_back(prop);
257
            it = Map.erase(it);
258
        } else {
259
            ++it;
260
        }
261
    }
262

263
    writer.incInd(); // indentation for 'Properties Count'
264
    writer.Stream() << writer.ind() << "<Properties Count=\"" << Map.size()
265
                    << "\" TransientCount=\"" << transients.size() << "\">" << endl;
266

267
    // First store transient properties to persist their status value. We use
268
    // a new element named "_Property" so that the save file can be opened by
269
    // older versions of FC.
270
    writer.incInd();
271
    for(auto prop : transients) {
272
        writer.Stream() << writer.ind() << "<_Property name=\"" << prop->getName()
273
            << "\" type=\"" << prop->getTypeId().getName()
274
            << "\" status=\"" << prop->getStatus() << "\"/>" << std::endl;
275
    }
276
    writer.decInd();
277

278
    // Now store normal properties
279
    for (const auto& it : Map)
280
    {
281
        writer.incInd(); // indentation for 'Property name'
282
        writer.Stream() << writer.ind() << "<Property name=\"" << it.first << "\" type=\""
283
                        << it.second->getTypeId().getName();
284

285
        dynamicProps.save(it.second,writer);
286

287
        auto status = it.second->getStatus();
288
        if(status)
289
            writer.Stream() << "\" status=\"" << status;
290
        writer.Stream() << "\">";
291

292
        if(it.second->testStatus(Property::Transient)
293
                || it.second->getType() & Prop_Transient)
294
        {
295
            writer.decInd();
296
            writer.Stream() << "</Property>" << std::endl;
297
            continue;
298
        }
299

300
        writer.Stream() << std::endl;
301

302
        writer.incInd(); // indentation for the actual property
303

304
        try {
305
            // We must make sure to handle all exceptions accordingly so that
306
            // the project file doesn't get invalidated. In the error case this
307
            // means to proceed instead of aborting the write operation.
308
            it.second->Save(writer);
309
        }
310
        catch (const Base::Exception &e) {
311
            Base::Console().Error("%s\n", e.what());
312
        }
313
        catch (const std::exception &e) {
314
            Base::Console().Error("%s\n", e.what());
315
        }
316
        catch (const char* e) {
317
            Base::Console().Error("%s\n", e);
318
        }
319
#ifndef FC_DEBUG
320
        catch (...) {
321
            Base::Console().Error("PropertyContainer::Save: Unknown C++ exception thrown. Try to continue...\n");
322
        }
323
#endif
324
        writer.decInd(); // indentation for the actual property
325
        writer.Stream() << writer.ind() << "</Property>" << endl;
326
        writer.decInd(); // indentation for 'Property name'
327
    }
328
    writer.Stream() << writer.ind() << "</Properties>" << endl;
329
    writer.decInd(); // indentation for 'Properties Count'
330
}
331

332
void PropertyContainer::Restore(Base::XMLReader &reader)
333
{
334
    reader.clearPartialRestoreProperty();
335
    reader.readElement("Properties");
336
    int Cnt = reader.getAttributeAsInteger("Count");
337

338
    int transientCount = 0;
339
    if(reader.hasAttribute("TransientCount"))
340
        transientCount = reader.getAttributeAsUnsigned("TransientCount");
341

342
    for (int i=0;i<transientCount; ++i) {
343
        reader.readElement("_Property");
344
        Property* prop = getPropertyByName(reader.getAttribute("name"));
345
        if(prop)
346
            FC_TRACE("restore transient '" << prop->getName() << "'");
347
        if(prop && reader.hasAttribute("status"))
348
            prop->setStatusValue(reader.getAttributeAsUnsigned("status"));
349
    }
350

351
    for (int i=0 ;i<Cnt ;i++) {
352
        reader.readElement("Property");
353
        std::string PropName = reader.getAttribute("name");
354
        std::string TypeName = reader.getAttribute("type");
355
        // NOTE: We must also check the type of the current property because a
356
        // subclass of PropertyContainer might change the type of a property but
357
        // not its name. In this case we would force to read-in a wrong property
358
        // type and the behaviour would be undefined.
359
        try {
360
            auto prop = getPropertyByName(PropName.c_str());
361
            if (!prop || prop->getContainer() != this) {
362
                prop = dynamicProps.restore(*this,PropName.c_str(),TypeName.c_str(),reader);
363
            }
364

365
            decltype(Property::StatusBits) status;
366
            if(reader.hasAttribute("status")) {
367
                status = decltype(status)(reader.getAttributeAsUnsigned("status"));
368
                if(prop)
369
                    prop->setStatusValue(status.to_ulong());
370
            }
371
            // name and type match
372
            if (prop && strcmp(prop->getTypeId().getName(), TypeName.c_str()) == 0) {
373
                if (!prop->testStatus(Property::Transient)
374
                        && !status.test(Property::Transient)
375
                        && !status.test(Property::PropTransient)
376
                        && !prop->testStatus(Property::PropTransient))
377
                {
378
                    FC_TRACE("restore property '" << prop->getName() << "'");
379
                    prop->Restore(reader);
380
                }else
381
                    FC_TRACE("skip transient '" << prop->getName() << "'");
382
            }
383
            // name matches but not the type
384
            else if (prop) {
385
                handleChangedPropertyType(reader, TypeName.c_str(), prop);
386
            }
387
            // name doesn't match, the sub-class then has to know
388
            // if the property has been renamed or removed
389
            else {
390
                handleChangedPropertyName(reader, TypeName.c_str(), PropName.c_str());
391
            }
392

393
            if (reader.testStatus(Base::XMLReader::ReaderStatus::PartialRestoreInProperty)) {
394
                Base::Console().Error("Property %s of type %s was subject to a partial restore.\n",PropName.c_str(),TypeName.c_str());
395
                reader.clearPartialRestoreProperty();
396
            }
397
        }
398
        catch (const Base::XMLParseException&) {
399
            throw; // re-throw
400
        }
401
        catch (const Base::RestoreError &) {
402
            reader.setPartialRestore(true);
403
            reader.clearPartialRestoreProperty();
404
            Base::Console().Error("Property %s of type %s was subject to a partial restore.\n",PropName.c_str(),TypeName.c_str());
405
        }
406
        catch (const Base::Exception &e) {
407
            Base::Console().Error("%s\n", e.what());
408
        }
409
        catch (const std::exception &e) {
410
            Base::Console().Error("%s\n", e.what());
411
        }
412
        catch (const char* e) {
413
            Base::Console().Error("%s\n", e);
414
        }
415
#ifndef FC_DEBUG
416
        catch (...) {
417
            Base::Console().Error("PropertyContainer::Restore: Unknown C++ exception thrown\n");
418
        }
419
#endif
420
        reader.readEndElement("Property");
421
    }
422
    reader.readEndElement("Properties");
423
}
424

425
void PropertyContainer::onPropertyStatusChanged(const Property &prop, unsigned long oldStatus)
426
{
427
    (void)prop;
428
    (void)oldStatus;
429
}
430

431
void PropertyData::addProperty(OffsetBase offsetBase,const char* PropName, Property *Prop, const char* PropertyGroup , PropertyType Type, const char* PropertyDocu)
432
{
433
#ifdef FC_DEBUG
434
    if(!parentMerged)
435
#endif
436
    {
437
        short offset = offsetBase.getOffsetTo(Prop);
438
        if(offset < 0)
439
            throw Base::RuntimeError("Invalid static property");
440
        auto &index = propertyData.get<1>();
441
        auto it = index.find(PropName);
442
        if(it == index.end()) {
443
            if(parentMerged)
444
                throw Base::RuntimeError("Cannot add static property");
445
            index.emplace(PropName, PropertyGroup, PropertyDocu, offset, Type);
446
        } else{
447
#ifdef FC_DEBUG
448
            if(it->Offset != offset) {
449
                FC_ERR("Duplicate property '" << PropName << "'");
450
            }
451
#endif
452
        }
453
    }
454

455
    Prop->syncType(Type);
456
    Prop->myName = PropName;
457
}
458

459
void PropertyData::merge(PropertyData *other) const {
460
    if(!other)
461
        other = const_cast<PropertyData*>(parentPropertyData);
462
    if(other == parentPropertyData) {
463
        if(parentMerged)
464
            return;
465
        parentMerged = true;
466
    }
467
    if(other)  {
468
        other->merge();
469
        auto &index = propertyData.get<0>();
470
        for(const auto &spec : other->propertyData.get<0>())
471
            index.push_back(spec);
472
    }
473
}
474

475
void PropertyData::split(PropertyData *other) {
476
    if(other == parentPropertyData) {
477
        if(!parentMerged)
478
            return;
479
        parentMerged = false;
480
    }
481
    if(other)  {
482
        auto &index = propertyData.get<2>();
483
        for(const auto &spec : other->propertyData.get<0>())
484
            index.erase(spec.Offset);
485
    }
486
}
487

488
const PropertyData::PropertySpec *PropertyData::findProperty(OffsetBase offsetBase,const char* PropName) const
489
{
490
    (void)offsetBase;
491
    merge();
492
    auto &index = propertyData.get<1>();
493
    auto it = index.find(PropName);
494
    if(it != index.end())
495
        return &(*it);
496
    return nullptr;
497
}
498

499
const PropertyData::PropertySpec *PropertyData::findProperty(OffsetBase offsetBase,const Property* prop) const
500
{
501
    merge();
502
    int diff = offsetBase.getOffsetTo(prop);
503
    if(diff<0)
504
        return nullptr;
505

506
    auto &index = propertyData.get<2>();
507
    auto it = index.find(diff);
508
    if(it!=index.end())
509
        return &(*it);
510

511
    return nullptr;
512
}
513

514
const char* PropertyData::getName(OffsetBase offsetBase,const Property* prop) const
515
{
516
  const PropertyData::PropertySpec* Spec = findProperty(offsetBase,prop);
517

518
  if(Spec)
519
    return Spec->Name;
520
  else
521
    return nullptr;
522
}
523

524
short PropertyData::getType(OffsetBase offsetBase,const Property* prop) const
525
{
526
  const PropertyData::PropertySpec* Spec = findProperty(offsetBase,prop);
527

528
  if(Spec)
529
    return Spec->Type;
530
  else
531
    return 0;
532
}
533

534
short PropertyData::getType(OffsetBase offsetBase,const char* name) const
535
{
536
  const PropertyData::PropertySpec* Spec = findProperty(offsetBase,name);
537

538
  if(Spec)
539
    return Spec->Type;
540
  else
541
    return 0;
542
}
543

544
const char* PropertyData::getGroup(OffsetBase offsetBase,const Property* prop) const
545
{
546
  const PropertyData::PropertySpec* Spec = findProperty(offsetBase,prop);
547

548
  if(Spec)
549
    return Spec->Group;
550
  else
551
    return nullptr;
552
}
553

554
const char* PropertyData::getGroup(OffsetBase offsetBase,const char* name) const
555
{
556
  const PropertyData::PropertySpec* Spec = findProperty(offsetBase,name);
557

558
  if(Spec)
559
    return Spec->Group;
560
  else
561
    return nullptr;
562
}
563

564
const char* PropertyData::getDocumentation(OffsetBase offsetBase,const Property* prop) const
565
{
566
  const PropertyData::PropertySpec* Spec = findProperty(offsetBase,prop);
567

568
  if(Spec)
569
    return Spec->Docu;
570
  else
571
    return nullptr;
572
}
573

574
const char* PropertyData::getDocumentation(OffsetBase offsetBase,const char* name) const
575
{
576
  const PropertyData::PropertySpec* Spec = findProperty(offsetBase,name);
577

578
  if(Spec)
579
    return Spec->Docu;
580
  else
581
    return nullptr;
582
}
583

584
Property *PropertyData::getPropertyByName(OffsetBase offsetBase,const char* name) const
585
{
586
  const PropertyData::PropertySpec* Spec = findProperty(offsetBase,name);
587

588
  if(Spec)
589
    return reinterpret_cast<Property *>(Spec->Offset + offsetBase.getOffset());
590
  else
591
    return nullptr;
592
}
593

594
void PropertyData::getPropertyMap(OffsetBase offsetBase,std::map<std::string,Property*> &Map) const
595
{
596
    merge();
597
    for(auto &spec : propertyData.get<0>())
598
        Map[spec.Name] = reinterpret_cast<Property *>(spec.Offset + offsetBase.getOffset());
599
}
600

601
void PropertyData::getPropertyList(OffsetBase offsetBase,std::vector<Property*> &List) const
602
{
603
    merge();
604
    size_t base = List.size();
605
    List.reserve(base+propertyData.size());
606
    for (auto &spec : propertyData.get<0>())
607
        List.push_back(reinterpret_cast<Property *>(spec.Offset + offsetBase.getOffset()));
608
}
609

610
void PropertyData::getPropertyNamedList(OffsetBase offsetBase,
611
        std::vector<std::pair<const char*,Property*> > &List) const
612
{
613
    merge();
614
    size_t base = List.size();
615
    List.reserve(base+propertyData.size());
616
    for (auto &spec : propertyData.get<0>()) {
617
        auto prop = reinterpret_cast<Property *>(spec.Offset + offsetBase.getOffset());
618
        List.emplace_back(prop->getName(),prop);
619
    }
620
}
621

622

623

624
/** \defgroup PropFrame Property framework
625
    \ingroup APP
626
    \brief System to access object properties
627
\section Introduction
628
The property framework introduces the ability to access attributes (member variables) of a class by name without
629
knowing the class type. It's like the reflection mechanism of Java or C#.
630
This ability is introduced by the App::PropertyContainer class and can be used by all derived classes.
631

632
This makes it possible in the first place to make an automatic mapping to python (e.g. in App::FeaturePy) and
633
abstract editing properties in Gui::PropertyEditor.
634

635
\section Examples
636

637
Here some little examples how to use it:
638

639
\code
640
// search in PropertyList
641
Property *prop = _pcFeature->getPropertyByName(attr);
642
if(prop)
643
{
644
  return prop->getPyObject();
645
}
646
\endcode
647

648
or:
649

650
\code
651
void PropertyContainer::Restore(Base::Reader &reader)
652
{
653
  reader.readElement("Properties");
654
  int Cnt = reader.getAttributeAsInteger("Count");
655

656
  for(int i=0 ;i<Cnt ;i++)
657
  {
658
    reader.readElement("Property");
659
    string PropName = reader.getAttribute("name");
660
    Property* prop = getPropertyByName(PropName.c_str());
661
    if(prop)
662
      prop->Restore(reader);
663

664
    reader.readEndElement("Property");
665
  }
666
  reader.readEndElement("Properties");
667
}
668
\endcode
669

670
*/
671

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

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

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

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