1
/***************************************************************************
2
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
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"
28
#include <boost/algorithm/string/predicate.hpp>
30
#include <Base/Console.h>
31
#include <Base/Exception.h>
32
#include <Base/Reader.h>
33
#include <Base/Writer.h>
35
#include "PropertyLinks.h"
36
#include "Application.h"
38
#include "DocumentObject.h"
39
#include "DocumentObjectPy.h"
40
#include "ObjectIdentifier.h"
43
FC_LOG_LEVEL_INIT("PropertyLinks",true,true)
48
namespace sp = std::placeholders;
50
//**************************************************************************
51
//**************************************************************************
53
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
55
TYPESYSTEM_SOURCE_ABSTRACT(App::PropertyLinkBase , App::Property)
57
static std::unordered_map<std::string, std::set<PropertyLinkBase*> > _LabelMap;
58
PropertyLinkBase::PropertyLinkBase() = default;
60
PropertyLinkBase::~PropertyLinkBase() {
61
unregisterLabelReferences();
62
unregisterElementReference();
65
void PropertyLinkBase::setAllowExternal(bool allow) {
66
setFlag(LinkAllowExternal,allow);
69
void PropertyLinkBase::hasSetValue() {
70
auto owner = dynamic_cast<DocumentObject*>(getContainer());
72
owner->clearOutListCache();
73
Property::hasSetValue();
76
bool PropertyLinkBase::isSame(const Property &other) const
80
if(other.isDerivedFrom(PropertyLinkBase::getClassTypeId())
81
|| getScope() != static_cast<const PropertyLinkBase*>(&other)->getScope())
84
static std::vector<App::DocumentObject*> ret;
85
static std::vector<std::string> subs;
86
static std::vector<App::DocumentObject*> ret2;
87
static std::vector<std::string> subs2;
93
getLinks(ret,true,&subs,false);
94
static_cast<const PropertyLinkBase *>(&other)->getLinks(ret2,true,&subs2,true);
96
return ret==ret2 && subs==subs2;
99
void PropertyLinkBase::unregisterElementReference() {
102
void PropertyLinkBase::unregisterLabelReferences()
104
for(auto &label : _LabelRefs) {
105
auto it = _LabelMap.find(label);
106
if(it!=_LabelMap.end()) {
107
it->second.erase(this);
108
if(it->second.empty())
115
void PropertyLinkBase::getLabelReferences(std::vector<std::string> &subs,const char *subname) {
117
for (; (subname = strchr(subname, '$')) != nullptr; subname = dot + 1) {
119
dot = strchr(subname,'.');
121
subs.emplace_back(subname,dot-subname);
125
void PropertyLinkBase::registerLabelReferences(std::vector<std::string> &&labels, bool reset) {
127
unregisterLabelReferences();
128
for(auto &label : labels) {
129
auto res = _LabelRefs.insert(std::move(label));
131
_LabelMap[*res.first].insert(this);
135
void PropertyLinkBase::checkLabelReferences(const std::vector<std::string> &subs, bool reset) {
137
unregisterLabelReferences();
138
std::vector<std::string> labels;
139
for(auto &sub : subs) {
141
getLabelReferences(labels,sub.c_str());
142
registerLabelReferences(std::move(labels),false);
146
std::string PropertyLinkBase::updateLabelReference(const App::DocumentObject *parent,
147
const char *subname, App::DocumentObject *obj, const std::string &ref, const char *newLabel)
149
if(!obj || !obj->isAttachedToDocument() || !parent || !parent->isAttachedToDocument())
152
// Because the label is allowed to be the same across different
153
// hierarchies, we have to search for all occurrences, and make sure the
154
// referenced sub-object at the found hierarchy is actually the given
156
for (const char *pos = subname; ((pos = strstr(pos, ref.c_str())) != nullptr); pos += ref.size()) {
157
auto sub = std::string(subname,pos+ref.size()-subname);
158
auto sobj = parent->getSubObject(sub.c_str());
161
sub.replace(pos+1-subname,ref.size()-2,newLabel);
168
std::vector<std::pair<Property*, std::unique_ptr<Property> > >
169
PropertyLinkBase::updateLabelReferences(App::DocumentObject *obj, const char *newLabel)
171
std::vector<std::pair<Property*,std::unique_ptr<Property> > > ret;
172
if(!obj || !obj->isAttachedToDocument())
174
auto it = _LabelMap.find(obj->Label.getStrValue());
175
if(it == _LabelMap.end())
177
std::string ref("$");
178
ref += obj->Label.getValue();
180
std::vector<PropertyLinkBase*> props;
181
props.reserve(it->second.size());
182
props.insert(props.end(),it->second.begin(),it->second.end());
183
for(auto prop : props) {
184
if(!prop->getContainer())
186
std::unique_ptr<Property> copy(prop->CopyOnLabelChange(obj,ref,newLabel));
188
ret.emplace_back(prop,std::move(copy));
193
static std::string propertyName(const Property *prop) {
196
if(!prop->getContainer() || !prop->hasName()) {
197
auto xlink = Base::freecad_dynamic_cast<const PropertyXLink>(prop);
199
return propertyName(xlink->parent());
201
return prop->getFullName();
204
void PropertyLinkBase::updateElementReferences(DocumentObject *feature, bool reverse) {
209
void PropertyLinkBase::_registerElementReference(App::DocumentObject *obj, std::string &sub, ShadowSub &shadow)
218
explicit StringGuard(char *c)
237
void PropertyLinkBase::restoreLabelReference(const DocumentObject *obj,
238
std::string &subname, ShadowSub *shadow)
240
std::ostringstream ss;
241
char *sub = &subname[0];
243
for(char *dot=strchr(next,'.');dot;next=dot+1,dot=strchr(next,'.')) {
244
if(dot!=next && dot[-1]!='@')
246
DocumentObject *sobj;
248
StringGuard guard(dot-1);
249
sobj = obj->getSubObject(subname.c_str());
251
FC_ERR("Failed to restore label reference " << obj->getFullName()
258
ss.write(sub,next-sub);
259
ss << '$' << sobj->Label.getStrValue() << '.';
262
if(sub == subname.c_str())
265
size_t count = sub-subname.c_str();
266
const auto &newSub = ss.str();
267
if(shadow && shadow->second.size()>=count)
268
shadow->second = newSub + (shadow->second.c_str()+count);
269
if(shadow && shadow->first.size()>=count)
270
shadow->first = newSub + (shadow->first.c_str()+count);
271
subname = newSub + sub;
274
bool PropertyLinkBase::_updateElementReference(DocumentObject *feature,
275
App::DocumentObject *obj, std::string &sub, ShadowSub &shadow,
276
bool reverse, bool notify)
286
std::pair<DocumentObject*, std::string>
287
PropertyLinkBase::tryReplaceLink(const PropertyContainer *owner, DocumentObject *obj,
288
const DocumentObject *parent, DocumentObject *oldObj, DocumentObject *newObj, const char *subname)
290
std::pair<DocumentObject*, std::string> res;
294
if(owner == parent) {
296
if(subname) res.second = subname;
301
if(!subname || !subname[0])
304
App::DocumentObject *prev = obj;
305
std::size_t prevPos = 0;
306
std::string sub = subname;
307
for(auto pos=sub.find('.');pos!=std::string::npos;pos=sub.find('.',pos)) {
311
auto sobj = obj->getSubObject(sub.c_str());
317
if(sub[prevPos] == '$')
318
sub.replace(prevPos+1,pos-1-prevPos,newObj->Label.getValue());
320
sub.replace(prevPos,pos-1-prevPos,newObj->getNameInDocument());
322
res.second = std::move(sub);
326
}else if(prev == parent)
334
std::pair<DocumentObject*,std::vector<std::string> >
335
PropertyLinkBase::tryReplaceLinkSubs(const PropertyContainer *owner,
336
DocumentObject *obj, const DocumentObject *parent, DocumentObject *oldObj,
337
DocumentObject *newObj, const std::vector<std::string> &subs)
339
std::pair<DocumentObject*,std::vector<std::string> > res;
342
auto r = tryReplaceLink(owner,obj,parent,oldObj,newObj);
348
for(auto it=subs.begin();it!=subs.end();++it) {
349
auto r = tryReplaceLink(owner,obj,parent,oldObj,newObj,it->c_str());
353
res.second.insert(res.second.end(),subs.begin(),it);
355
res.second.push_back(std::move(r.second));
357
res.second.push_back(*it);
362
//**************************************************************************
363
//**************************************************************************
364
// PropertyLinkListBase
365
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
367
TYPESYSTEM_SOURCE_ABSTRACT(App::PropertyLinkListBase , App::PropertyLinkBase)
369
//**************************************************************************
370
//**************************************************************************
372
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
374
TYPESYSTEM_SOURCE(App::PropertyLink, App::PropertyLinkBase)
375
TYPESYSTEM_SOURCE(App::PropertyLinkChild , App::PropertyLink)
376
TYPESYSTEM_SOURCE(App::PropertyLinkGlobal , App::PropertyLink)
377
TYPESYSTEM_SOURCE(App::PropertyLinkHidden , App::PropertyLink)
379
//**************************************************************************
380
// Construction/Destruction
383
PropertyLink::PropertyLink() = default;
385
PropertyLink::~PropertyLink()
390
//**************************************************************************
391
// Base class implementer
393
void PropertyLink::resetLink() {
394
//in case this property gets dynamically removed
396
// maintain the back link in the DocumentObject class if it is from a document object
397
if (_pcScope!=LinkScope::Hidden &&
400
getContainer()->isDerivedFrom(App::DocumentObject::getClassTypeId()))
402
App::DocumentObject* parent = static_cast<DocumentObject*>(getContainer());
403
// before accessing internals make sure the object is not about to be destroyed
404
// otherwise the backlink contains dangling pointers
405
if (!parent->testStatus(ObjectStatus::Destroy)) {
407
_pcLink->_removeBackLink(parent);
414
void PropertyLink::setValue(App::DocumentObject * lValue)
416
auto parent = dynamic_cast<App::DocumentObject*>(getContainer());
417
if(!testFlag(LinkAllowExternal) && parent && lValue && parent->getDocument()!=lValue->getDocument())
418
throw Base::ValueError("PropertyLink does not support external object");
422
// maintain the back link in the DocumentObject class if it is from a document object
423
if (_pcScope!=LinkScope::Hidden && parent) {
424
// before accessing internals make sure the object is not about to be destroyed
425
// otherwise the backlink contains dangling pointers
426
if (!parent->testStatus(ObjectStatus::Destroy)) {
428
_pcLink->_removeBackLink(parent);
430
lValue->_addBackLink(parent);
438
App::DocumentObject * PropertyLink::getValue() const
443
App::DocumentObject * PropertyLink::getValue(Base::Type t) const
445
return (_pcLink && _pcLink->getTypeId().isDerivedFrom(t)) ? _pcLink : nullptr;
448
PyObject *PropertyLink::getPyObject()
451
return _pcLink->getPyObject();
456
void PropertyLink::setPyObject(PyObject *value)
458
Base::PyTypeCheck(&value, &DocumentObjectPy::Type);
460
DocumentObjectPy *pcObject = static_cast<DocumentObjectPy*>(value);
461
setValue(pcObject->getDocumentObjectPtr());
468
void PropertyLink::Save (Base::Writer &writer) const
470
writer.Stream() << writer.ind() << "<Link value=\"" << (_pcLink?_pcLink->getExportName():"") <<"\"/>" << std::endl;
473
void PropertyLink::Restore(Base::XMLReader &reader)
476
reader.readElement("Link");
477
// get the value of my attribute
478
std::string name = reader.getName(reader.getAttribute("value"));
480
// Property not in a DocumentObject!
481
assert(getContainer()->isDerivedFrom<App::DocumentObject>() );
484
DocumentObject* parent = static_cast<DocumentObject*>(getContainer());
486
App::Document* document = parent->getDocument();
487
DocumentObject* object = document ? document->getObject(name.c_str()) : nullptr;
489
if (reader.isVerbose()) {
490
Base::Console().Warning("Lost link to '%s' while loading, maybe "
491
"an object was not loaded correctly\n",name.c_str());
494
else if (parent == object) {
495
if (reader.isVerbose()) {
496
Base::Console().Warning("Object '%s' links to itself, nullify it\n",name.c_str());
508
Property *PropertyLink::Copy() const
510
PropertyLink *p= new PropertyLink();
511
p->_pcLink = _pcLink;
515
void PropertyLink::Paste(const Property &from)
517
if (!from.isDerivedFrom(PropertyLink::getClassTypeId()))
518
throw Base::TypeError("Incompatible property to paste to");
520
setValue(static_cast<const PropertyLink&>(from)._pcLink);
523
void PropertyLink::getLinks(std::vector<App::DocumentObject *> &objs,
524
bool all, std::vector<std::string> *subs, bool newStyle) const
528
if((all||_pcScope!=LinkScope::Hidden) && _pcLink && _pcLink->isAttachedToDocument())
529
objs.push_back(_pcLink);
532
void PropertyLink::breakLink(App::DocumentObject *obj, bool clear) {
533
if(_pcLink == obj || (clear && getContainer()==obj))
537
bool PropertyLink::adjustLink(const std::set<App::DocumentObject*> &inList) {
542
Property *PropertyLink::CopyOnLinkReplace(const App::DocumentObject *parent,
543
App::DocumentObject *oldObj, App::DocumentObject *newObj) const
545
auto res = tryReplaceLink(getContainer(),_pcLink,parent,oldObj,newObj);
547
auto p = new PropertyLink();
548
p->_pcLink = res.first;
554
//**************************************************************************
556
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
558
TYPESYSTEM_SOURCE(App::PropertyLinkList, App::PropertyLinkListBase)
559
TYPESYSTEM_SOURCE(App::PropertyLinkListChild, App::PropertyLinkList)
560
TYPESYSTEM_SOURCE(App::PropertyLinkListGlobal, App::PropertyLinkList)
561
TYPESYSTEM_SOURCE(App::PropertyLinkListHidden, App::PropertyLinkList)
563
//**************************************************************************
564
// Construction/Destruction
567
PropertyLinkList::PropertyLinkList() = default;
569
PropertyLinkList::~PropertyLinkList()
571
//in case this property gety dynamically removed
573
//maintain the back link in the DocumentObject class
574
if (_pcScope!=LinkScope::Hidden &&
575
!_lValueList.empty() &&
577
getContainer()->isDerivedFrom(App::DocumentObject::getClassTypeId()))
579
App::DocumentObject* parent = static_cast<DocumentObject*>(getContainer());
580
// before accessing internals make sure the object is not about to be destroyed
581
// otherwise the backlink contains dangling pointers
582
if (!parent->testStatus(ObjectStatus::Destroy)) {
583
for(auto *obj : _lValueList) {
585
obj->_removeBackLink(parent);
593
void PropertyLinkList::setSize(int newSize)
595
for(int i=newSize;i<(int)_lValueList.size();++i) {
596
auto obj = _lValueList[i];
597
if (!obj || !obj->isAttachedToDocument())
599
_nameMap.erase(obj->getNameInDocument());
601
if (_pcScope!=LinkScope::Hidden)
602
obj->_removeBackLink(static_cast<DocumentObject*>(getContainer()));
605
_lValueList.resize(newSize);
608
void PropertyLinkList::setSize(int newSize, const_reference def) {
609
auto oldSize = getSize();
611
for(auto i=oldSize;i<newSize;++i)
612
_lValueList[i] = def;
615
void PropertyLinkList::set1Value(int idx, DocumentObject* const &value) {
616
DocumentObject *obj = nullptr;
617
if(idx>=0 && idx<(int)_lValueList.size()) {
618
obj = _lValueList[idx];
623
if(!value || !value->isAttachedToDocument())
624
throw Base::ValueError("invalid document object");
629
if (getContainer() && getContainer()->isDerivedFrom(App::DocumentObject::getClassTypeId())) {
630
App::DocumentObject* parent = static_cast<DocumentObject*>(getContainer());
631
// before accessing internals make sure the object is not about to be destroyed
632
// otherwise the backlink contains dangling pointers
633
if (!parent->testStatus(ObjectStatus::Destroy) && _pcScope!=LinkScope::Hidden) {
635
obj->_removeBackLink(static_cast<DocumentObject*>(getContainer()));
637
value->_addBackLink(static_cast<DocumentObject*>(getContainer()));
642
inherited::set1Value(idx,value);
645
void PropertyLinkList::setValues(const std::vector<DocumentObject*>& lValue) {
646
if(lValue.size()==1 && !lValue[0]) {
647
// one null element means clear, as backward compatibility for old code
648
setValues(std::vector<DocumentObject*>());
652
auto parent = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
653
for(auto obj : lValue) {
654
if(!obj || !obj->isAttachedToDocument())
655
throw Base::ValueError("PropertyLinkList: invalid document object");
656
if(!testFlag(LinkAllowExternal) && parent && parent->getDocument()!=obj->getDocument())
657
throw Base::ValueError("PropertyLinkList does not support external object");
662
//maintain the back link in the DocumentObject class
664
// before accessing internals make sure the object is not about to be destroyed
665
// otherwise the backlink contains dangling pointers
666
if (!parent->testStatus(ObjectStatus::Destroy) && _pcScope!=LinkScope::Hidden) {
667
for(auto *obj : _lValueList) {
669
obj->_removeBackLink(parent);
671
for(auto *obj : lValue) {
673
obj->_addBackLink(parent);
678
inherited::setValues(lValue);
681
PyObject *PropertyLinkList::getPyObject()
683
int count = getSize();
684
#if 0//FIXME: Should switch to tuple
685
Py::Tuple sequence(count);
687
Py::List sequence(count);
689
for (int i = 0; i<count; i++) {
690
auto obj = _lValueList[i];
691
if(obj && obj->isAttachedToDocument())
692
sequence.setItem(i, Py::asObject(_lValueList[i]->getPyObject()));
694
sequence.setItem(i, Py::None());
697
return Py::new_reference_to(sequence);
700
DocumentObject *PropertyLinkList::getPyValue(PyObject *item) const
702
Base::PyTypeCheck(&item, &DocumentObjectPy::Type);
704
return item ? static_cast<DocumentObjectPy*>(item)->getDocumentObjectPtr() : nullptr;
707
void PropertyLinkList::Save(Base::Writer &writer) const
709
writer.Stream() << writer.ind() << "<LinkList count=\"" << getSize() << "\">" << endl;
711
for (int i = 0; i<getSize(); i++) {
712
DocumentObject* obj = _lValueList[i];
714
writer.Stream() << writer.ind() << "<Link value=\"" << obj->getExportName() << "\"/>" << endl;
716
writer.Stream() << writer.ind() << "<Link value=\"\"/>" << endl;
720
writer.Stream() << writer.ind() << "</LinkList>" << endl;
723
void PropertyLinkList::Restore(Base::XMLReader &reader)
726
reader.readElement("LinkList");
727
// get the value of my attribute
728
int count = reader.getAttributeAsInteger("count");
729
App::PropertyContainer* container = getContainer();
731
throw Base::RuntimeError("Property is not part of a container");
732
if (!container->isDerivedFrom<App::DocumentObject>()) {
733
std::stringstream str;
734
str << "Container is not a document object ("
735
<< container->getTypeId().getName() << ")";
736
throw Base::TypeError(str.str());
739
std::vector<DocumentObject*> values;
740
values.reserve(count);
741
for (int i = 0; i < count; i++) {
742
reader.readElement("Link");
743
std::string name = reader.getName(reader.getAttribute("value"));
744
// In order to do copy/paste it must be allowed to have defined some
745
// referenced objects in XML which do not exist anymore in the new
746
// document. Thus, we should silently ignore this.
747
// Property not in an object!
748
DocumentObject* father = static_cast<DocumentObject*>(getContainer());
749
App::Document* document = father->getDocument();
750
DocumentObject* child = document ? document->getObject(name.c_str()) : nullptr;
752
values.push_back(child);
753
else if (reader.isVerbose())
754
FC_WARN("Lost link to " << (document?document->getName():"") << " " << name
755
<< " while loading, maybe an object was not loaded correctly");
758
reader.readEndElement("LinkList");
764
Property *PropertyLinkList::CopyOnLinkReplace(const App::DocumentObject *parent,
765
App::DocumentObject *oldObj, App::DocumentObject *newObj) const
767
std::vector<DocumentObject*> links;
770
for(auto it=_lValueList.begin();it!=_lValueList.end();++it) {
771
auto res = tryReplaceLink(getContainer(),*it,parent,oldObj,newObj);
776
links.insert(links.end(),_lValueList.begin(),it);
778
links.push_back(res.first);
779
} else if(*it == newObj) {
780
// in case newObj already exists here, we shall remove all existing
781
// entry, and insert it to take over oldObj's position.
784
links.insert(links.end(),_lValueList.begin(),it);
787
links.push_back(*it);
791
auto p= new PropertyLinkList();
792
p->_lValueList = std::move(links);
796
Property *PropertyLinkList::Copy() const
798
PropertyLinkList *p = new PropertyLinkList();
799
p->_lValueList = _lValueList;
803
void PropertyLinkList::Paste(const Property &from)
805
if(!from.isDerivedFrom(PropertyLinkList::getClassTypeId()))
806
throw Base::TypeError("Incompatible property to paste to");
808
setValues(static_cast<const PropertyLinkList&>(from)._lValueList);
811
unsigned int PropertyLinkList::getMemSize() const
813
return static_cast<unsigned int>(_lValueList.size() * sizeof(App::DocumentObject *));
816
DocumentObject *PropertyLinkList::find(const std::string &name, int *pindex) const {
817
if(_nameMap.empty() || _nameMap.size()>_lValueList.size()) {
819
for(int i=0;i<(int)_lValueList.size();++i) {
820
auto obj = _lValueList[i];
821
if(obj && obj->isAttachedToDocument())
822
_nameMap[obj->getNameInDocument()] = i;
825
auto it = _nameMap.find(name);
826
if(it == _nameMap.end())
828
if(pindex) *pindex = it->second;
829
return _lValueList[it->second];
832
void PropertyLinkList::getLinks(std::vector<App::DocumentObject *> &objs,
833
bool all, std::vector<std::string> *subs, bool newStyle) const
837
if(all||_pcScope!=LinkScope::Hidden) {
838
objs.reserve(objs.size()+_lValueList.size());
839
for(auto obj : _lValueList) {
840
if(obj && obj->isAttachedToDocument())
846
void PropertyLinkList::breakLink(App::DocumentObject *obj, bool clear) {
847
if(clear && getContainer()==obj) {
851
std::vector<App::DocumentObject*> values;
852
values.reserve(_lValueList.size());
853
for(auto o : _lValueList) {
857
if(values.size()!=_lValueList.size())
861
bool PropertyLinkList::adjustLink(const std::set<App::DocumentObject*> &inList) {
867
//**************************************************************************
869
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
871
TYPESYSTEM_SOURCE(App::PropertyLinkSub, App::PropertyLinkBase)
872
TYPESYSTEM_SOURCE(App::PropertyLinkSubChild, App::PropertyLinkSub)
873
TYPESYSTEM_SOURCE(App::PropertyLinkSubGlobal, App::PropertyLinkSub)
874
TYPESYSTEM_SOURCE(App::PropertyLinkSubHidden, App::PropertyLinkSub)
876
//**************************************************************************
877
// Construction/Destruction
880
PropertyLinkSub::PropertyLinkSub() = default;
882
PropertyLinkSub::~PropertyLinkSub()
884
//in case this property is dynamically removed
886
if (_pcLinkSub && getContainer() && getContainer()->isDerivedFrom(App::DocumentObject::getClassTypeId())) {
887
App::DocumentObject* parent = static_cast<DocumentObject*>(getContainer());
888
// before accessing internals make sure the object is not about to be destroyed
889
// otherwise the backlink contains dangling pointers
890
if (!parent->testStatus(ObjectStatus::Destroy) && _pcScope!=LinkScope::Hidden) {
892
_pcLinkSub->_removeBackLink(parent);
898
void PropertyLinkSub::setSyncSubObject(bool enable)
900
_Flags.set((std::size_t)LinkSyncSubObject, enable);
903
void PropertyLinkSub::setValue(App::DocumentObject * lValue,
904
const std::vector<std::string> &SubList, std::vector<ShadowSub> &&shadows)
906
setValue(lValue,std::vector<std::string>(SubList),std::move(shadows));
909
void PropertyLinkSub::setValue(App::DocumentObject * lValue,
910
std::vector<std::string> &&subs, std::vector<ShadowSub> &&shadows)
912
auto parent = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
914
if(!lValue->isAttachedToDocument())
915
throw Base::ValueError("PropertyLinkSub: invalid document object");
916
if(!testFlag(LinkAllowExternal) && parent && parent->getDocument()!=lValue->getDocument())
917
throw Base::ValueError("PropertyLinkSub does not support external object");
922
// before accessing internals make sure the object is not about to be destroyed
923
// otherwise the backlink contains dangling pointers
924
if (!parent->testStatus(ObjectStatus::Destroy) && _pcScope!=LinkScope::Hidden) {
926
_pcLinkSub->_removeBackLink(parent);
928
lValue->_addBackLink(parent);
933
_cSubList=std::move(subs);
934
if(shadows.size()==_cSubList.size())
935
_ShadowSubList = std::move(shadows);
937
updateElementReference(nullptr);
938
checkLabelReferences(_cSubList);
942
App::DocumentObject * PropertyLinkSub::getValue() const
947
const std::vector<std::string>& PropertyLinkSub::getSubValues() const
952
static inline const std::string &getSubNameWithStyle(const std::string &subName,
953
const PropertyLinkBase::ShadowSub &shadow, bool newStyle)
956
if(!shadow.second.empty())
957
return shadow.second;
958
}else if(!shadow.first.empty())
963
std::vector<std::string> PropertyLinkSub::getSubValues(bool newStyle) const {
964
assert(_cSubList.size() == _ShadowSubList.size());
965
std::vector<std::string> ret;
966
ret.reserve(_cSubList.size());
967
for(size_t i=0;i<_ShadowSubList.size();++i)
968
ret.push_back(getSubNameWithStyle(_cSubList[i],_ShadowSubList[i],newStyle));
972
std::vector<std::string> PropertyLinkSub::getSubValuesStartsWith(const char* starter, bool newStyle) const
976
std::vector<std::string> temp;
977
for(const auto & it : _cSubList) {
978
if(strncmp(starter, it.c_str(), strlen(starter)) == 0) {
985
App::DocumentObject * PropertyLinkSub::getValue(Base::Type t) const
987
return (_pcLinkSub && _pcLinkSub->getTypeId().isDerivedFrom(t)) ? _pcLinkSub : nullptr;
990
PyObject *PropertyLinkSub::getPyObject()
993
Py::List list(static_cast<int>(_cSubList.size()));
995
tup[0] = Py::asObject(_pcLinkSub->getPyObject());
996
for(unsigned int i = 0;i<_cSubList.size(); i++)
997
list[i] = Py::String(_cSubList[i]);
999
return Py::new_reference_to(tup);
1002
return Py::new_reference_to(Py::None());
1006
void PropertyLinkSub::setPyObject(PyObject *value)
1008
if (PyObject_TypeCheck(value, &(DocumentObjectPy::Type))) {
1009
DocumentObjectPy *pcObject = static_cast<DocumentObjectPy*>(value);
1010
setValue(pcObject->getDocumentObjectPtr());
1012
else if (PyTuple_Check(value) || PyList_Check(value)) {
1013
Py::Sequence seq(value);
1016
else if(seq.size()!=2)
1017
throw Base::ValueError("Expect input sequence of size 2");
1018
else if (PyObject_TypeCheck(seq[0].ptr(), &(DocumentObjectPy::Type))) {
1019
DocumentObjectPy *pcObj = static_cast<DocumentObjectPy*>(seq[0].ptr());
1020
static const char *errMsg = "type of second element in tuple must be str or sequence of str";
1021
PropertyString propString;
1022
if (seq[1].isString()) {
1023
std::vector<std::string> vals;
1024
propString.setPyObject(seq[1].ptr());
1025
vals.emplace_back(propString.getValue());
1026
setValue(pcObj->getDocumentObjectPtr(),std::move(vals));
1028
else if (seq[1].isSequence()) {
1029
Py::Sequence list(seq[1]);
1030
std::vector<std::string> vals(list.size());
1032
for (Py::Sequence::iterator it = list.begin();it!=list.end();++it,++i) {
1033
if(!(*it).isString())
1034
throw Base::TypeError(errMsg);
1035
propString.setPyObject((*it).ptr());
1036
vals[i] = propString.getValue();
1038
setValue(pcObj->getDocumentObjectPtr(),std::move(vals));
1041
throw Base::TypeError(errMsg);
1045
std::string error = std::string("type of first element in tuple must be 'DocumentObject', not ");
1046
error += seq[0].ptr()->ob_type->tp_name;
1047
throw Base::TypeError(error);
1050
else if(Py_None == value) {
1054
std::string error = std::string("type must be 'DocumentObject', 'NoneType' or ('DocumentObject',['String',]) not ");
1055
error += value->ob_type->tp_name;
1056
throw Base::TypeError(error);
1060
static bool updateLinkReference(App::PropertyLinkBase *prop,
1061
App::DocumentObject *feature, bool reverse, bool notify,
1062
App::DocumentObject *link, std::vector<std::string> &subs, std::vector<int> &mapped,
1063
std::vector<PropertyLinkBase::ShadowSub> &shadows)
1067
prop->unregisterElementReference();
1069
shadows.resize(subs.size());
1070
if(!link || !link->isAttachedToDocument())
1072
auto owner = dynamic_cast<DocumentObject*>(prop->getContainer());
1073
if(owner && owner->isRestoring())
1076
bool touched = false;
1077
for(auto &sub : subs) {
1078
if(prop->_updateElementReference(
1079
feature,link,sub,shadows[i++],reverse,notify&&!touched))
1084
for(int idx : mapped) {
1085
if(idx<(int)subs.size() && !shadows[idx].first.empty())
1086
subs[idx] = shadows[idx].first;
1089
if(owner && feature)
1090
owner->onUpdateElementReference(prop);
1094
void PropertyLinkSub::afterRestore() {
1095
_ShadowSubList.resize(_cSubList.size());
1096
if(!testFlag(LinkRestoreLabel) ||!_pcLinkSub || !_pcLinkSub->isAttachedToDocument())
1098
setFlag(LinkRestoreLabel,false);
1099
for(std::size_t i=0;i<_cSubList.size();++i)
1100
restoreLabelReference(_pcLinkSub,_cSubList[i],&_ShadowSubList[i]);
1103
void PropertyLinkSub::onContainerRestored() {
1104
unregisterElementReference();
1105
if(!_pcLinkSub || !_pcLinkSub->isAttachedToDocument())
1107
for(std::size_t i=0;i<_cSubList.size();++i)
1108
_registerElementReference(_pcLinkSub,_cSubList[i],_ShadowSubList[i]);
1111
void PropertyLinkSub::updateElementReference(DocumentObject *feature, bool reverse, bool notify) {
1112
if(!updateLinkReference(this,feature,reverse,notify,_pcLinkSub,_cSubList,_mapped,_ShadowSubList))
1118
bool PropertyLinkSub::referenceChanged() const {
1119
return !_mapped.empty();
1122
std::string PropertyLinkBase::importSubName(Base::XMLReader &reader, const char *sub, bool &restoreLabel) {
1123
if(!reader.doNameMapping())
1125
std::ostringstream str;
1126
for(const char *dot=strchr(sub,'.');dot;sub=dot+1,dot=strchr(sub,'.')) {
1127
size_t count = dot-sub;
1128
const char *tail = ".";
1129
if(count && dot[-1] == '@') {
1130
// tail=='@' means we are exporting a label reference. So retain
1131
// this marker so that the label can be restored in afterRestore().
1134
restoreLabel = true;
1136
str << reader.getName(std::string(sub,count).c_str()) << tail;
1142
const char *PropertyLinkBase::exportSubName(std::string &output,
1143
const App::DocumentObject *obj, const char *sub, bool first_obj)
1145
std::ostringstream str;
1146
const char *res = sub;
1151
bool touched = false;
1153
auto dot = strchr(sub,'.');
1157
for(hash=sub;hash<dot && *hash!='#';++hash) {}
1158
App::Document *doc = nullptr;
1160
doc = GetApplication().getDocument(std::string(sub,hash-sub).c_str());
1163
if(obj && obj->isAttachedToDocument())
1164
doc = obj->getDocument();
1167
FC_ERR("Failed to get document for the first object in " << sub);
1170
obj = doc->getObject(std::string(sub,dot-sub).c_str());
1171
if(!obj || !obj->isAttachedToDocument())
1174
if(!obj->isExporting())
1175
str << doc->getName() << '#';
1178
}else if(!obj || !obj->isAttachedToDocument())
1181
for(const char *dot=strchr(sub,'.');dot;sub=dot+1,dot=strchr(sub,'.')) {
1182
// name with trailing '.'
1183
auto name = std::string(sub,dot-sub+1);
1187
obj = obj->getSubObject(name.c_str());
1188
if(!obj || !obj->isAttachedToDocument()) {
1189
FC_WARN("missing sub object '" << name << "' in '" << sub <<"'");
1192
if(obj->isExporting()) {
1193
if(name[0] == '$') {
1194
if(name.compare(1,name.size()-2,obj->Label.getValue())!=0) {
1195
str << obj->getExportName(true) << "@.";
1199
} else if(name.compare(0,name.size()-1,obj->getNameInDocument())==0) {
1200
str << obj->getExportName(true) << '.';
1211
return output.c_str();
1214
App::DocumentObject *PropertyLinkBase::tryImport(const App::Document *doc,
1215
const App::DocumentObject *obj, const std::map<std::string,std::string> &nameMap)
1217
if(doc && obj && obj->isAttachedToDocument()) {
1218
auto it = nameMap.find(obj->getExportName(true));
1219
if(it!=nameMap.end()) {
1220
obj = doc->getObject(it->second.c_str());
1222
FC_THROWM(Base::RuntimeError,"Cannot find import object " << it->second);
1225
return const_cast<DocumentObject*>(obj);
1228
std::string PropertyLinkBase::tryImportSubName(const App::DocumentObject *obj, const char *_subname,
1229
const App::Document *doc, const std::map<std::string,std::string> &nameMap)
1231
if(!doc || !obj || !obj->isAttachedToDocument())
1234
std::ostringstream ss;
1235
std::string subname(_subname);
1236
char *sub = &subname[0];
1238
for(char *dot=strchr(next,'.');dot;next=dot+1,dot=strchr(next,'.')) {
1239
StringGuard guard(dot);
1240
auto sobj = obj->getSubObject(subname.c_str());
1242
FC_ERR("Failed to restore label reference " << obj->getFullName() << '.' << subname);
1246
if(next[0] == '$') {
1247
if(strcmp(next+1,sobj->Label.getValue())!=0)
1249
} else if(strcmp(next,sobj->getNameInDocument())!=0) {
1252
auto it = nameMap.find(sobj->getExportName(true));
1253
if(it == nameMap.end())
1255
auto imported = doc->getObject(it->second.c_str());
1257
FC_THROWM(RuntimeError, "Failed to find imported object " << it->second);
1258
ss.write(sub,next-sub);
1260
ss << '$' << imported->Label.getStrValue() << '.';
1262
ss << it->second << '.';
1265
if(sub!=subname.c_str())
1270
#define ATTR_SHADOWED "shadowed"
1271
#define ATTR_SHADOW "shadow"
1272
#define ATTR_MAPPED "mapped"
1274
// We do not have topo naming yet, ignore shadow sub for now
1275
#define IGNORE_SHADOW true
1277
void PropertyLinkSub::Save (Base::Writer &writer) const
1279
assert(_cSubList.size() == _ShadowSubList.size());
1281
std::string internal_name;
1282
// it can happen that the object is still alive but is not part of the document anymore and thus
1284
if (_pcLinkSub && _pcLinkSub->isAttachedToDocument())
1285
internal_name = _pcLinkSub->getExportName();
1286
writer.Stream() << writer.ind() << "<LinkSub value=\""
1287
<< internal_name <<"\" count=\"" << _cSubList.size();
1288
writer.Stream() << "\">" << std::endl;
1290
auto owner = dynamic_cast<DocumentObject*>(getContainer());
1291
bool exporting = owner && owner->isExporting();
1292
for(unsigned int i = 0;i<_cSubList.size(); i++) {
1293
const auto &shadow = _ShadowSubList[i];
1294
// shadow.second stores the old style element name. For backward
1295
// compatibility reason, we shall store the old name into attribute
1296
// 'value' whenever possible.
1297
const auto &sub = shadow.second.empty()?_cSubList[i]:shadow.second;
1298
writer.Stream() << writer.ind() << "<Sub value=\"";
1300
std::string exportName;
1301
writer.Stream() << encodeAttribute(exportSubName(exportName,_pcLinkSub,sub.c_str()));
1302
if(!shadow.second.empty() && shadow.first == _cSubList[i])
1303
writer.Stream() << "\" " ATTR_MAPPED "=\"1";
1305
writer.Stream() << encodeAttribute(sub);
1306
if(!_cSubList[i].empty()) {
1307
if(sub!=_cSubList[i]) {
1308
// Stores the actual value that is shadowed. For new version FC,
1309
// we will restore this shadowed value instead.
1310
writer.Stream() << "\" " ATTR_SHADOWED "=\"" << encodeAttribute(_cSubList[i]);
1311
}else if(!shadow.first.empty()){
1312
// Here means the user set value is old style element name.
1313
// We shall then store the shadow somewhere else.
1314
writer.Stream() << "\" " ATTR_SHADOW "=\"" << encodeAttribute(shadow.first);
1318
writer.Stream()<<"\"/>" << endl;
1321
writer.Stream() << writer.ind() << "</LinkSub>" << endl ;
1324
void PropertyLinkSub::Restore(Base::XMLReader &reader)
1327
reader.readElement("LinkSub");
1328
// get the values of my attributes
1329
std::string name = reader.getName(reader.getAttribute("value"));
1330
int count = reader.getAttributeAsInteger("count");
1332
// Property not in a DocumentObject!
1333
assert(getContainer()->isDerivedFrom<App::DocumentObject>() );
1334
App::Document* document = static_cast<DocumentObject*>(getContainer())->getDocument();
1336
DocumentObject *pcObject = nullptr;
1337
if (!name.empty()) {
1338
pcObject = document ? document->getObject(name.c_str()) : nullptr;
1340
if (reader.isVerbose()) {
1341
FC_WARN("Lost link to " << name
1342
<< " while loading, maybe an object was not loaded correctly");
1347
std::vector<int> mapped;
1348
std::vector<std::string> values(count);
1349
std::vector<ShadowSub> shadows(count);
1350
bool restoreLabel=false;
1351
// Sub may store '.' separated object names, so be aware of the possible mapping when import
1352
for (int i = 0; i < count; i++) {
1353
reader.readElement("Sub");
1354
shadows[i].second = importSubName(reader,reader.getAttribute("value"),restoreLabel);
1355
if(reader.hasAttribute(ATTR_SHADOWED) && !IGNORE_SHADOW) {
1356
values[i] = shadows[i].first =
1357
importSubName(reader,reader.getAttribute(ATTR_SHADOWED),restoreLabel);
1359
values[i] = shadows[i].second;
1360
if(reader.hasAttribute(ATTR_SHADOW) && !IGNORE_SHADOW)
1361
shadows[i].first = importSubName(reader,reader.getAttribute(ATTR_SHADOW),restoreLabel);
1363
if(reader.hasAttribute(ATTR_MAPPED))
1364
mapped.push_back(i);
1366
setFlag(LinkRestoreLabel,restoreLabel);
1368
reader.readEndElement("LinkSub");
1371
setValue(pcObject,std::move(values),std::move(shadows));
1372
_mapped = std::move(mapped);
1379
template<class Func, class... Args >
1380
std::vector<std::string> updateLinkSubs(const App::DocumentObject *obj,
1381
const std::vector<std::string> &subs, Func *f, Args&&... args )
1383
if(!obj || !obj->isAttachedToDocument())
1386
std::vector<std::string> res;
1387
for(auto it=subs.begin();it!=subs.end();++it) {
1388
const auto &sub = *it;
1389
auto new_sub = (*f)(obj,sub.c_str(),std::forward<Args>(args)...);
1390
if(new_sub.size()) {
1392
res.reserve(subs.size());
1393
res.insert(res.end(),subs.begin(),it);
1395
res.push_back(std::move(new_sub));
1396
}else if(!res.empty())
1402
Property *PropertyLinkSub::CopyOnImportExternal(
1403
const std::map<std::string,std::string> &nameMap) const
1405
auto owner = dynamic_cast<const DocumentObject*>(getContainer());
1406
if(!owner || !owner->getDocument())
1408
if(!_pcLinkSub || !_pcLinkSub->isAttachedToDocument())
1411
auto subs = updateLinkSubs(_pcLinkSub,_cSubList,
1412
&tryImportSubName,owner->getDocument(),nameMap);
1413
auto linked = tryImport(owner->getDocument(),_pcLinkSub,nameMap);
1414
if(subs.empty() && linked==_pcLinkSub)
1417
PropertyLinkSub *p= new PropertyLinkSub();
1418
p->_pcLinkSub = linked;
1420
p->_cSubList = _cSubList;
1422
p->_cSubList = std::move(subs);
1426
Property *PropertyLinkSub::CopyOnLabelChange(App::DocumentObject *obj,
1427
const std::string &ref, const char *newLabel) const
1429
auto owner = dynamic_cast<const DocumentObject*>(getContainer());
1430
if(!owner || !owner->getDocument())
1432
if(!_pcLinkSub || !_pcLinkSub->isAttachedToDocument())
1435
auto subs = updateLinkSubs(_pcLinkSub,_cSubList,&updateLabelReference,obj,ref,newLabel);
1439
PropertyLinkSub *p= new PropertyLinkSub();
1440
p->_pcLinkSub = _pcLinkSub;
1441
p->_cSubList = std::move(subs);
1445
Property *PropertyLinkSub::CopyOnLinkReplace(const App::DocumentObject *parent,
1446
App::DocumentObject *oldObj, App::DocumentObject *newObj) const
1448
auto res = tryReplaceLinkSubs(getContainer(),_pcLinkSub,parent,oldObj,newObj,_cSubList);
1450
PropertyLinkSub *p= new PropertyLinkSub();
1451
p->_pcLinkSub = res.first;
1452
p->_cSubList = std::move(res.second);
1458
Property *PropertyLinkSub::Copy() const
1460
PropertyLinkSub *p= new PropertyLinkSub();
1461
p->_pcLinkSub = _pcLinkSub;
1462
p->_cSubList = _cSubList;
1466
void PropertyLinkSub::Paste(const Property &from)
1468
if(!from.isDerivedFrom(PropertyLinkSub::getClassTypeId()))
1469
throw Base::TypeError("Incompatible property to paste to");
1470
auto &link = static_cast<const PropertyLinkSub&>(from);
1471
setValue(link._pcLinkSub, link._cSubList);
1474
void PropertyLinkSub::getLinks(std::vector<App::DocumentObject *> &objs,
1475
bool all, std::vector<std::string> *subs, bool newStyle) const
1477
if(all||_pcScope!=LinkScope::Hidden) {
1478
if(_pcLinkSub && _pcLinkSub->isAttachedToDocument()) {
1479
objs.push_back(_pcLinkSub);
1481
*subs = getSubValues(newStyle);
1486
void PropertyLinkSub::breakLink(App::DocumentObject *obj, bool clear) {
1487
if(obj == _pcLinkSub || (clear && getContainer()==obj))
1491
static App::DocumentObject *adjustLinkSubs(App::PropertyLinkBase *prop,
1492
const std::set<App::DocumentObject*> &inList,
1493
App::DocumentObject *link, std::vector<std::string> &subs,
1494
std::map<App::DocumentObject *, std::vector<std::string> > *links=nullptr)
1496
App::DocumentObject *newLink = nullptr;
1497
for(auto &sub : subs) {
1498
size_t pos = sub.find('.');
1499
for(;pos!=std::string::npos;pos=sub.find('.',pos+1)) {
1500
auto sobj = link->getSubObject(sub.substr(0,pos+1).c_str());
1502
(!prop->testFlag(PropertyLinkBase::LinkAllowExternal) &&
1503
sobj->getDocument()!=link->getDocument()))
1505
pos = std::string::npos;
1509
if(inList.count(sobj))
1513
(*links)[sobj].push_back(sub.substr(pos+1));
1515
sub = sub.substr(pos+1);
1517
(*links)[sobj].push_back(sub.substr(pos+1));
1518
else if(sobj == newLink)
1519
sub = sub.substr(pos+1);
1522
if(pos == std::string::npos)
1528
bool PropertyLinkSub::adjustLink(const std::set<App::DocumentObject*> &inList) {
1529
if (_pcScope==LinkScope::Hidden)
1531
if(!_pcLinkSub || !_pcLinkSub->isAttachedToDocument() || !inList.count(_pcLinkSub))
1533
auto subs = _cSubList;
1534
auto link = adjustLinkSubs(this,inList,_pcLinkSub,subs);
1536
setValue(link,std::move(subs));
1542
//**************************************************************************
1543
// PropertyLinkSubList
1544
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1546
TYPESYSTEM_SOURCE(App::PropertyLinkSubList, App::PropertyLinkBase)
1547
TYPESYSTEM_SOURCE(App::PropertyLinkSubListChild, App::PropertyLinkSubList)
1548
TYPESYSTEM_SOURCE(App::PropertyLinkSubListGlobal, App::PropertyLinkSubList)
1549
TYPESYSTEM_SOURCE(App::PropertyLinkSubListHidden, App::PropertyLinkSubList)
1551
//**************************************************************************
1552
// Construction/Destruction
1555
PropertyLinkSubList::PropertyLinkSubList() = default;
1557
PropertyLinkSubList::~PropertyLinkSubList()
1559
//in case this property is dynamically removed
1561
//maintain backlinks
1562
if (!_lValueList.empty() && getContainer() && getContainer()->isDerivedFrom(App::DocumentObject::getClassTypeId())) {
1563
App::DocumentObject* parent = static_cast<DocumentObject*>(getContainer());
1564
// before accessing internals make sure the object is not about to be destroyed
1565
// otherwise the backlink contains dangling pointers
1566
if (!parent->testStatus(ObjectStatus::Destroy) && _pcScope!=LinkScope::Hidden) {
1567
for(auto *obj : _lValueList) {
1569
obj->_removeBackLink(parent);
1576
void PropertyLinkSubList::setSyncSubObject(bool enable)
1578
_Flags.set((std::size_t)LinkSyncSubObject, enable);
1581
void PropertyLinkSubList::verifyObject(App::DocumentObject* obj, App::DocumentObject* parent)
1584
if (!obj->isAttachedToDocument())
1585
throw Base::ValueError("PropertyLinkSubList: invalid document object");
1586
if (!testFlag(LinkAllowExternal) && parent && parent->getDocument() != obj->getDocument())
1587
throw Base::ValueError("PropertyLinkSubList does not support external object");
1591
void PropertyLinkSubList::setSize(int newSize)
1593
_lValueList.resize(newSize);
1594
_lSubList .resize(newSize);
1595
_ShadowSubList.resize(newSize);
1598
int PropertyLinkSubList::getSize() const
1600
return static_cast<int>(_lValueList.size());
1603
void PropertyLinkSubList::setValue(DocumentObject* lValue,const char* SubName)
1605
auto parent = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
1606
verifyObject(lValue, parent);
1609
//maintain backlinks
1611
// before accessing internals make sure the object is not about to be destroyed
1612
// otherwise the backlink contains dangling pointers
1613
if (!parent->testStatus(ObjectStatus::Destroy) && _pcScope!=LinkScope::Hidden) {
1614
for(auto *obj : _lValueList) {
1616
obj->_removeBackLink(parent);
1619
lValue->_addBackLink(parent);
1626
_lValueList.resize(1);
1627
_lValueList[0]=lValue;
1628
_lSubList.resize(1);
1629
_lSubList[0]=SubName;
1633
_lValueList.clear();
1636
updateElementReference(nullptr);
1637
checkLabelReferences(_lSubList);
1641
void PropertyLinkSubList::setValues(const std::vector<DocumentObject*>& lValue,const std::vector<const char*>& lSubNames)
1643
auto parent = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
1644
for(auto obj : lValue) {
1645
verifyObject(obj, parent);
1648
if (lValue.size() != lSubNames.size())
1649
throw Base::ValueError("PropertyLinkSubList::setValues: size of subelements list != size of objects list");
1652
//maintain backlinks.
1654
// before accessing internals make sure the object is not about to be destroyed
1655
// otherwise the backlink contains dangling pointers
1656
if (!parent->testStatus(ObjectStatus::Destroy) && _pcScope!=LinkScope::Hidden) {
1657
//_lValueList can contain items multiple times, but we trust the document
1658
//object to ensure that this works
1659
for(auto *obj : _lValueList) {
1661
obj->_removeBackLink(parent);
1664
//maintain backlinks. lValue can contain items multiple times, but we trust the document
1665
//object to ensure that the backlink is only added once
1666
for(auto *obj : lValue) {
1668
obj->_addBackLink(parent);
1675
_lValueList = lValue;
1676
_lSubList.resize(lSubNames.size());
1678
for (std::vector<const char*>::const_iterator it = lSubNames.begin();it!=lSubNames.end();++it,++i) {
1682
updateElementReference(nullptr);
1683
checkLabelReferences(_lSubList);
1687
void PropertyLinkSubList::setValues(const std::vector<DocumentObject*>& lValue,
1688
const std::vector<std::string>& lSubNames, std::vector<ShadowSub> &&ShadowSubList)
1690
setValues(std::vector<DocumentObject*>(lValue),
1691
std::vector<std::string>(lSubNames),std::move(ShadowSubList));
1694
void PropertyLinkSubList::setValues(std::vector<DocumentObject*>&& lValue,
1695
std::vector<std::string>&& lSubNames, std::vector<ShadowSub> &&ShadowSubList)
1697
auto parent = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
1698
for(auto obj : lValue) {
1699
verifyObject(obj, parent);
1701
if (lValue.size() != lSubNames.size())
1702
throw Base::ValueError("PropertyLinkSubList::setValues: size of subelements list != size of objects list");
1705
//maintain backlinks.
1707
// before accessing internals make sure the object is not about to be destroyed
1708
// otherwise the backlink contains dangling pointers
1709
if (!parent->testStatus(ObjectStatus::Destroy) && _pcScope!=LinkScope::Hidden) {
1710
//_lValueList can contain items multiple times, but we trust the document
1711
//object to ensure that this works
1712
for(auto *obj : _lValueList) {
1714
obj->_removeBackLink(parent);
1717
//maintain backlinks. lValue can contain items multiple times, but we trust the document
1718
//object to ensure that the backlink is only added once
1719
for(auto *obj : lValue) {
1721
obj->_addBackLink(parent);
1728
_lValueList = std::move(lValue);
1729
_lSubList = std::move(lSubNames);
1730
if(ShadowSubList.size()==_lSubList.size())
1731
_ShadowSubList = std::move(ShadowSubList);
1733
updateElementReference(nullptr);
1734
checkLabelReferences(_lSubList);
1738
void PropertyLinkSubList::setValue(DocumentObject* lValue, const std::vector<std::string> &SubList)
1740
auto parent = dynamic_cast<App::DocumentObject*>(getContainer());
1741
verifyObject(lValue, parent);
1744
//maintain backlinks.
1746
// before accessing internals make sure the object is not about to be destroyed
1747
// otherwise the backlink contains dangling pointers
1748
if (!parent->testStatus(ObjectStatus::Destroy) && _pcScope!=LinkScope::Hidden) {
1749
//_lValueList can contain items multiple times, but we trust the document
1750
//object to ensure that this works
1751
for(auto *obj : _lValueList) {
1753
obj->_removeBackLink(parent);
1756
//maintain backlinks. lValue can contain items multiple times, but we trust the document
1757
//object to ensure that the backlink is only added once
1759
lValue->_addBackLink(parent);
1765
std::size_t size = SubList.size();
1766
this->_lValueList.clear();
1767
this->_lSubList.clear();
1770
this->_lValueList.push_back(lValue);
1771
this->_lSubList.emplace_back();
1775
this->_lSubList = SubList;
1776
this->_lValueList.insert(this->_lValueList.begin(), size, lValue);
1778
updateElementReference(nullptr);
1779
checkLabelReferences(_lSubList);
1783
void PropertyLinkSubList::addValue(App::DocumentObject *obj, const std::vector<std::string> &subs, bool reset)
1785
auto parent = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
1786
verifyObject(obj, parent);
1789
//maintain backlinks.
1791
// before accessing internals make sure the object is not about to be destroyed
1792
// otherwise the backlink contains dangling pointers
1793
if (!parent->testStatus(ObjectStatus::Destroy) && _pcScope != LinkScope::Hidden) {
1794
//_lValueList can contain items multiple times, but we trust the document
1795
//object to ensure that this works
1797
for(auto* value : _lValueList) {
1798
if (value && value == obj)
1799
value->_removeBackLink(parent);
1803
//maintain backlinks. lValue can contain items multiple times, but we trust the document
1804
//object to ensure that the backlink is only added once
1806
obj->_addBackLink(parent);
1811
std::vector<DocumentObject*> valueList;
1812
std::vector<std::string> subList;
1815
for (std::size_t i=0; i<_lValueList.size(); i++) {
1816
if (_lValueList[i] != obj) {
1817
valueList.push_back(_lValueList[i]);
1818
subList.push_back(_lSubList[i]);
1823
valueList = _lValueList;
1824
subList = _lSubList;
1827
std::size_t size = subs.size();
1830
valueList.push_back(obj);
1831
subList.emplace_back();
1835
subList.insert(subList.end(), subs.begin(), subs.end());
1836
valueList.insert(valueList.end(), size, obj);
1840
_lValueList = valueList;
1841
_lSubList = subList;
1842
updateElementReference(nullptr);
1843
checkLabelReferences(_lSubList);
1847
const string PropertyLinkSubList::getPyReprString() const
1849
assert(this->_lValueList.size() == this->_lSubList.size());
1851
if (this->_lValueList.empty())
1852
return std::string("None");
1854
std::stringstream strm;
1856
for (std::size_t i = 0; i < this->_lSubList.size(); i++) {
1861
App::DocumentObject* obj = this->_lValueList[i];
1863
strm << "App.getDocument('" << obj->getDocument()->getName()
1864
<< "').getObject('" << obj->getNameInDocument() << "')";
1869
strm << "'" << this->_lSubList[i] << "'";
1876
DocumentObject *PropertyLinkSubList::getValue() const
1878
App::DocumentObject* ret = nullptr;
1879
//FIXME: cache this to avoid iterating each time, to improve speed
1880
for (auto i : this->_lValueList) {
1889
int PropertyLinkSubList::removeValue(App::DocumentObject *lValue)
1891
assert(this->_lValueList.size() == this->_lSubList.size());
1893
std::size_t num = std::count(this->_lValueList.begin(), this->_lValueList.end(), lValue);
1897
std::vector<DocumentObject*> links;
1898
std::vector<std::string> subs;
1899
links.reserve(this->_lValueList.size() - num);
1900
subs.reserve(this->_lSubList.size() - num);
1902
for (std::size_t i=0; i<this->_lValueList.size(); ++i) {
1903
if (this->_lValueList[i] != lValue) {
1904
links.push_back(this->_lValueList[i]);
1905
subs.push_back(this->_lSubList[i]);
1909
setValues(links, subs);
1910
return static_cast<int>(num);
1913
void PropertyLinkSubList::setSubListValues(const std::vector<PropertyLinkSubList::SubSet>& values)
1915
std::vector<DocumentObject*> links;
1916
std::vector<std::string> subs;
1918
for (const auto & value : values) {
1919
for (const auto& jt : value.second) {
1920
links.push_back(value.first);
1925
setValues(links, subs);
1928
std::vector<PropertyLinkSubList::SubSet> PropertyLinkSubList::getSubListValues(bool newStyle) const
1930
std::vector<PropertyLinkSubList::SubSet> values;
1931
if (_lValueList.size() != _lSubList.size())
1932
throw Base::ValueError("PropertyLinkSubList::getSubListValues: size of subelements list != size of objects list");
1934
assert(_ShadowSubList.size() == _lSubList.size());
1936
for (std::size_t i = 0; i < _lValueList.size(); i++) {
1937
App::DocumentObject* link = _lValueList[i];
1939
if(newStyle && !_ShadowSubList[i].first.empty())
1940
sub = _ShadowSubList[i].first;
1941
else if(!newStyle && !_ShadowSubList[i].second.empty())
1942
sub = _ShadowSubList[i].second;
1945
if (values.empty() || values.back().first != link){
1946
//new object started, start a new subset.
1947
values.emplace_back(link, std::vector<std::string>());
1949
values.back().second.push_back(sub);
1954
PyObject *PropertyLinkSubList::getPyObject()
1956
std::vector<SubSet> subLists = getSubListValues();
1957
std::size_t count = subLists.size();
1958
#if 0//FIXME: Should switch to tuple
1959
Py::Tuple sequence(count);
1961
Py::List sequence(count);
1963
for (std::size_t i = 0; i<count; i++) {
1965
tup[0] = Py::asObject(subLists[i].first->getPyObject());
1967
const std::vector<std::string>& sub = subLists[i].second;
1968
Py::Tuple items(sub.size());
1969
for (std::size_t j = 0; j < sub.size(); j++) {
1970
items[j] = Py::String(sub[j]);
1977
return Py::new_reference_to(sequence);
1980
void PropertyLinkSubList::setPyObject(PyObject *value)
1982
try { //try PropertyLinkSub syntax
1983
PropertyLinkSub dummy;
1984
dummy.setPyObject(value);
1985
this->setValue(dummy.getValue(), dummy.getSubValues());
1990
// try PropertyLinkList syntax
1991
PropertyLinkList dummy;
1992
dummy.setPyObject(value);
1993
const auto &values = dummy.getValues();
1994
std::vector<std::string> subs(values.size());
1995
this->setValues(values,subs);
1999
static const char *errMsg =
2000
"Expects sequence of items of type DocObj, (DocObj,SubName), or (DocObj, (SubName,...))";
2002
if (!PyTuple_Check(value) && !PyList_Check(value))
2003
throw Base::TypeError(errMsg);
2005
Py::Sequence list(value);
2006
Py::Sequence::size_type size = list.size();
2008
std::vector<DocumentObject*> values;
2009
values.reserve(size);
2010
std::vector<std::string> SubNames;
2011
SubNames.reserve(size);
2012
for (Py::Sequence::size_type i=0; i<size; i++) {
2013
Py::Object item = list[i];
2014
if ((item.isTuple() || item.isSequence()) && PySequence_Size(*item)==2) {
2015
Py::Sequence seq(item);
2016
if (PyObject_TypeCheck(seq[0].ptr(), &(DocumentObjectPy::Type))){
2017
auto obj = static_cast<DocumentObjectPy*>(seq[0].ptr())->getDocumentObjectPtr();
2018
PropertyString propString;
2019
if (seq[1].isString()) {
2020
values.push_back(obj);
2021
propString.setPyObject(seq[1].ptr());
2022
SubNames.emplace_back(propString.getValue());
2023
} else if (seq[1].isSequence()) {
2024
Py::Sequence list(seq[1]);
2025
for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) {
2026
if(!(*it).isString())
2027
throw Base::TypeError(errMsg);
2028
values.push_back(obj);
2029
propString.setPyObject((*it).ptr());
2030
SubNames.emplace_back(propString.getValue());
2033
throw Base::TypeError(errMsg);
2035
} else if (PyObject_TypeCheck(*item, &(DocumentObjectPy::Type))) {
2036
DocumentObjectPy *pcObj;
2037
pcObj = static_cast<DocumentObjectPy*>(*item);
2038
values.push_back(pcObj->getDocumentObjectPtr());
2039
SubNames.emplace_back();
2041
throw Base::TypeError(errMsg);
2043
setValues(values,SubNames);
2046
void PropertyLinkSubList::afterRestore() {
2047
assert(_lSubList.size() == _ShadowSubList.size());
2048
if(!testFlag(LinkRestoreLabel))
2050
setFlag(LinkRestoreLabel,false);
2051
for(size_t i=0;i<_lSubList.size();++i)
2052
restoreLabelReference(_lValueList[i],_lSubList[i],&_ShadowSubList[i]);
2055
void PropertyLinkSubList::onContainerRestored() {
2056
unregisterElementReference();
2057
for(size_t i=0;i<_lSubList.size();++i)
2058
_registerElementReference(_lValueList[i],_lSubList[i],_ShadowSubList[i]);
2061
void PropertyLinkSubList::updateElementReference(DocumentObject *feature, bool reverse, bool notify) {
2063
_ShadowSubList.clear();
2064
unregisterElementReference();
2066
_ShadowSubList.resize(_lSubList.size());
2067
auto owner = freecad_dynamic_cast<DocumentObject>(getContainer());
2068
if(owner && owner->isRestoring())
2071
bool touched = false;
2072
for(auto &sub : _lSubList) {
2073
auto obj = _lValueList[i];
2074
if(_updateElementReference(feature,obj,sub,_ShadowSubList[i++],reverse,notify&&!touched))
2080
std::vector<int> mapped;
2081
mapped.reserve(_mapped.size());
2082
for(int idx : _mapped) {
2083
if(idx<(int)_lSubList.size()) {
2084
if(!_ShadowSubList[idx].first.empty())
2085
_lSubList[idx] = _ShadowSubList[idx].first;
2087
mapped.push_back(idx);
2090
_mapped.swap(mapped);
2091
if(owner && feature)
2092
owner->onUpdateElementReference(this);
2097
bool PropertyLinkSubList::referenceChanged() const{
2098
return !_mapped.empty();
2101
void PropertyLinkSubList::Save (Base::Writer &writer) const
2103
assert(_lSubList.size() == _ShadowSubList.size());
2106
for(auto obj : _lValueList) {
2107
if(obj && obj->isAttachedToDocument())
2110
writer.Stream() << writer.ind() << "<LinkSubList count=\"" << count <<"\">" << endl;
2112
auto owner = dynamic_cast<DocumentObject*>(getContainer());
2113
bool exporting = owner && owner->isExporting();
2114
for (int i = 0; i < getSize(); i++) {
2115
auto obj = _lValueList[i];
2116
if(!obj || !obj->isAttachedToDocument())
2118
const auto &shadow = _ShadowSubList[i];
2119
// shadow.second stores the old style element name. For backward
2120
// compatibility reason, we shall store the old name into attribute
2121
// 'value' whenever possible.
2122
const auto &sub = shadow.second.empty()?_lSubList[i]:shadow.second;
2124
writer.Stream() << writer.ind() << "<Link obj=\"" << obj->getExportName() << "\" sub=\"";
2126
std::string exportName;
2127
writer.Stream() << encodeAttribute(exportSubName(exportName,obj,sub.c_str()));
2128
if(!shadow.second.empty() && _lSubList[i]==shadow.first)
2129
writer.Stream() << "\" " ATTR_MAPPED "=\"1";
2131
writer.Stream() << encodeAttribute(sub);
2132
if(!_lSubList[i].empty()) {
2133
if(sub!=_lSubList[i]) {
2134
// Stores the actual value that is shadowed. For new version FC,
2135
// we will restore this shadowed value instead.
2136
writer.Stream() << "\" " ATTR_SHADOWED "=\"" << encodeAttribute(_lSubList[i]);
2137
}else if(!shadow.first.empty()) {
2138
// Here means the user set value is old style element name.
2139
// We shall then store the shadow somewhere else.
2140
writer.Stream() << "\" " ATTR_SHADOW "=\"" << encodeAttribute(shadow.first);
2144
writer.Stream() << "\"/>" << endl;
2148
writer.Stream() << writer.ind() << "</LinkSubList>" << endl ;
2151
void PropertyLinkSubList::Restore(Base::XMLReader &reader)
2154
reader.readElement("LinkSubList");
2155
// get the value of my attribute
2156
int count = reader.getAttributeAsInteger("count");
2158
std::vector<DocumentObject*> values;
2159
values.reserve(count);
2160
std::vector<std::string> SubNames;
2161
SubNames.reserve(count);
2162
std::vector<ShadowSub> shadows;
2163
shadows.reserve(count);
2164
DocumentObject* father = dynamic_cast<DocumentObject*>(getContainer());
2165
App::Document* document = father ? father->getDocument() : nullptr;
2166
std::vector<int> mapped;
2167
bool restoreLabel=false;
2168
for (int i = 0; i < count; i++) {
2169
reader.readElement("Link");
2170
std::string name = reader.getName(reader.getAttribute("obj"));
2171
// In order to do copy/paste it must be allowed to have defined some
2172
// referenced objects in XML which do not exist anymore in the new
2173
// document. Thus, we should silently ignore this.
2174
// Property not in an object!
2175
DocumentObject* child = document ? document->getObject(name.c_str()) : nullptr;
2177
values.push_back(child);
2178
shadows.emplace_back();
2179
auto &shadow = shadows.back();
2180
shadow.second = importSubName(reader,reader.getAttribute("sub"),restoreLabel);
2181
if(reader.hasAttribute(ATTR_SHADOWED) && !IGNORE_SHADOW) {
2182
shadow.first = importSubName(reader,reader.getAttribute(ATTR_SHADOWED),restoreLabel);
2183
SubNames.push_back(shadow.first);
2185
SubNames.push_back(shadow.second);
2186
if(reader.hasAttribute(ATTR_SHADOW) && !IGNORE_SHADOW)
2187
shadow.first = importSubName(reader,reader.getAttribute(ATTR_SHADOW),restoreLabel);
2189
if(reader.hasAttribute(ATTR_MAPPED))
2190
mapped.push_back(i);
2191
} else if (reader.isVerbose())
2192
Base::Console().Warning("Lost link to '%s' while loading, maybe "
2193
"an object was not loaded correctly\n",name.c_str());
2195
setFlag(LinkRestoreLabel,restoreLabel);
2197
reader.readEndElement("LinkSubList");
2200
setValues(values,SubNames,std::move(shadows));
2201
_mapped.swap(mapped);
2204
bool PropertyLinkSubList::upgrade(Base::XMLReader &reader, const char *typeName)
2206
Base::Type type = Base::Type::fromName(typeName);
2207
if (type.isDerivedFrom(PropertyLink::getClassTypeId())) {
2209
prop.setContainer(getContainer());
2210
prop.Restore(reader);
2211
setValue(prop.getValue());
2214
else if (type.isDerivedFrom(PropertyLinkList::getClassTypeId())) {
2215
PropertyLinkList prop;
2216
prop.setContainer(getContainer());
2217
prop.Restore(reader);
2218
std::vector<std::string> subnames;
2219
subnames.resize(prop.getSize());
2220
setValues(prop.getValues(), subnames);
2223
else if (type.isDerivedFrom(PropertyLinkSub::getClassTypeId())) {
2224
PropertyLinkSub prop;
2225
prop.setContainer(getContainer());
2226
prop.Restore(reader);
2227
setValue(prop.getValue(), prop.getSubValues());
2234
Property *PropertyLinkSubList::CopyOnImportExternal(
2235
const std::map<std::string,std::string> &nameMap) const
2237
auto owner = dynamic_cast<const DocumentObject*>(getContainer());
2238
if(!owner || !owner->getDocument() || _lValueList.size()!=_lSubList.size())
2240
std::vector<App::DocumentObject *> values;
2241
std::vector<std::string> subs;
2242
auto itSub = _lSubList.begin();
2243
for(auto itValue=_lValueList.begin();itValue!=_lValueList.end();++itValue,++itSub) {
2244
auto value = *itValue;
2245
const auto &sub = *itSub;
2246
if(!value || !value->isAttachedToDocument()) {
2247
if(!values.empty()) {
2248
values.push_back(value);
2249
subs.push_back(sub);
2253
auto linked = tryImport(owner->getDocument(),value,nameMap);
2254
auto new_sub = tryImportSubName(value,sub.c_str(),owner->getDocument(),nameMap);
2255
if(linked!=value || !new_sub.empty()) {
2256
if(values.empty()) {
2257
values.reserve(_lValueList.size());
2258
values.insert(values.end(),_lValueList.begin(),itValue);
2259
subs.reserve(_lSubList.size());
2260
subs.insert(subs.end(),_lSubList.begin(),itSub);
2262
values.push_back(linked);
2263
subs.push_back(std::move(new_sub));
2264
}else if(!values.empty()) {
2265
values.push_back(linked);
2266
subs.push_back(sub);
2271
std::unique_ptr<PropertyLinkSubList> p(new PropertyLinkSubList);
2272
p->_lValueList = std::move(values);
2273
p->_lSubList = std::move(subs);
2277
Property *PropertyLinkSubList::CopyOnLabelChange(App::DocumentObject *obj,
2278
const std::string &ref, const char *newLabel) const
2280
auto owner = dynamic_cast<const DocumentObject*>(getContainer());
2281
if(!owner || !owner->getDocument())
2283
std::vector<App::DocumentObject *> values;
2284
std::vector<std::string> subs;
2285
auto itSub = _lSubList.begin();
2286
for(auto itValue=_lValueList.begin();itValue!=_lValueList.end();++itValue,++itSub) {
2287
auto value = *itValue;
2288
const auto &sub = *itSub;
2289
if(!value || !value->isAttachedToDocument()) {
2290
if(!values.empty()) {
2291
values.push_back(value);
2292
subs.push_back(sub);
2296
auto new_sub = updateLabelReference(value,sub.c_str(),obj,ref,newLabel);
2297
if(!new_sub.empty()) {
2298
if(values.empty()) {
2299
values.reserve(_lValueList.size());
2300
values.insert(values.end(),_lValueList.begin(),itValue);
2301
subs.reserve(_lSubList.size());
2302
subs.insert(subs.end(),_lSubList.begin(),itSub);
2304
values.push_back(value);
2305
subs.push_back(std::move(new_sub));
2306
}else if(!values.empty()) {
2307
values.push_back(value);
2308
subs.push_back(sub);
2313
std::unique_ptr<PropertyLinkSubList> p(new PropertyLinkSubList);
2314
p->_lValueList = std::move(values);
2315
p->_lSubList = std::move(subs);
2319
Property *PropertyLinkSubList::CopyOnLinkReplace(const App::DocumentObject *parent,
2320
App::DocumentObject *oldObj, App::DocumentObject *newObj) const
2322
std::vector<App::DocumentObject *> values;
2323
std::vector<std::string> subs;
2324
auto itSub = _lSubList.begin();
2325
std::vector<size_t> positions;
2326
for(auto itValue=_lValueList.begin();itValue!=_lValueList.end();++itValue,++itSub) {
2327
auto value = *itValue;
2328
const auto &sub = *itSub;
2329
if(!value || !value->isAttachedToDocument()) {
2330
if(!values.empty()) {
2331
values.push_back(value);
2332
subs.push_back(sub);
2336
auto res = tryReplaceLink(getContainer(),value,parent,oldObj,newObj,sub.c_str());
2338
if(values.empty()) {
2339
values.reserve(_lValueList.size());
2340
values.insert(values.end(),_lValueList.begin(),itValue);
2341
subs.reserve(_lSubList.size());
2342
subs.insert(subs.end(),_lSubList.begin(),itSub);
2344
if(res.first == newObj) {
2345
// check for duplication
2346
auto itS = subs.begin();
2347
for(auto itV=values.begin();itV!=values.end();) {
2348
if(*itV == res.first && *itS == res.second) {
2349
itV = values.erase(itV);
2350
itS = subs.erase(itS);
2356
positions.push_back(values.size());
2358
values.push_back(res.first);
2359
subs.push_back(std::move(res.second));
2360
}else if(!values.empty()) {
2361
bool duplicate = false;
2362
if(value == newObj) {
2363
for(auto pos : positions) {
2364
if(sub == subs[pos]) {
2371
values.push_back(value);
2372
subs.push_back(sub);
2378
std::unique_ptr<PropertyLinkSubList> p(new PropertyLinkSubList);
2379
p->_lValueList = std::move(values);
2380
p->_lSubList = std::move(subs);
2384
Property *PropertyLinkSubList::Copy() const
2386
PropertyLinkSubList *p = new PropertyLinkSubList();
2387
p->_lValueList = _lValueList;
2388
p->_lSubList = _lSubList;
2392
void PropertyLinkSubList::Paste(const Property &from)
2394
if(!from.isDerivedFrom(PropertyLinkSubList::getClassTypeId()))
2395
throw Base::TypeError("Incompatible property to paste to");
2396
auto &link = static_cast<const PropertyLinkSubList&>(from);
2397
setValues(link._lValueList, link._lSubList);
2400
unsigned int PropertyLinkSubList::getMemSize () const
2402
unsigned int size = static_cast<unsigned int>(_lValueList.size() * sizeof(App::DocumentObject *));
2403
for(int i = 0;i<getSize(); i++)
2404
size += _lSubList[i].size();
2408
std::vector<std::string> PropertyLinkSubList::getSubValues(bool newStyle) const {
2409
assert(_lSubList.size() == _ShadowSubList.size());
2410
std::vector<std::string> ret;
2411
ret.reserve(_ShadowSubList.size());
2412
for(size_t i=0;i<_ShadowSubList.size();++i)
2413
ret.push_back(getSubNameWithStyle(_lSubList[i],_ShadowSubList[i],newStyle));
2417
void PropertyLinkSubList::getLinks(std::vector<App::DocumentObject *> &objs,
2418
bool all, std::vector<std::string> *subs, bool newStyle) const
2420
if(all||_pcScope!=LinkScope::Hidden) {
2421
objs.reserve(objs.size()+_lValueList.size());
2422
for(auto obj : _lValueList) {
2423
if(obj && obj->isAttachedToDocument())
2424
objs.push_back(obj);
2427
auto _subs = getSubValues(newStyle);
2428
subs->reserve(subs->size()+_subs.size());
2429
std::move(_subs.begin(),_subs.end(),std::back_inserter(*subs));
2434
void PropertyLinkSubList::breakLink(App::DocumentObject *obj, bool clear) {
2435
std::vector<DocumentObject*> values;
2436
std::vector<std::string> subs;
2438
if(clear && getContainer()==obj) {
2439
setValues(values,subs);
2442
assert(_lValueList.size()==_lSubList.size());
2444
values.reserve(_lValueList.size());
2445
subs.reserve(_lSubList.size());
2448
for(auto o : _lValueList) {
2452
values.push_back(o);
2453
subs.push_back(_lSubList[i]);
2455
if(values.size()!=_lValueList.size())
2456
setValues(values,subs);
2459
bool PropertyLinkSubList::adjustLink(const std::set<App::DocumentObject*> &inList) {
2460
if (_pcScope==LinkScope::Hidden)
2462
auto subs = _lSubList;
2463
auto links = _lValueList;
2465
bool touched = false;
2466
for(std::string &sub : subs) {
2468
auto &link = links[idx];
2469
if(!link || !link->isAttachedToDocument() || !inList.count(link))
2472
size_t pos = sub.find('.');
2473
for(;pos!=std::string::npos;pos=sub.find('.',pos+1)) {
2474
auto sobj = link->getSubObject(sub.substr(0,pos+1).c_str());
2475
if(!sobj || sobj->getDocument()!=link->getDocument()) {
2476
pos = std::string::npos;
2479
if(!inList.count(sobj)) {
2481
sub = sub.substr(pos+1);
2485
if(pos == std::string::npos)
2489
setValues(links,subs);
2493
//**************************************************************************
2495
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2497
// Key on absolute path.
2498
// Because of possible symbolic links, multiple entry may refer to the same
2499
// file. We used to rely on QFileInfo::canonicalFilePath to resolve it, but
2500
// has now been changed to simply use the absoluteFilePath(), and rely on user
2501
// to be aware of possible duplicated file location. The reason being that
2502
// some user (especially Linux user) use symlink to organize file tree.
2503
using DocInfoMap = std::map<QString, DocInfoPtr>;
2504
DocInfoMap _DocInfoMap;
2507
public std::enable_shared_from_this<App::DocInfo>
2510
using Connection = boost::signals2::scoped_connection;
2511
Connection connFinishRestoreDocument;
2512
Connection connPendingReloadDocument;
2513
Connection connDeleteDocument;
2514
Connection connSaveDocument;
2515
Connection connDeletedObject;
2517
DocInfoMap::iterator myPos;
2519
App::Document *pcDoc{nullptr};
2520
std::set<PropertyXLink*> links;
2522
static std::string getDocPath(
2523
const char *filename, App::Document *pDoc, bool relative, QString *fullPath = nullptr)
2526
// The path could be an URI, in that case
2527
// TODO: build a far much more resilient approach to test for an URI
2528
QString path = QString::fromUtf8(filename);
2529
if (path.startsWith(QLatin1String("https://"))) {
2530
// We do have an URI
2533
return std::string(filename);
2536
// make sure the filename is absolute path
2537
path = QDir::cleanPath(path);
2538
if((absolute = QFileInfo(path).isAbsolute())) {
2542
return std::string(path.toUtf8().constData());
2545
const char *docPath = pDoc->getFileName();
2546
if(!docPath || *docPath==0)
2547
throw Base::RuntimeError("Owner document not saved");
2549
QDir docDir(QFileInfo(QString::fromUtf8(docPath)).absoluteDir());
2551
path = QDir::cleanPath(docDir.absoluteFilePath(path));
2557
return std::string(docDir.relativeFilePath(path).toUtf8().constData());
2559
return std::string(path.toUtf8().constData());
2562
static DocInfoPtr get(const char *filename,
2563
App::Document *pDoc,PropertyXLink *l, const char *objName)
2566
l->filePath = getDocPath(filename,pDoc,true,&path);
2568
FC_LOG("finding doc " << filename);
2570
auto it = _DocInfoMap.find(path);
2572
if(it != _DocInfoMap.end()) {
2575
QString fullpath(info->getFullPath());
2576
if(fullpath.size() &&
2577
App::GetApplication().addPendingDocument(
2578
fullpath.toUtf8().constData(),objName,
2579
l->testFlag(PropertyLinkBase::LinkAllowPartial))==0)
2581
for(App::Document *doc : App::GetApplication().getDocuments()) {
2582
if(getFullPath(doc->getFileName()) == fullpath) {
2590
info = std::make_shared<DocInfo>();
2591
auto ret = _DocInfoMap.insert(std::make_pair(path,info));
2592
info->init(ret.first,objName,l);
2596
// make sure to attach only external object
2597
auto owner = Base::freecad_dynamic_cast<DocumentObject>(l->getContainer());
2598
if(owner && owner->getDocument() == info->pcDoc)
2602
info->links.insert(l);
2606
static QString getFullPath(const char *p) {
2607
QString path = QString::fromUtf8(p);
2611
if (path.startsWith(QLatin1String("https://")))
2614
return QFileInfo(path).absoluteFilePath();
2618
QString getFullPath() const {
2619
QString path = myPos->first;
2620
if (path.startsWith(QLatin1String("https://")))
2623
return QFileInfo(myPos->first).absoluteFilePath();
2627
const char *filePath() const {
2628
return myPath.c_str();
2632
FC_LOG("deinit " << (pcDoc?pcDoc->getName():filePath()));
2633
assert(links.empty());
2634
connFinishRestoreDocument.disconnect();
2635
connPendingReloadDocument.disconnect();
2636
connDeleteDocument.disconnect();
2637
connSaveDocument.disconnect();
2638
connDeletedObject.disconnect();
2640
auto me = shared_from_this();
2641
_DocInfoMap.erase(myPos);
2642
myPos = _DocInfoMap.end();
2647
void init(DocInfoMap::iterator pos, const char *objName, PropertyXLink *l) {
2649
myPath = myPos->first.toUtf8().constData();
2650
App::Application &app = App::GetApplication();
2652
connFinishRestoreDocument = app.signalFinishRestoreDocument.connect(
2653
std::bind(&DocInfo::slotFinishRestoreDocument,this,sp::_1));
2654
connPendingReloadDocument = app.signalPendingReloadDocument.connect(
2655
std::bind(&DocInfo::slotFinishRestoreDocument,this,sp::_1));
2656
connDeleteDocument = app.signalDeleteDocument.connect(
2657
std::bind(&DocInfo::slotDeleteDocument,this,sp::_1));
2658
connSaveDocument = app.signalSaveDocument.connect(
2659
std::bind(&DocInfo::slotSaveDocument,this,sp::_1));
2662
QString fullpath(getFullPath());
2663
if(fullpath.isEmpty())
2664
FC_ERR("document not found " << filePath());
2666
for(App::Document *doc : App::GetApplication().getDocuments()) {
2667
if(getFullPath(doc->getFileName()) == fullpath) {
2668
if(doc->testStatus(App::Document::PartialDoc) && !doc->getObject(objName))
2674
FC_LOG("document pending " << filePath());
2675
app.addPendingDocument(fullpath.toUtf8().constData(),objName,
2676
l->testFlag(PropertyLinkBase::LinkAllowPartial));
2680
void attach(Document *doc) {
2683
FC_LOG("attaching " << doc->getName() << ", " << doc->getFileName());
2684
std::map<App::PropertyLinkBase*,std::vector<App::PropertyXLink*> > parentLinks;
2685
for(auto it=links.begin(),itNext=it;it!=links.end();it=itNext) {
2690
if(link->parentProp) {
2691
parentLinks[link->parentProp].push_back(link);
2694
auto obj = doc->getObject(link->objectName.c_str());
2696
link->restoreLink(obj);
2697
else if (doc->testStatus(App::Document::PartialDoc)) {
2698
App::GetApplication().addPendingDocument(
2699
doc->FileName.getValue(),
2700
link->objectName.c_str(),
2702
FC_WARN("reloading partial document '" << doc->FileName.getValue()
2703
<< "' due to object " << link->objectName);
2705
FC_WARN("object '" << link->objectName << "' not found in document '"
2706
<< doc->getName() << "'");
2708
for(auto &v : parentLinks) {
2709
v.first->setFlag(PropertyLinkBase::LinkRestoring);
2710
v.first->aboutToSetValue();
2711
for(auto link : v.second) {
2712
auto obj = doc->getObject(link->objectName.c_str());
2714
link->restoreLink(obj);
2715
else if (doc->testStatus(App::Document::PartialDoc)) {
2716
App::GetApplication().addPendingDocument(
2717
doc->FileName.getValue(),
2718
link->objectName.c_str(),
2720
FC_WARN("reloading partial document '" << doc->FileName.getValue()
2721
<< "' due to object " << link->objectName);
2723
FC_WARN("object '" << link->objectName << "' not found in document '"
2724
<< doc->getName() << "'");
2726
v.first->hasSetValue();
2727
v.first->setFlag(PropertyLinkBase::LinkRestoring,false);
2731
void remove(PropertyXLink *l) {
2732
auto it = links.find(l);
2733
if(it != links.end()) {
2740
static void restoreDocument(const App::Document &doc) {
2741
auto it = _DocInfoMap.find(getFullPath(doc.FileName.getValue()));
2742
if(it==_DocInfoMap.end())
2744
it->second->slotFinishRestoreDocument(doc);
2747
void slotFinishRestoreDocument(const App::Document &doc) {
2750
QString fullpath(getFullPath());
2751
if(!fullpath.isEmpty() && getFullPath(doc.getFileName())==fullpath)
2752
attach(const_cast<App::Document*>(&doc));
2755
void slotSaveDocument(const App::Document &doc) {
2757
slotFinishRestoreDocument(doc);
2763
QFileInfo info(myPos->first);
2764
QString path(info.absoluteFilePath());
2765
const char *filename = doc.getFileName();
2766
QString docPath(getFullPath(filename));
2768
if(path.isEmpty() || path!=docPath) {
2769
FC_LOG("document '" << doc.getName() << "' path changed");
2770
auto me = shared_from_this();
2771
auto ret = _DocInfoMap.insert(std::make_pair(docPath,me));
2773
// is that even possible?
2774
FC_WARN("document '" << doc.getName() << "' path exists, detach");
2775
slotDeleteDocument(doc);
2778
_DocInfoMap.erase(myPos);
2781
std::set<PropertyXLink *> tmp;
2783
for(auto link : tmp) {
2784
auto owner = static_cast<DocumentObject*>(link->getContainer());
2785
// adjust file path for each PropertyXLink
2786
DocInfo::get(filename,owner->getDocument(),link,link->objectName.c_str());
2790
// time stamp changed, touch the linking document.
2791
std::set<Document*> docs;
2792
for(auto link : links) {
2793
auto linkdoc = static_cast<DocumentObject*>(link->getContainer())->getDocument();
2794
auto ret = docs.insert(linkdoc);
2796
// This will signal the Gui::Document to call setModified();
2797
FC_LOG("touch document " << linkdoc->getName()
2798
<< " on time stamp change of " << link->getFullName());
2799
linkdoc->Comment.touch();
2804
void slotDeleteDocument(const App::Document &doc) {
2805
for(auto it=links.begin(),itNext=it;it!=links.end();it=itNext) {
2808
auto obj = dynamic_cast<DocumentObject*>(link->getContainer());
2809
if(obj && obj->getDocument() == &doc) {
2811
// must call unlink here, so that PropertyLink::resetLink can
2812
// remove back link before the owner object is marked as being
2823
std::map<App::PropertyLinkBase*,std::vector<App::PropertyXLink*> > parentLinks;
2824
for(auto link : links) {
2825
link->setFlag(PropertyLinkBase::LinkDetached);
2826
if(link->parentProp)
2827
parentLinks[link->parentProp].push_back(link);
2829
parentLinks[nullptr].push_back(link);
2831
for(auto &v : parentLinks) {
2833
v.first->setFlag(PropertyLinkBase::LinkDetached);
2834
v.first->aboutToSetValue();
2836
for(auto l : v.second)
2839
v.first->hasSetValue();
2840
v.first->setFlag(PropertyLinkBase::LinkDetached,false);
2846
bool hasXLink(const App::Document *doc) const{
2847
for(auto link : links) {
2848
auto obj = dynamic_cast<DocumentObject*>(link->getContainer());
2849
if(obj && obj->getDocument() == doc)
2855
static void breakLinks(App::DocumentObject *obj, bool clear) {
2856
auto doc = obj->getDocument();
2857
for(auto itD=_DocInfoMap.begin(),itDNext=itD;itD!=_DocInfoMap.end();itD=itDNext) {
2859
auto docInfo = itD->second;
2860
if(docInfo->pcDoc != doc)
2862
auto &links = docInfo->links;
2863
std::set<PropertyLinkBase*> parentLinks;
2864
for(auto it=links.begin(),itNext=it;it!=links.end();it=itNext) {
2867
if(link->_pcLink!=obj && !(clear && link->getContainer()==obj))
2869
if(link->parentProp)
2870
parentLinks.insert(link->parentProp);
2872
link->breakLink(obj,clear);
2874
for(auto link : parentLinks)
2875
link->breakLink(obj,clear);
2880
void PropertyLinkBase::breakLinks(App::DocumentObject *link,
2881
const std::vector<App::DocumentObject*> &objs, bool clear)
2883
std::vector<Property*> props;
2884
for(auto obj : objs) {
2886
obj->getPropertyList(props);
2887
for(auto prop : props) {
2888
auto linkProp = dynamic_cast<PropertyLinkBase*>(prop);
2890
linkProp->breakLink(link,clear);
2893
DocInfo::breakLinks(link,clear);
2896
//**************************************************************************
2898
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2900
TYPESYSTEM_SOURCE(App::PropertyXLink , App::PropertyLink)
2902
PropertyXLink::PropertyXLink(bool _allowPartial, PropertyLinkBase *parent)
2905
setAllowPartial(_allowPartial);
2906
setAllowExternal(true);
2907
setSyncSubObject(true);
2909
setContainer(parent->getContainer());
2912
PropertyXLink::~PropertyXLink() {
2916
void PropertyXLink::setSyncSubObject(bool enable)
2918
_Flags.set((std::size_t)LinkSyncSubObject, enable);
2921
void PropertyXLink::unlink() {
2923
docInfo->remove(this);
2930
void PropertyXLink::detach() {
2931
if(docInfo && _pcLink) {
2934
updateElementReference(nullptr);
2939
void PropertyXLink::aboutToSetValue() {
2941
parentProp->aboutToSetChildValue(*this);
2943
PropertyLinkBase::aboutToSetValue();
2946
void PropertyXLink::hasSetValue() {
2948
parentProp->hasSetChildValue(*this);
2950
PropertyLinkBase::hasSetValue();
2953
void PropertyXLink::setSubName(const char *subname)
2955
std::vector<std::string> subs;
2956
if(subname && subname[0])
2957
subs.emplace_back(subname);
2959
setSubValues(std::move(subs));
2963
void PropertyXLink::setSubValues(std::vector<std::string> &&subs,
2964
std::vector<ShadowSub> &&shadows)
2966
_SubList = std::move(subs);
2967
_ShadowSubList.clear();
2968
if(shadows.size() == _SubList.size())
2969
_ShadowSubList = std::move(shadows);
2971
updateElementReference(nullptr);
2972
checkLabelReferences(_SubList);
2975
void PropertyXLink::setValue(App::DocumentObject * lValue) {
2976
setValue(lValue,nullptr);
2979
void PropertyXLink::setValue(App::DocumentObject * lValue, const char *subname)
2981
std::vector<std::string> subs;
2982
if(subname && subname[0])
2983
subs.emplace_back(subname);
2984
setValue(lValue,std::move(subs));
2987
void PropertyXLink::restoreLink(App::DocumentObject *lValue) {
2988
assert(!_pcLink && lValue && docInfo);
2990
auto owner = dynamic_cast<DocumentObject*>(getContainer());
2991
if(!owner || !owner->isAttachedToDocument())
2992
throw Base::RuntimeError("invalid container");
2994
bool touched = owner->isTouched();
2995
setFlag(LinkDetached,false);
2996
setFlag(LinkRestoring);
2999
if (!owner->testStatus(ObjectStatus::Destroy) && _pcScope!=LinkScope::Hidden)
3000
lValue->_addBackLink(owner);
3003
updateElementReference(nullptr);
3005
setFlag(LinkRestoring,false);
3008
owner->isTouched() &&
3011
stamp==docInfo->pcDoc->LastModifiedDate.getValue())
3013
owner->purgeTouched();
3017
void PropertyXLink::setValue(App::DocumentObject *lValue,
3018
std::vector<std::string> &&subs, std::vector<ShadowSub> &&shadows)
3020
if(_pcLink==lValue && _SubList==subs)
3023
if(lValue && (!lValue->isAttachedToDocument() || !lValue->getDocument())) {
3024
throw Base::ValueError("Invalid object");
3028
auto owner = dynamic_cast<DocumentObject*>(getContainer());
3029
if(!owner || !owner->isAttachedToDocument())
3030
throw Base::RuntimeError("invalid container");
3033
throw Base::ValueError("self linking");
3038
const char *name = "";
3040
name = lValue->getNameInDocument();
3041
if(lValue->getDocument() != owner->getDocument()) {
3042
if(!docInfo || lValue->getDocument()!=docInfo->pcDoc)
3044
const char *filename = lValue->getDocument()->getFileName();
3045
if(!filename || *filename==0)
3046
throw Base::RuntimeError("Linked document not saved");
3047
FC_LOG("xlink set to new document " << lValue->getDocument()->getName());
3048
info = DocInfo::get(filename,owner->getDocument(),this,name);
3049
assert(info && info->pcDoc == lValue->getDocument());
3055
setFlag(LinkDetached,false);
3057
if (!owner->testStatus(ObjectStatus::Destroy) && _pcScope!=LinkScope::Hidden) {
3059
_pcLink->_removeBackLink(owner);
3061
lValue->_addBackLink(owner);
3071
if(docInfo && docInfo->pcDoc)
3072
stamp=docInfo->pcDoc->LastModifiedDate.getValue();
3074
setSubValues(std::move(subs),std::move(shadows));
3078
void PropertyXLink::setValue(std::string &&filename, std::string &&name,
3079
std::vector<std::string> &&subs, std::vector<ShadowSub> &&shadows)
3082
setValue(nullptr,std::move(subs),std::move(shadows));
3085
auto owner = dynamic_cast<DocumentObject*>(getContainer());
3086
if(!owner || !owner->isAttachedToDocument())
3087
throw Base::RuntimeError("invalid container");
3089
DocumentObject *pObject=nullptr;
3091
if(!filename.empty()) {
3092
owner->getDocument()->signalLinkXsetValue(filename);
3093
info = DocInfo::get(filename.c_str(),owner->getDocument(),this,name.c_str());
3095
pObject = info->pcDoc->getObject(name.c_str());
3097
pObject = owner->getDocument()->getObject(name.c_str());
3100
setValue(pObject,std::move(subs),std::move(shadows));
3103
setFlag(LinkDetached,false);
3106
if (_pcLink && !owner->testStatus(ObjectStatus::Destroy) && _pcScope!=LinkScope::Hidden)
3107
_pcLink->_removeBackLink(owner);
3116
if(docInfo && docInfo->pcDoc)
3117
stamp=docInfo->pcDoc->LastModifiedDate.getValue();
3118
objectName = std::move(name);
3119
setSubValues(std::move(subs),std::move(shadows));
3123
void PropertyXLink::setValue(App::DocumentObject *link,
3124
const std::vector<std::string> &subs, std::vector<ShadowSub> &&shadows)
3126
setValue(link,std::vector<std::string>(subs),std::move(shadows));
3129
App::Document *PropertyXLink::getDocument() const {
3130
return docInfo?docInfo->pcDoc:nullptr;
3133
const char *PropertyXLink::getDocumentPath() const {
3134
return docInfo?docInfo->filePath():filePath.c_str();
3137
const char *PropertyXLink::getObjectName() const {
3138
return objectName.c_str();
3141
bool PropertyXLink::upgrade(Base::XMLReader &reader, const char *typeName) {
3142
if(strcmp(typeName,App::PropertyLinkGlobal::getClassTypeId().getName())==0 ||
3143
strcmp(typeName,App::PropertyLink::getClassTypeId().getName())==0 ||
3144
strcmp(typeName,App::PropertyLinkChild::getClassTypeId().getName())==0)
3146
PropertyLink::Restore(reader);
3149
FC_ERR("Cannot upgrade from " << typeName);
3153
int PropertyXLink::checkRestore(std::string *msg) const {
3155
if(!_pcLink && !objectName.empty()) {
3156
// this condition means linked object not found
3158
std::ostringstream ss;
3159
ss << "Link not restored" << std::endl;
3160
ss << "Object: " << objectName;
3161
if(!filePath.empty())
3162
ss << std::endl << "File: " << filePath;
3170
if(testFlag(LinkAllowPartial) &&
3172
docInfo->pcDoc->testStatus(App::Document::PartialDoc)))
3177
std::ostringstream ss;
3178
ss << "Link not restored" << std::endl;
3179
ss << "Linked object: " << objectName;
3181
ss << std::endl << "Linked document: " << docInfo->pcDoc->Label.getValue();
3182
else if(!filePath.empty())
3183
ss << std::endl << "Linked file: " << filePath;
3188
if(!docInfo->pcDoc || stamp==docInfo->pcDoc->LastModifiedDate.getValue())
3192
std::ostringstream ss;
3193
ss << "Time stamp changed on link "
3194
<< _pcLink->getFullName();
3200
void PropertyXLink::afterRestore() {
3201
assert(_SubList.size() == _ShadowSubList.size());
3202
if(!testFlag(LinkRestoreLabel) || !_pcLink || !_pcLink->isAttachedToDocument())
3204
setFlag(LinkRestoreLabel,false);
3205
for(size_t i=0;i<_SubList.size();++i)
3206
restoreLabelReference(_pcLink,_SubList[i],&_ShadowSubList[i]);
3209
void PropertyXLink::onContainerRestored() {
3210
if(!_pcLink || !_pcLink->isAttachedToDocument())
3212
for(size_t i=0;i<_SubList.size();++i)
3213
_registerElementReference(_pcLink,_SubList[i],_ShadowSubList[i]);
3216
void PropertyXLink::updateElementReference(DocumentObject *feature,bool reverse,bool notify) {
3217
if(!updateLinkReference(this,feature,reverse,notify,_pcLink,_SubList,_mapped,_ShadowSubList))
3223
bool PropertyXLink::referenceChanged() const{
3224
return !_mapped.empty();
3227
void PropertyXLink::Save (Base::Writer &writer) const {
3228
auto owner = dynamic_cast<const DocumentObject *>(getContainer());
3229
if(!owner || !owner->getDocument())
3232
assert(_SubList.size() == _ShadowSubList.size());
3234
auto exporting = owner->isExporting();
3235
if(_pcLink && exporting && _pcLink->isExporting()) {
3236
// this means, we are exporting the owner and the linked object together.
3237
// Lets save the export name
3238
writer.Stream() << writer.ind() << "<XLink name=\"" << _pcLink->getExportName();
3240
const char *path = filePath.c_str();
3243
// Here means we are exporting the owner but not exporting the
3244
// linked object. Try to use absolute file path for easy transition
3245
// into document at different directory
3247
_path = docInfo->filePath();
3249
auto pDoc = owner->getDocument();
3250
const char *docPath = pDoc->getFileName();
3251
if(docPath && docPath[0]) {
3252
if(!filePath.empty())
3253
_path = DocInfo::getDocPath(filePath.c_str(),pDoc,false);
3257
FC_WARN("PropertyXLink export without saving the document");
3260
path = _path.c_str();
3262
writer.Stream() << writer.ind()
3263
<< "<XLink file=\"" << encodeAttribute(path)
3264
<< "\" stamp=\"" << (docInfo&&docInfo->pcDoc?docInfo->pcDoc->LastModifiedDate.getValue():"")
3265
<< "\" name=\"" << objectName;
3268
if(testFlag(LinkAllowPartial))
3269
writer.Stream() << "\" partial=\"1";
3271
if(_SubList.empty()) {
3272
writer.Stream() << "\"/>" << std::endl;
3273
} else if(_SubList.size() == 1) {
3274
const auto &subName = _SubList[0];
3275
const auto &shadowSub = _ShadowSubList[0];
3276
const auto &sub = shadowSub.second.empty()?subName:shadowSub.second;
3278
std::string exportName;
3279
writer.Stream() << "\" sub=\"" <<
3280
encodeAttribute(exportSubName(exportName,_pcLink,sub.c_str()));
3281
if(!shadowSub.second.empty() && shadowSub.first==subName)
3282
writer.Stream() << "\" " ATTR_MAPPED "=\"1";
3284
writer.Stream() << "\" sub=\"" << encodeAttribute(sub);
3287
writer.Stream() << "\" " ATTR_SHADOWED "=\"" << encodeAttribute(subName);
3288
else if(!shadowSub.first.empty())
3289
writer.Stream() << "\" " ATTR_SHADOW "=\"" << encodeAttribute(shadowSub.first);
3292
writer.Stream() << "\"/>" << std::endl;
3294
writer.Stream() <<"\" count=\"" << _SubList.size() << "\">" << std::endl;
3296
for(unsigned int i = 0;i<_SubList.size(); i++) {
3297
const auto &shadow = _ShadowSubList[i];
3298
// shadow.second stores the old style element name. For backward
3299
// compatibility reason, we shall store the old name into attribute
3300
// 'value' whenever possible.
3301
const auto &sub = shadow.second.empty()?_SubList[i]:shadow.second;
3302
writer.Stream() << writer.ind() << "<Sub value=\"";
3304
std::string exportName;
3305
writer.Stream() << encodeAttribute(exportSubName(exportName,_pcLink,sub.c_str()));
3306
if(!shadow.second.empty() && shadow.first == _SubList[i])
3307
writer.Stream() << "\" " ATTR_MAPPED "=\"1";
3309
writer.Stream() << encodeAttribute(sub);
3310
if(!_SubList[i].empty()) {
3311
if(sub!=_SubList[i])
3312
writer.Stream() << "\" " ATTR_SHADOWED "=\"" << encodeAttribute(_SubList[i]);
3313
else if(!shadow.first.empty())
3314
writer.Stream() << "\" " ATTR_SHADOW "=\"" << encodeAttribute(shadow.first);
3317
writer.Stream()<<"\"/>" << endl;
3320
writer.Stream() << writer.ind() << "</XLink>" << endl ;
3324
void PropertyXLink::Restore(Base::XMLReader &reader)
3327
reader.readElement("XLink");
3328
std::string stampAttr,file;
3329
if(reader.hasAttribute("stamp"))
3330
stampAttr = reader.getAttribute("stamp");
3331
if(reader.hasAttribute("file"))
3332
file = reader.getAttribute("file");
3333
setFlag(LinkAllowPartial,
3334
reader.hasAttribute("partial") &&
3335
reader.getAttributeAsInteger("partial"));
3338
name = reader.getName(reader.getAttribute("name"));
3340
name = reader.getAttribute("name");
3342
assert(getContainer()->isDerivedFrom<App::DocumentObject>());
3343
DocumentObject *object = nullptr;
3344
if(!name.empty() && file.empty()) {
3345
DocumentObject* parent = static_cast<DocumentObject*>(getContainer());
3346
Document *document = parent->getDocument();
3347
object = document ? document->getObject(name.c_str()) : nullptr;
3349
if(reader.isVerbose()) {
3350
FC_WARN("Lost link to '" << name << "' while loading, maybe "
3351
"an object was not loaded correctly");
3356
std::vector<std::string> subs;
3357
std::vector<ShadowSub> shadows;
3358
std::vector<int> mapped;
3359
bool restoreLabel = false;
3360
if(reader.hasAttribute("sub")) {
3361
if(reader.hasAttribute(ATTR_MAPPED))
3362
mapped.push_back(0);
3363
subs.emplace_back();
3364
auto &subname = subs.back();
3365
shadows.emplace_back();
3366
auto &shadow = shadows.back();
3367
shadow.second = importSubName(reader,reader.getAttribute("sub"),restoreLabel);
3368
if(reader.hasAttribute(ATTR_SHADOWED) && !IGNORE_SHADOW)
3369
subname = shadow.first = importSubName(reader,reader.getAttribute(ATTR_SHADOWED),restoreLabel);
3371
subname = shadow.second;
3372
if(reader.hasAttribute(ATTR_SHADOW) && !IGNORE_SHADOW)
3373
shadow.first = importSubName(reader,reader.getAttribute(ATTR_SHADOW),restoreLabel);
3375
}else if(reader.hasAttribute("count")) {
3376
int count = reader.getAttributeAsInteger("count");
3378
shadows.resize(count);
3379
for (int i = 0; i < count; i++) {
3380
reader.readElement("Sub");
3381
shadows[i].second = importSubName(reader,reader.getAttribute("value"),restoreLabel);
3382
if(reader.hasAttribute(ATTR_SHADOWED) && !IGNORE_SHADOW)
3383
subs[i] = shadows[i].first =
3384
importSubName(reader,reader.getAttribute(ATTR_SHADOWED),restoreLabel);
3386
subs[i] = shadows[i].second;
3387
if(reader.hasAttribute(ATTR_SHADOW) && !IGNORE_SHADOW)
3388
shadows[i].first = importSubName(reader,reader.getAttribute(ATTR_SHADOW),restoreLabel);
3390
if(reader.hasAttribute(ATTR_MAPPED))
3391
mapped.push_back(i);
3393
reader.readEndElement("XLink");
3395
setFlag(LinkRestoreLabel,restoreLabel);
3402
if(!file.empty() || (!object && !name.empty())) {
3403
this->stamp = stampAttr;
3404
setValue(std::move(file),std::move(name),std::move(subs),std::move(shadows));
3406
setValue(object,std::move(subs),std::move(shadows));
3407
_mapped = std::move(mapped);
3410
Property *PropertyXLink::CopyOnImportExternal(
3411
const std::map<std::string,std::string> &nameMap) const
3413
auto owner = Base::freecad_dynamic_cast<const DocumentObject>(getContainer());
3414
if(!owner || !owner->getDocument() || !_pcLink || !_pcLink->isAttachedToDocument())
3417
auto subs = updateLinkSubs(_pcLink,_SubList,
3418
&tryImportSubName,owner->getDocument(),nameMap);
3419
auto linked = tryImport(owner->getDocument(),_pcLink,nameMap);
3420
if(subs.empty() && linked==_pcLink)
3423
std::unique_ptr<PropertyXLink> p(new PropertyXLink);
3424
copyTo(*p,linked,&subs);
3428
Property *PropertyXLink::CopyOnLinkReplace(const App::DocumentObject *parent,
3429
App::DocumentObject *oldObj, App::DocumentObject *newObj) const
3431
auto res = tryReplaceLinkSubs(getContainer(),_pcLink,parent,oldObj,newObj,_SubList);
3434
std::unique_ptr<PropertyXLink> p(new PropertyXLink);
3435
copyTo(*p,res.first,&res.second);
3439
Property *PropertyXLink::CopyOnLabelChange(App::DocumentObject *obj,
3440
const std::string &ref, const char *newLabel) const
3442
auto owner = dynamic_cast<const DocumentObject*>(getContainer());
3443
if(!owner || !owner->getDocument() || !_pcLink || !_pcLink->isAttachedToDocument())
3445
auto subs = updateLinkSubs(_pcLink,_SubList,&updateLabelReference,obj,ref,newLabel);
3448
std::unique_ptr<PropertyXLink> p(new PropertyXLink);
3449
copyTo(*p,_pcLink,&subs);
3453
void PropertyXLink::copyTo(PropertyXLink &other,
3454
DocumentObject *linked, std::vector<std::string> *subs) const
3458
if(linked && linked->isAttachedToDocument()) {
3459
other.docName = linked->getDocument()->getName();
3460
other.objectName = linked->getNameInDocument();
3461
other.docInfo.reset();
3462
other.filePath.clear();
3464
other.objectName = objectName;
3465
other.docName.clear();
3466
other.docInfo = docInfo;
3467
other.filePath = filePath;
3470
other._SubList = std::move(*subs);
3472
other._SubList = _SubList;
3473
other._Flags = _Flags;
3476
Property *PropertyXLink::Copy() const
3478
std::unique_ptr<PropertyXLink> p(new PropertyXLink);
3483
void PropertyXLink::Paste(const Property &from)
3485
if(!from.isDerivedFrom(PropertyXLink::getClassTypeId()))
3486
throw Base::TypeError("Incompatible property to paste to");
3488
const auto &other = static_cast<const PropertyXLink&>(from);
3489
if(!other.docName.empty()) {
3490
auto doc = GetApplication().getDocument(other.docName.c_str());
3492
FC_WARN("Document '" << other.docName << "' not found");
3495
auto obj = doc->getObject(other.objectName.c_str());
3497
FC_WARN("Object '" << other.docName << '#' << other.objectName << "' not found");
3500
setValue(obj,std::vector<std::string>(other._SubList));
3502
setValue(std::string(other.filePath),std::string(other.objectName),
3503
std::vector<std::string>(other._SubList));
3504
setFlag(LinkAllowPartial,other.testFlag(LinkAllowPartial));
3507
bool PropertyXLink::supportXLink(const App::Property *prop) {
3508
return prop->isDerivedFrom(PropertyXLink::getClassTypeId()) ||
3509
prop->isDerivedFrom(PropertyXLinkSubList::getClassTypeId()) ||
3510
prop->isDerivedFrom(PropertyXLinkContainer::getClassTypeId());
3513
bool PropertyXLink::hasXLink(const App::Document *doc) {
3514
for(auto &v : _DocInfoMap) {
3515
if(v.second->hasXLink(doc))
3521
bool PropertyXLink::hasXLink(
3522
const std::vector<App::DocumentObject*> &objs, std::vector<App::Document*> *unsaved)
3524
std::set<App::Document*> docs;
3526
for(auto o : objs) {
3527
if(o && o->isAttachedToDocument() && docs.insert(o->getDocument()).second) {
3528
if(!hasXLink(o->getDocument()))
3533
if(!o->getDocument()->isSaved())
3534
unsaved->push_back(o->getDocument());
3540
void PropertyXLink::restoreDocument(const App::Document &doc) {
3541
DocInfo::restoreDocument(doc);
3544
std::map<App::Document*,std::set<App::Document*> >
3545
PropertyXLink::getDocumentOutList(App::Document *doc) {
3546
std::map<App::Document*,std::set<App::Document*> > ret;
3547
for(auto &v : _DocInfoMap) {
3548
for(auto link : v.second->links) {
3550
|| link->getScope() == LinkScope::Hidden
3551
|| link->testStatus(Property::PropTransient)
3552
|| link->testStatus(Property::Transient)
3553
|| link->testStatus(Property::PropNoPersist))
3555
auto obj = dynamic_cast<App::DocumentObject*>(link->getContainer());
3556
if(!obj || !obj->isAttachedToDocument() || !obj->getDocument())
3558
if(doc && obj->getDocument()!=doc)
3560
ret[obj->getDocument()].insert(v.second->pcDoc);
3566
std::map<App::Document*,std::set<App::Document*> >
3567
PropertyXLink::getDocumentInList(App::Document *doc) {
3568
std::map<App::Document*,std::set<App::Document*> > ret;
3569
for(auto &v : _DocInfoMap) {
3570
if(!v.second->pcDoc || (doc && doc!=v.second->pcDoc))
3572
auto &docs = ret[v.second->pcDoc];
3573
for(auto link : v.second->links) {
3574
if(link->getScope() == LinkScope::Hidden
3575
|| link->testStatus(Property::PropTransient)
3576
|| link->testStatus(Property::Transient)
3577
|| link->testStatus(Property::PropNoPersist))
3579
auto obj = dynamic_cast<App::DocumentObject*>(link->getContainer());
3580
if(obj && obj->isAttachedToDocument() && obj->getDocument())
3581
docs.insert(obj->getDocument());
3587
PyObject *PropertyXLink::getPyObject()
3591
const auto &subs = getSubValues(false);
3593
return _pcLink->getPyObject();
3595
ret.setItem(0,Py::Object(_pcLink->getPyObject(),true));
3596
PropertyString propString;
3597
if (subs.size() == 1) {
3598
propString.setValue(subs.front());
3599
ret.setItem(1,Py::asObject(propString.getPyObject()));
3601
Py::List list(subs.size());
3603
for (auto &sub : subs) {
3604
propString.setValue(sub);
3605
list[i++] = Py::asObject(propString.getPyObject());
3607
ret.setItem(1, list);
3609
return Py::new_reference_to(ret);
3612
void PropertyXLink::setPyObject(PyObject *value) {
3613
if(PySequence_Check(value)) {
3614
Py::Sequence seq(value);
3616
throw Base::ValueError("Expect input sequence of size 2");
3617
std::vector<std::string> subs;
3618
Py::Object pyObj(seq[0].ptr());
3619
Py::Object pySub(seq[1].ptr());
3620
if(pyObj.isNone()) {
3623
} else if(!PyObject_TypeCheck(pyObj.ptr(), &DocumentObjectPy::Type))
3624
throw Base::TypeError("Expect the first element to be of 'DocumentObject'");
3625
PropertyString propString;
3626
if(pySub.isString()) {
3627
propString.setPyObject(pySub.ptr());
3628
subs.push_back(propString.getStrValue());
3629
} else if (pySub.isSequence()) {
3630
Py::Sequence seq(pySub);
3631
subs.reserve(seq.size());
3632
for(Py_ssize_t i=0;i<seq.size();++i) {
3633
Py::Object sub(seq[i]);
3635
throw Base::TypeError("Expect only string inside second argument");
3636
propString.setPyObject(sub.ptr());
3637
subs.push_back(propString.getStrValue());
3640
throw Base::TypeError("Expect the second element to be a string or sequence of string");
3641
setValue(static_cast<DocumentObjectPy*>(pyObj.ptr())->getDocumentObjectPtr(), std::move(subs));
3642
} else if(PyObject_TypeCheck(value, &(DocumentObjectPy::Type))) {
3643
setValue(static_cast<DocumentObjectPy*>(value)->getDocumentObjectPtr());
3644
} else if (Py_None == value) {
3647
throw Base::TypeError("type must be 'DocumentObject', 'None', or '(DocumentObject, SubName)' or "
3648
"'DocumentObject, [SubName..])");
3652
const char *PropertyXLink::getSubName(bool newStyle) const {
3653
if(_SubList.empty() || _ShadowSubList.empty())
3655
return getSubNameWithStyle(_SubList[0],_ShadowSubList[0],newStyle).c_str();
3658
void PropertyXLink::getLinks(std::vector<App::DocumentObject *> &objs,
3659
bool all, std::vector<std::string> *subs, bool newStyle) const
3661
if((all||_pcScope!=LinkScope::Hidden) && _pcLink && _pcLink->isAttachedToDocument()) {
3662
objs.push_back(_pcLink);
3663
if(subs && _SubList.size()==_ShadowSubList.size())
3664
*subs = getSubValues(newStyle);
3668
bool PropertyXLink::adjustLink(const std::set<App::DocumentObject*> &inList) {
3669
if (_pcScope==LinkScope::Hidden)
3671
if(!_pcLink || !_pcLink->isAttachedToDocument() || !inList.count(_pcLink))
3673
auto subs = _SubList;
3674
auto link = adjustLinkSubs(this,inList,_pcLink,subs);
3676
setValue(link,std::move(subs));
3682
std::vector<std::string> PropertyXLink::getSubValues(bool newStyle) const {
3683
assert(_SubList.size() == _ShadowSubList.size());
3684
std::vector<std::string> ret;
3685
ret.reserve(_SubList.size());
3686
for(size_t i=0;i<_ShadowSubList.size();++i)
3687
ret.push_back(getSubNameWithStyle(_SubList[i],_ShadowSubList[i],newStyle));
3691
std::vector<std::string> PropertyXLink::getSubValuesStartsWith(const char* starter, bool newStyle) const
3695
std::vector<std::string> temp;
3696
for(const auto & it : _SubList) {
3697
if(strncmp(starter, it.c_str(), strlen(starter)) == 0) {
3704
void PropertyXLink::setAllowPartial(bool enable) {
3705
setFlag(LinkAllowPartial,enable);
3708
auto owner = dynamic_cast<const DocumentObject*>(getContainer());
3711
if(!App::GetApplication().isRestoring() &&
3712
!owner->getDocument()->isPerformingTransaction() &&
3713
!_pcLink && docInfo && !filePath.empty() && !objectName.empty() &&
3714
(!docInfo->pcDoc || docInfo->pcDoc->testStatus(Document::PartialDoc)))
3716
auto path = docInfo->getDocPath(filePath.c_str(),owner->getDocument(),false);
3718
App::GetApplication().openDocument(path.c_str());
3722
//**************************************************************************
3724
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3726
TYPESYSTEM_SOURCE(App::PropertyXLinkSub , App::PropertyXLink)
3728
PropertyXLinkSub::PropertyXLinkSub(bool allowPartial, PropertyLinkBase *parent)
3729
:PropertyXLink(allowPartial,parent)
3734
PropertyXLinkSub::~PropertyXLinkSub() = default;
3736
bool PropertyXLinkSub::upgrade(Base::XMLReader &reader, const char *typeName) {
3737
if(strcmp(typeName, PropertyLinkSubGlobal::getClassTypeId().getName())==0 ||
3738
strcmp(typeName, PropertyLinkSub::getClassTypeId().getName())==0 ||
3739
strcmp(typeName, PropertyLinkSubChild::getClassTypeId().getName())==0)
3741
App::PropertyLinkSub linkProp;
3742
linkProp.setContainer(getContainer());
3743
linkProp.Restore(reader);
3744
setValue(linkProp.getValue(),linkProp.getSubValues());
3747
return PropertyXLink::upgrade(reader,typeName);
3750
PyObject *PropertyXLinkSub::getPyObject()
3755
ret.setItem(0,Py::Object(_pcLink->getPyObject(),true));
3756
const auto &subs = getSubValues(false);
3757
Py::List list(subs.size());
3759
PropertyString propString;
3760
for (auto &sub : subs) {
3761
propString.setValue(sub);
3762
list[i++] = Py::asObject(propString.getPyObject());
3764
ret.setItem(1, list);
3765
return Py::new_reference_to(ret);
3768
//**************************************************************************
3769
// PropertyXLinkSubList
3770
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3772
TYPESYSTEM_SOURCE(App::PropertyXLinkSubList , App::PropertyLinkBase)
3774
//**************************************************************************
3775
// Construction/Destruction
3778
PropertyXLinkSubList::PropertyXLinkSubList()
3780
_pcScope = LinkScope::Global;
3781
setSyncSubObject(true);
3784
PropertyXLinkSubList::~PropertyXLinkSubList() = default;
3786
void PropertyXLinkSubList::setSyncSubObject(bool enable)
3788
_Flags.set((std::size_t)LinkSyncSubObject, enable);
3791
int PropertyXLinkSubList::getSize() const
3793
return static_cast<int>(_Links.size());
3796
void PropertyXLinkSubList::setValue(DocumentObject* lValue,const char* SubName)
3798
std::map<DocumentObject*,std::vector<std::string> > values;
3800
auto &subs = values[lValue];
3802
subs.emplace_back(SubName);
3804
setValues(std::move(values));
3807
void PropertyXLinkSubList::setValues(
3808
const std::vector<DocumentObject*>& lValue,
3809
const std::vector<const char*>& lSubNames)
3811
#define CHECK_SUB_SIZE(_l,_r) do{\
3812
if(_l.size()!=_r.size())\
3813
FC_THROWM(Base::ValueError, "object and subname size mismatch");\
3815
CHECK_SUB_SIZE(lValue,lSubNames);
3816
std::map<DocumentObject*,std::vector<std::string> > values;
3818
for(auto &obj : lValue) {
3819
const char *sub = lSubNames[i++];
3821
values[obj].emplace_back(sub);
3823
setValues(std::move(values));
3826
void PropertyXLinkSubList::setValues(const std::vector<DocumentObject*>& lValue,
3827
const std::vector<std::string>& lSubNames)
3829
CHECK_SUB_SIZE(lValue,lSubNames);
3830
std::map<DocumentObject*,std::vector<std::string> > values;
3832
for(auto &obj : lValue)
3833
values[obj].push_back(lSubNames[i++]);
3834
setValues(std::move(values));
3837
void PropertyXLinkSubList::setSubListValues(const std::vector<PropertyLinkSubList::SubSet> &svalues) {
3838
std::map<DocumentObject*,std::vector<std::string> > values;
3839
for(auto &v : svalues) {
3840
auto &s = values[v.first];
3841
s.reserve(s.size()+v.second.size());
3842
s.insert(s.end(),v.second.begin(),v.second.end());
3844
setValues(std::move(values));
3847
void PropertyXLinkSubList::setValues(
3848
const std::map<App::DocumentObject*,std::vector<std::string> > &values)
3850
setValues(std::map<App::DocumentObject*,std::vector<std::string> >(values));
3853
void PropertyXLinkSubList::setValues(
3854
std::map<App::DocumentObject*,std::vector<std::string> > &&values)
3856
for(auto &v : values) {
3857
if(!v.first || !v.first->isAttachedToDocument())
3858
FC_THROWM(Base::ValueError,"invalid document object");
3861
atomic_change guard(*this);
3863
for(auto it=_Links.begin(),itNext=it;it!=_Links.end();it=itNext) {
3865
auto iter = values.find(it->getValue());
3866
if(iter == values.end()) {
3870
it->setSubValues(std::move(iter->second));
3874
for(auto &v : values) {
3875
_Links.emplace_back(testFlag(LinkAllowPartial),this);
3876
_Links.back().setValue(v.first,std::move(v.second));
3881
void PropertyXLinkSubList::addValue(App::DocumentObject *obj,
3882
const std::vector<std::string> &subs, bool reset)
3884
addValue(obj,std::vector<std::string>(subs),reset);
3887
void PropertyXLinkSubList::addValue(App::DocumentObject *obj,
3888
std::vector<std::string> &&subs, bool reset) {
3890
if(!obj || !obj->isAttachedToDocument())
3891
FC_THROWM(Base::ValueError,"invalid document object");
3893
for(auto &l : _Links) {
3894
if(l.getValue() == obj) {
3895
auto s = l.getSubValues();
3896
if(s.empty() || reset)
3897
l.setSubValues(std::move(subs));
3899
s.reserve(s.size()+subs.size());
3900
std::move(subs.begin(),subs.end(),std::back_inserter(s));
3901
l.setSubValues(std::move(s));
3906
atomic_change guard(*this);
3907
_Links.emplace_back(testFlag(LinkAllowPartial),this);
3908
_Links.back().setValue(obj,std::move(subs));
3912
void PropertyXLinkSubList::setValue(DocumentObject *lValue, const std::vector<std::string> &SubList)
3914
std::map<DocumentObject *, std::vector<std::string> > values;
3916
values[lValue] = SubList;
3917
setValues(std::move(values));
3920
void PropertyXLinkSubList::setValues(const std::vector<DocumentObject*> &values) {
3921
atomic_change guard(*this);
3923
for(auto obj : values) {
3924
_Links.emplace_back(testFlag(LinkAllowPartial),this);
3925
_Links.back().setValue(obj);
3930
void PropertyXLinkSubList::set1Value(int idx,
3931
DocumentObject *value,
3932
const std::vector<std::string> &SubList)
3934
if(idx < -1 || idx > getSize())
3935
throw Base::RuntimeError("index out of bound");
3937
if(idx < 0 || idx+1 == getSize()) {
3938
if(SubList.empty()) {
3939
addValue(value,SubList);
3942
atomic_change guard(*this);
3943
_Links.emplace_back(testFlag(LinkAllowPartial),this);
3944
_Links.back().setValue(value);
3949
auto it = _Links.begin();
3952
it->setValue(value,SubList);
3955
const string PropertyXLinkSubList::getPyReprString() const
3958
return std::string("None");
3959
std::ostringstream ss;
3961
for(auto &link : _Links) {
3962
auto obj = link.getValue();
3963
if(!obj || !obj->isAttachedToDocument())
3965
ss << "(App.getDocument('" << obj->getDocument()->getName()
3966
<< "').getObject('" << obj->getNameInDocument() << "'), (";
3967
const auto &subs = link.getSubValues();
3971
for(auto &sub : subs)
3972
ss << "'" << sub << "',";
3980
DocumentObject *PropertyXLinkSubList::getValue() const
3983
return _Links.begin()->getValue();
3987
int PropertyXLinkSubList::removeValue(App::DocumentObject *lValue)
3989
atomic_change guard(*this,false);
3991
for(auto it=_Links.begin();it!=_Links.end();) {
3992
if(it->getValue() != lValue)
3995
guard.aboutToChange();
3996
it = _Links.erase(it);
4004
PyObject *PropertyXLinkSubList::getPyObject()
4007
for(auto &link : _Links) {
4008
auto obj = link.getValue();
4009
if(!obj || !obj->isAttachedToDocument())
4013
tup[0] = Py::asObject(obj->getPyObject());
4015
const auto &subs = link.getSubValues();
4016
Py::Tuple items(subs.size());
4017
for (std::size_t j = 0; j < subs.size(); j++) {
4018
items[j] = Py::String(subs[j]);
4023
return Py::new_reference_to(list);
4026
void PropertyXLinkSubList::setPyObject(PyObject *value)
4028
try { //try PropertyLinkSub syntax
4029
PropertyLinkSub dummy;
4030
dummy.setAllowExternal(true);
4031
dummy.setPyObject(value);
4032
this->setValue(dummy.getValue(), dummy.getSubValues());
4035
catch (Base::Exception&) {}
4037
if (!PyTuple_Check(value) && !PyList_Check(value))
4038
throw Base::TypeError("Invalid type. Accepts (DocumentObject, (subname...)) or sequence of such type.");
4039
Py::Sequence seq(value);
4040
std::map<DocumentObject*, std::vector<std::string> > values;
4042
for(Py_ssize_t i=0;i<seq.size();++i) {
4043
PropertyLinkSub link;
4044
link.setAllowExternal(true);
4045
link.setPyObject(seq[i].ptr());
4046
const auto &subs = link.getSubValues();
4047
auto &s = values[link.getValue()];
4048
s.reserve(s.size()+subs.size());
4049
s.insert(s.end(),subs.begin(),subs.end());
4052
catch(Base::Exception&){
4053
throw Base::TypeError("Invalid type inside sequence. Must be type of (DocumentObject, (subname...))");
4055
setValues(std::move(values));
4058
void PropertyXLinkSubList::afterRestore() {
4059
for(auto &l : _Links)
4063
void PropertyXLinkSubList::onContainerRestored() {
4064
for(auto &l : _Links)
4065
l.onContainerRestored();
4068
void PropertyXLinkSubList::updateElementReference(DocumentObject *feature, bool reverse,bool notify) {
4069
for(auto &l : _Links)
4070
l.updateElementReference(feature,reverse,notify);
4073
bool PropertyXLinkSubList::referenceChanged() const{
4074
for(auto &l : _Links) {
4075
if(l.referenceChanged())
4081
void PropertyXLinkSubList::Save (Base::Writer &writer) const
4083
writer.Stream() << writer.ind() << "<XLinkSubList count=\"" << _Links.size();
4084
if(testFlag(LinkAllowPartial))
4085
writer.Stream() << "\" partial=\"1";
4086
writer.Stream() <<"\">" << endl;
4088
for(auto &l : _Links)
4091
writer.Stream() << writer.ind() << "</XLinkSubList>" << endl ;
4094
void PropertyXLinkSubList::Restore(Base::XMLReader &reader)
4096
reader.readElement("XLinkSubList");
4097
setFlag(LinkAllowPartial,
4098
reader.hasAttribute("partial") &&
4099
reader.getAttributeAsInteger("partial"));
4100
int count = reader.getAttributeAsInteger("count");
4101
atomic_change guard(*this,false);
4103
for(int i=0;i<count;++i) {
4104
_Links.emplace_back(false,this);
4105
_Links.back().Restore(reader);
4107
reader.readEndElement("XLinkSubList");
4111
Property *PropertyXLinkSubList::CopyOnImportExternal(
4112
const std::map<std::string,std::string> &nameMap) const
4114
std::unique_ptr<Property> copy;
4115
auto it = _Links.begin();
4116
for(;it!=_Links.end();++it) {
4117
copy.reset(it->CopyOnImportExternal(nameMap));
4122
std::unique_ptr<PropertyXLinkSubList> p(new PropertyXLinkSubList);
4123
for(auto iter=_Links.begin();iter!=it;++iter) {
4124
p->_Links.emplace_back();
4125
iter->copyTo(p->_Links.back());
4127
p->_Links.emplace_back();
4128
static_cast<PropertyXLinkSub&>(*copy).copyTo(p->_Links.back());
4129
for(++it;it!=_Links.end();++it) {
4130
p->_Links.emplace_back();
4131
copy.reset(it->CopyOnImportExternal(nameMap));
4133
static_cast<PropertyXLinkSub&>(*copy).copyTo(p->_Links.back());
4135
it->copyTo(p->_Links.back());
4140
Property *PropertyXLinkSubList::CopyOnLabelChange(App::DocumentObject *obj,
4141
const std::string &ref, const char *newLabel) const
4143
std::unique_ptr<Property> copy;
4144
auto it = _Links.begin();
4145
for(;it!=_Links.end();++it) {
4146
copy.reset(it->CopyOnLabelChange(obj,ref,newLabel));
4151
std::unique_ptr<PropertyXLinkSubList> p(new PropertyXLinkSubList);
4152
for(auto iter=_Links.begin();iter!=it;++iter) {
4153
p->_Links.emplace_back();
4154
iter->copyTo(p->_Links.back());
4156
p->_Links.emplace_back();
4157
static_cast<PropertyXLinkSub&>(*copy).copyTo(p->_Links.back());
4158
for(++it;it!=_Links.end();++it) {
4159
p->_Links.emplace_back();
4160
copy.reset(it->CopyOnLabelChange(obj,ref,newLabel));
4162
static_cast<PropertyXLinkSub&>(*copy).copyTo(p->_Links.back());
4164
it->copyTo(p->_Links.back());
4169
Property *PropertyXLinkSubList::CopyOnLinkReplace(const App::DocumentObject *parent,
4170
App::DocumentObject *oldObj, App::DocumentObject *newObj) const
4172
std::unique_ptr<Property> copy;
4173
PropertyXLinkSub *copied = nullptr;
4174
std::set<std::string> subs;
4175
auto it = _Links.begin();
4176
for(;it!=_Links.end();++it) {
4177
copy.reset(it->CopyOnLinkReplace(parent,oldObj,newObj));
4179
copied = static_cast<PropertyXLinkSub*>(copy.get());
4180
if(copied->getValue() == newObj) {
4181
for(auto &sub : copied->getSubValues())
4189
std::unique_ptr<PropertyXLinkSubList> p(new PropertyXLinkSubList);
4190
for(auto iter=_Links.begin();iter!=it;++iter) {
4191
if(iter->getValue()==newObj && copied->getValue()==newObj) {
4192
// merge subnames in case new object already exists
4193
for(auto &sub : iter->getSubValues()) {
4194
if(subs.insert(sub).second)
4195
copied->_SubList.push_back(sub);
4198
p->_Links.emplace_back();
4199
iter->copyTo(p->_Links.back());
4202
p->_Links.emplace_back();
4203
copied->copyTo(p->_Links.back());
4204
copied = &p->_Links.back();
4205
for(++it;it!=_Links.end();++it) {
4206
if((it->getValue()==newObj||it->getValue()==oldObj)
4207
&& copied->getValue()==newObj)
4209
// merge subnames in case new object already exists
4210
for(auto &sub : it->getSubValues()) {
4211
if(subs.insert(sub).second)
4212
copied->_SubList.push_back(sub);
4216
p->_Links.emplace_back();
4217
copy.reset(it->CopyOnLinkReplace(parent,oldObj,newObj));
4219
static_cast<PropertyXLinkSub&>(*copy).copyTo(p->_Links.back());
4221
it->copyTo(p->_Links.back());
4226
Property *PropertyXLinkSubList::Copy() const
4228
PropertyXLinkSubList *p = new PropertyXLinkSubList();
4229
for(auto &l : _Links) {
4230
p->_Links.emplace_back(testFlag(LinkAllowPartial),p);
4231
l.copyTo(p->_Links.back());
4236
void PropertyXLinkSubList::Paste(const Property &from)
4238
if(!from.isDerivedFrom(PropertyXLinkSubList::getClassTypeId()))
4239
throw Base::TypeError("Incompatible property to paste to");
4243
for(auto &l : static_cast<const PropertyXLinkSubList&>(from)._Links) {
4244
_Links.emplace_back(testFlag(LinkAllowPartial),this);
4245
_Links.back().Paste(l);
4250
unsigned int PropertyXLinkSubList::getMemSize () const
4252
unsigned int size=0;
4253
for(auto &l : _Links)
4254
size += l.getMemSize();
4258
const std::vector<std::string> &PropertyXLinkSubList::getSubValues(App::DocumentObject *obj) const {
4259
for(auto &l : _Links) {
4260
if(l.getValue() == obj)
4261
return l.getSubValues();
4263
FC_THROWM(Base::RuntimeError, "object not found");
4266
std::vector<std::string> PropertyXLinkSubList::getSubValues(App::DocumentObject *obj, bool newStyle) const {
4267
for(auto &l : _Links) {
4268
if(l.getValue() == obj)
4269
return l.getSubValues(newStyle);
4274
void PropertyXLinkSubList::getLinks(std::vector<App::DocumentObject *> &objs,
4275
bool all, std::vector<std::string> *subs, bool newStyle) const
4277
if(all||_pcScope!=LinkScope::Hidden) {
4279
objs.reserve(objs.size()+_Links.size());
4280
for(auto &l : _Links) {
4281
auto obj = l.getValue();
4282
if(obj && obj->isAttachedToDocument())
4283
objs.push_back(obj);
4288
for(auto &l : _Links) {
4289
auto obj = l.getValue();
4290
if(obj && obj->isAttachedToDocument())
4291
count += std::max((int)l.getSubValues().size(), 1);
4294
objs.reserve(objs.size()+_Links.size());
4295
for(auto &l : _Links) {
4296
auto obj = l.getValue();
4297
if(obj && obj->isAttachedToDocument())
4298
objs.push_back(obj);
4303
objs.reserve(objs.size()+count);
4304
subs->reserve(subs->size()+count);
4305
for(auto &l : _Links) {
4306
auto obj = l.getValue();
4307
if(obj && obj->isAttachedToDocument()) {
4308
auto subnames = l.getSubValues(newStyle);
4309
if (subnames.empty())
4310
subnames.emplace_back("");
4311
for(auto &sub : subnames) {
4312
objs.push_back(obj);
4313
subs->push_back(std::move(sub));
4320
void PropertyXLinkSubList::breakLink(App::DocumentObject *obj, bool clear) {
4321
if(clear && getContainer()==obj) {
4325
atomic_change guard(*this,false);
4326
for(auto &l : _Links) {
4327
if(l.getValue() == obj) {
4328
guard.aboutToChange();
4329
l.setValue(nullptr);
4335
bool PropertyXLinkSubList::adjustLink(const std::set<App::DocumentObject*> &inList) {
4336
if (_pcScope==LinkScope::Hidden)
4338
std::map<App::DocumentObject*,std::vector<std::string> > values;
4339
bool touched = false;
4341
for(auto &l : _Links) {
4342
auto obj = l.getValue();
4343
if(!obj || !obj->isAttachedToDocument()) {
4347
if(inList.count(obj) && adjustLinkSubs(this,inList,obj,l._SubList,&values))
4351
decltype(_Links) tmp;
4353
// XLink allows detached state, i.e. with closed external document. So
4354
// we need to preserve empty link
4355
for(auto it=_Links.begin(),itNext=it;it!=_Links.end();it=itNext) {
4358
tmp.splice(tmp.end(),_Links,it);
4361
setValues(std::move(values));
4362
_Links.splice(_Links.end(),tmp);
4367
int PropertyXLinkSubList::checkRestore(std::string *msg) const {
4368
for(auto &l : _Links) {
4370
if((res = l.checkRestore(msg)))
4376
bool PropertyXLinkSubList::upgrade(Base::XMLReader &reader, const char *typeName) {
4377
if(strcmp(typeName, PropertyLinkListGlobal::getClassTypeId().getName())==0 ||
4378
strcmp(typeName, PropertyLinkList::getClassTypeId().getName())==0 ||
4379
strcmp(typeName, PropertyLinkListChild::getClassTypeId().getName())==0)
4381
PropertyLinkList linkProp;
4382
linkProp.setContainer(getContainer());
4383
linkProp.Restore(reader);
4384
setValues(linkProp.getValues());
4386
} else if (strcmp(typeName, PropertyLinkSubListGlobal::getClassTypeId().getName())==0 ||
4387
strcmp(typeName, PropertyLinkSubList::getClassTypeId().getName())==0 ||
4388
strcmp(typeName, PropertyLinkSubListChild::getClassTypeId().getName())==0)
4390
PropertyLinkSubList linkProp;
4391
linkProp.setContainer(getContainer());
4392
linkProp.Restore(reader);
4393
std::map<DocumentObject *, std::vector<std::string> > values;
4394
const auto &objs = linkProp.getValues();
4395
const auto &subs = linkProp.getSubValues();
4396
assert(objs.size() == subs.size());
4397
for(size_t i=0;i<objs.size();++i)
4398
values[objs[i]].push_back(subs[i]);
4399
setValues(std::move(values));
4403
_Links.emplace_back(testFlag(LinkAllowPartial),this);
4404
if(!_Links.back().upgrade(reader,typeName)) {
4411
void PropertyXLinkSubList::setAllowPartial(bool enable) {
4412
setFlag(LinkAllowPartial,enable);
4413
for(auto &l : _Links)
4414
l.setAllowPartial(enable);
4417
void PropertyXLinkSubList::hasSetChildValue(Property &) {
4422
void PropertyXLinkSubList::aboutToSetChildValue(Property &) {
4423
if(!signalCounter || !hasChanged) {
4430
std::vector<App::DocumentObject*> PropertyXLinkSubList::getValues() const
4432
std::vector<DocumentObject*> xLinks;
4437
//**************************************************************************
4439
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4441
TYPESYSTEM_SOURCE(App::PropertyXLinkList , App::PropertyXLinkSubList)
4443
//**************************************************************************
4444
// Construction/Destruction
4446
PropertyXLinkList::PropertyXLinkList() = default;
4448
PropertyXLinkList::~PropertyXLinkList() = default;
4450
PyObject *PropertyXLinkList::getPyObject()
4452
for(auto &link : _Links) {
4453
auto obj = link.getValue();
4454
if(!obj || !obj->isAttachedToDocument())
4456
if(link.hasSubName())
4457
return PropertyXLinkSubList::getPyObject();
4461
for(auto &link : _Links) {
4462
auto obj = link.getValue();
4463
if(!obj || !obj->isAttachedToDocument())
4465
list.append(Py::asObject(obj->getPyObject()));
4467
return Py::new_reference_to(list);
4470
void PropertyXLinkList::setPyObject(PyObject *value)
4472
try { //try PropertyLinkList syntax
4473
PropertyLinkList dummy;
4474
dummy.setAllowExternal(true);
4475
dummy.setPyObject(value);
4476
this->setValues(dummy.getValues());
4479
catch (Base::Exception&) {}
4481
PropertyXLinkSubList::setPyObject(value);
4484
//**************************************************************************
4485
// PropertyXLinkContainer
4486
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4487
TYPESYSTEM_SOURCE_ABSTRACT(App::PropertyXLinkContainer , App::PropertyLinkBase)
4489
PropertyXLinkContainer::PropertyXLinkContainer() {
4490
_pcScope = LinkScope::Global;
4491
_LinkRestored = false;
4494
PropertyXLinkContainer::~PropertyXLinkContainer() = default;
4496
void PropertyXLinkContainer::afterRestore() {
4501
for(auto &info : *_XLinkRestores) {
4502
auto obj = info.xlink->getValue();
4505
if(!info.docName.empty()) {
4506
if(info.docName != obj->getDocument()->getName())
4507
_DocMap[info.docName] = obj->getDocument()->getName();
4508
if(info.docLabel != obj->getDocument()->Label.getValue())
4509
_DocMap[App::quote(info.docLabel)] = obj->getDocument()->Label.getValue();
4511
if(_Deps.insert(std::make_pair(obj,info.xlink->getScope()==LinkScope::Hidden)).second)
4512
_XLinks[obj->getFullName()] = std::move(info.xlink);
4514
_XLinkRestores.reset();
4517
void PropertyXLinkContainer::breakLink(App::DocumentObject *obj, bool clear) {
4518
if(!obj || !obj->isAttachedToDocument())
4520
auto owner = dynamic_cast<App::DocumentObject*>(getContainer());
4521
if(!owner || !owner->isAttachedToDocument())
4523
if(!clear || obj!=owner) {
4524
auto it = _Deps.find(obj);
4525
if(it == _Deps.end())
4529
if (obj->getDocument() != owner->getDocument())
4530
_XLinks.erase(obj->getFullName());
4531
else if (!it->second)
4532
obj->_removeBackLink(owner);
4539
for(auto &v : _Deps) {
4541
if(!key || !key->isAttachedToDocument())
4544
if(!v.second && key->getDocument()==owner->getDocument())
4545
key->_removeBackLink(owner);
4551
int PropertyXLinkContainer::checkRestore(std::string *msg) const {
4553
for(auto &v : _XLinks) {
4554
int res = v.second->checkRestore(msg);
4562
void PropertyXLinkContainer::Save (Base::Writer &writer) const {
4564
writer.Stream() << writer.ind() << "<XLinks count=\"" << _XLinks.size();
4566
std::map<App::Document*,int> docSet;
4567
auto owner = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
4568
if(owner && !owner->isExporting()) {
4569
// Document name and label can change on restore, we shall record the
4570
// current document name and label and pair it with the associated
4571
// xlink, so that we can restore them correctly.
4573
for(auto &v : _XLinks) {
4575
auto obj = v.second->getValue();
4576
if(obj && obj->getDocument())
4577
docSet.insert(std::make_pair(obj->getDocument(),i));
4581
writer.Stream() << "\" docs=\"" << docSet.size();
4584
std::ostringstream ss;
4587
for(auto &v : _XLinks) {
4589
if(v.second->getScope() == LinkScope::Hidden) {
4595
writer.Stream() << "\" hidden=\"" << ss.str();
4597
writer.Stream() << "\">" << std::endl;
4600
for(auto &v : docSet) {
4601
writer.Stream() << writer.ind() << "<DocMap "
4602
<< "name=\"" << v.first->getName()
4603
<< "\" label=\"" << encodeAttribute(v.first->Label.getValue())
4604
<< "\" index=\"" << v.second << "\"/>" << std::endl;
4607
for(auto &v : _XLinks)
4608
v.second->Save(writer);
4611
writer.Stream() << writer.ind() << "</XLinks>" << std::endl;
4614
void PropertyXLinkContainer::Restore(Base::XMLReader &reader) {
4615
reader.readElement("XLinks");
4616
auto count = reader.getAttributeAsUnsigned("count");
4617
_XLinkRestores = std::make_unique<std::vector<RestoreInfo>>(count);
4619
if(reader.hasAttribute("hidden")) {
4620
std::istringstream iss(reader.getAttribute("hidden"));
4622
while(iss >> index) {
4623
if(index>=0 && index<static_cast<int>(count))
4624
_XLinkRestores->at(index).hidden = true;
4628
if(reader.hasAttribute("docs")) {
4629
auto docCount = reader.getAttributeAsUnsigned("docs");
4631
for(unsigned i=0;i<docCount;++i) {
4632
reader.readElement("DocMap");
4633
auto index = reader.getAttributeAsUnsigned("index");
4635
FC_ERR(propertyName(this) << " invalid document map entry");
4638
auto &info = _XLinkRestores->at(index);
4639
info.docName = reader.getAttribute("name");
4640
info.docLabel = reader.getAttribute("label");
4644
for(auto &info : *_XLinkRestores) {
4645
info.xlink.reset(createXLink());
4647
info.xlink->setScope(LinkScope::Hidden);
4648
info.xlink->Restore(reader);
4650
reader.readEndElement("XLinks");
4653
void PropertyXLinkContainer::aboutToSetChildValue(App::Property &prop) {
4654
auto xlink = dynamic_cast<App::PropertyXLink*>(&prop);
4655
if(xlink && xlink->testFlag(LinkDetached)) {
4656
if(_Deps.erase(const_cast<App::DocumentObject*>(xlink->getValue())))
4657
onBreakLink(xlink->getValue());
4661
void PropertyXLinkContainer::onBreakLink(DocumentObject *) {
4664
PropertyXLink *PropertyXLinkContainer::createXLink() {
4665
return new PropertyXLink(false,this);
4668
bool PropertyXLinkContainer::isLinkedToDocument(const App::Document &doc) const {
4669
auto iter = _XLinks.lower_bound(doc.getName());
4670
if(iter != _XLinks.end()) {
4671
size_t len = strlen(doc.getName());
4672
return iter->first.size()>len
4673
&& iter->first[len] == '#'
4674
&& boost::starts_with(iter->first,doc.getName());
4679
void PropertyXLinkContainer::updateDeps(std::map<DocumentObject*,bool> &&newDeps) {
4680
auto owner = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
4681
if(!owner || !owner->isAttachedToDocument())
4683
newDeps.erase(owner);
4685
for(auto &v : newDeps) {
4687
if(obj && obj->isAttachedToDocument()) {
4688
auto it = _Deps.find(obj);
4689
if(it != _Deps.end()) {
4690
if(v.second != it->second) {
4692
obj->_removeBackLink(owner);
4694
obj->_addBackLink(owner);
4699
if(owner->getDocument()!=obj->getDocument()) {
4700
auto &xlink = _XLinks[obj->getFullName()];
4702
xlink.reset(createXLink());
4703
xlink->setValue(obj);
4705
xlink->setScope(v.second?LinkScope::Hidden:LinkScope::Global);
4708
obj->_addBackLink(owner);
4713
for(auto &v : _Deps) {
4715
if(!obj || !obj->isAttachedToDocument())
4717
if(obj->getDocument()==owner->getDocument()) {
4719
obj->_removeBackLink(owner);
4721
_XLinks.erase(obj->getFullName());
4724
_Deps = std::move(newDeps);
4726
_LinkRestored = testFlag(LinkRestoring);
4728
if(!_LinkRestored && !testFlag(LinkDetached)) {
4729
for(auto it=_XLinks.begin(),itNext=it;it!=_XLinks.end();it=itNext) {
4731
if(!it->second->getValue())
4737
void PropertyXLinkContainer::clearDeps() {
4738
auto owner = dynamic_cast<App::DocumentObject*>(getContainer());
4739
if(!owner || !owner->isAttachedToDocument())
4742
if (!owner->testStatus(ObjectStatus::Destroy)) {
4743
for(auto &v : _Deps) {
4745
if(!v.second && obj && obj->isAttachedToDocument() && obj->getDocument()==owner->getDocument())
4746
obj->_removeBackLink(owner);
4752
_LinkRestored = false;
4755
void PropertyXLinkContainer::getLinks(std::vector<App::DocumentObject *> &objs,
4756
bool all, std::vector<std::string> * /*subs*/, bool /*newStyle*/) const
4758
for(auto &v : _Deps) {
4759
if(all || !v.second)
4760
objs.push_back(v.first);