1
/***************************************************************************
2
* Copyright (c) 2015 Eivind Kvedalen <eivind@kvedalen.name> *
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
***************************************************************************/
23
#include "PreCompiled.h"
29
#include <boost/algorithm/string/predicate.hpp>
31
#include <App/DocumentObjectPy.h>
32
#include <Base/GeometryPyCXX.h>
33
#include <Base/Tools.h>
34
#include <Base/Interpreter.h>
35
#include <Base/QuantityPy.h>
36
#include <Base/Console.h>
37
#include <Base/Reader.h>
38
#include <CXX/Objects.hxx>
40
#include "ObjectIdentifier.h"
41
#include "Application.h"
43
#include "ExpressionParser.h"
48
FC_LOG_LEVEL_INIT("Expression",true,true)
56
* @brief Quote input string according to quoting rules for an expression: because " and ' are
57
* used to designate inch and foot units, strings are quoted as <<string>>.
63
std::string App::quote(const std::string &input, bool toPython)
65
std::stringstream output;
67
std::string::const_iterator cur = input.begin();
68
std::string::const_iterator end = input.end();
70
output << (toPython?"'":"<<");
92
output << (toPython?">":"\\>");
99
output << (toPython?"'":">>");
106
* @brief Construct an ObjectIdentifier object, given an owner and a single-value property.
107
* @param _owner Owner of property.
108
* @param property Name of property.
111
ObjectIdentifier::ObjectIdentifier(const App::PropertyContainer * _owner,
112
const std::string & property, int index)
114
, documentNameSet(false)
115
, documentObjectNameSet(false)
116
, localProperty(false)
120
const DocumentObject * docObj = freecad_dynamic_cast<const DocumentObject>(_owner);
122
FC_THROWM(Base::RuntimeError,"Property must be owned by a document object.");
123
owner = const_cast<DocumentObject*>(docObj);
125
if (!property.empty()) {
126
setDocumentObjectName(docObj);
129
if (!property.empty()) {
130
addComponent(SimpleComponent(property));
132
addComponent(ArrayComponent(index));
136
ObjectIdentifier::ObjectIdentifier(const App::PropertyContainer * _owner, bool localProperty)
138
, documentNameSet(false)
139
, documentObjectNameSet(false)
140
, localProperty(localProperty)
144
const DocumentObject * docObj = freecad_dynamic_cast<const DocumentObject>(_owner);
146
FC_THROWM(Base::RuntimeError,"Property must be owned by a document object.");
147
owner = const_cast<DocumentObject*>(docObj);
152
* @brief Construct an ObjectIdentifier object given a property. The property is assumed to be single-valued.
153
* @param prop Property to construct object identifier for.
156
ObjectIdentifier::ObjectIdentifier(const Property &prop, int index)
158
, documentNameSet(false)
159
, documentObjectNameSet(false)
160
, localProperty(false)
163
DocumentObject * docObj = freecad_dynamic_cast<DocumentObject>(prop.getContainer());
166
FC_THROWM(Base::TypeError, "Property must be owned by a document object.");
168
FC_THROWM(Base::RuntimeError, "Property must have a name.");
170
owner = const_cast<DocumentObject*>(docObj);
172
setDocumentObjectName(docObj);
174
addComponent(SimpleComponent(String(prop.getName())));
176
addComponent(ArrayComponent(index));
180
* @brief Get the name of the property.
184
std::string App::ObjectIdentifier::getPropertyName() const
186
ResolveResults result(*this);
188
assert(result.propertyIndex >=0 && static_cast<std::size_t>(result.propertyIndex) < components.size());
190
return components[result.propertyIndex].getName();
194
* @brief Get Component at given index \a i.
195
* @param i: Index to get
196
* @param idx: optional return of adjusted component index
197
* @return A component.
200
const App::ObjectIdentifier::Component &App::ObjectIdentifier::getPropertyComponent(int i, int *idx) const
202
ResolveResults result(*this);
204
i += result.propertyIndex;
205
if (i < 0 || i >= static_cast<int>(components.size()))
206
FC_THROWM(Base::ValueError, "Invalid property component index");
211
return components[i];
214
void App::ObjectIdentifier::setComponent(int idx, Component &&comp)
216
if (idx < 0 || idx >= static_cast<int>(components.size()))
217
FC_THROWM(Base::ValueError, "Invalid component index");
218
components[idx] = std::move(comp);
222
void App::ObjectIdentifier::setComponent(int idx, const Component &comp)
224
setComponent(idx, Component(comp));
227
std::vector<ObjectIdentifier::Component> ObjectIdentifier::getPropertyComponents() const {
228
if(components.size()<=1 || documentObjectName.getString().empty())
230
ResolveResults result(*this);
231
if(result.propertyIndex==0)
233
std::vector<ObjectIdentifier::Component> res;
234
res.insert(res.end(),components.begin()+result.propertyIndex,components.end());
239
* @brief Compare object identifier with \a other.
240
* @param other Other object identifier.
241
* @return true if they are equal.
244
bool ObjectIdentifier::operator ==(const ObjectIdentifier &other) const
246
return owner==other.owner && toString() == other.toString();
250
* @brief Compare object identifier with \a other.
251
* @param other Other object identifier
252
* @return true if they differ from each other.
255
bool ObjectIdentifier::operator !=(const ObjectIdentifier &other) const
257
return !(operator==)(other);
261
* @brief Compare object identifier with other.
262
* @param other Other object identifier.
263
* @return true if this object is less than the other.
266
bool ObjectIdentifier::operator <(const ObjectIdentifier &other) const
268
if(owner < other.owner)
270
if(owner > other.owner)
272
return toString() < other.toString();
276
* @brief Return number of components.
277
* @return Number of components in this identifier.
280
int ObjectIdentifier::numComponents() const
282
return components.size();
286
* @brief Compute number of sub components, i.e excluding the property.
287
* @return Number of components.
290
int ObjectIdentifier::numSubComponents() const
292
ResolveResults result(*this);
294
return components.size() - result.propertyIndex;
297
bool ObjectIdentifier::verify(const App::Property &prop, bool silent) const {
298
ResolveResults result(*this);
299
if(components.size() - result.propertyIndex != 1) {
302
FC_THROWM(Base::ValueError,"Invalid property path: single component expected");
304
if(!components[result.propertyIndex].isSimple()) {
307
FC_THROWM(Base::ValueError,"Invalid property path: simple component expected");
309
const std::string &name = components[result.propertyIndex].getName();
311
bool isAddress = addr.parseAbsoluteAddress(name.c_str());
312
if((isAddress && addr.toString(CellAddress::Cell::ShowRowColumn) != prop.getName()) ||
313
(!isAddress && name!=prop.getName()))
317
FC_THROWM(Base::ValueError,"Invalid property path: name mismatch");
323
* @brief Create a string representation of this object identifier.
325
* An identifier is written as document#documentobject.property.subproperty1...subpropertyN
326
* document# may be dropped; it is assumed to be within owner's document. If documentobject is dropped,
327
* the property is assumed to be owned by the owner specified in the object identifiers constructor.
332
const std::string &ObjectIdentifier::toString() const
334
if(!_cache.empty() || !owner)
337
std::ostringstream s;
338
ResolveResults result(*this);
340
if(result.propertyIndex >= (int)components.size())
344
(result.resolvedProperty &&
345
result.resolvedDocumentObject==owner &&
346
components.size()>1 &&
347
components[1].isSimple() &&
348
result.propertyIndex==0))
351
}else if (documentNameSet && !documentName.getString().empty()) {
352
if(documentObjectNameSet && !documentObjectName.getString().empty())
353
s << documentName.toString() << "#"
354
<< documentObjectName.toString() << '.';
355
else if(!result.resolvedDocumentObjectName.getString().empty())
356
s << documentName.toString() << "#"
357
<< result.resolvedDocumentObjectName.toString() << '.';
358
} else if (documentObjectNameSet && !documentObjectName.getString().empty()) {
359
s << documentObjectName.toString() << '.';
360
} else if (result.propertyIndex > 0) {
361
components[0].toString(s);
365
if(!subObjectName.getString().empty())
366
s << subObjectName.toString() << '.';
368
s << components[result.propertyIndex].getName();
369
getSubPathStr(s,result);
370
const_cast<ObjectIdentifier*>(this)->_cache = s.str();
374
std::string ObjectIdentifier::toPersistentString() const {
379
std::ostringstream s;
380
ResolveResults result(*this);
382
if(result.propertyIndex >= (int)components.size())
386
(result.resolvedProperty &&
387
result.resolvedDocumentObject==owner &&
388
components.size()>1 &&
389
components[1].isSimple() &&
390
result.propertyIndex==0))
393
}else if(result.resolvedDocumentObject &&
394
result.resolvedDocumentObject!=owner &&
395
result.resolvedDocumentObject->isExporting())
397
s << result.resolvedDocumentObject->getExportName(true);
398
if(documentObjectName.isRealString())
401
} else if (documentNameSet && !documentName.getString().empty()) {
402
if(documentObjectNameSet && !documentObjectName.getString().empty())
403
s << documentName.toString() << "#"
404
<< documentObjectName.toString() << '.';
405
else if(!result.resolvedDocumentObjectName.getString().empty())
406
s << documentName.toString() << "#"
407
<< result.resolvedDocumentObjectName.toString() << '.';
408
} else if (documentObjectNameSet && !documentObjectName.getString().empty()) {
409
s << documentObjectName.toString() << '.';
410
} else if (result.propertyIndex > 0) {
411
components[0].toString(s);
415
if(!subObjectName.getString().empty()) {
416
const char *subname = subObjectName.getString().c_str();
417
std::string exportName;
418
s << String(PropertyLinkBase::exportSubName(exportName,
419
result.resolvedDocumentObject,subname),true).toString() << '.';
422
s << components[result.propertyIndex].getName();
423
getSubPathStr(s,result);
427
std::size_t ObjectIdentifier::hash() const
429
if(_hash && !_cache.empty())
431
const_cast<ObjectIdentifier*>(this)->_hash = boost::hash_value(toString());
435
bool ObjectIdentifier::replaceObject(ObjectIdentifier &res, const App::DocumentObject *parent,
436
App::DocumentObject *oldObj, App::DocumentObject *newObj) const
438
ResolveResults result(*this);
440
if(!result.resolvedDocumentObject)
443
auto r = PropertyLinkBase::tryReplaceLink(owner, result.resolvedDocumentObject,
444
parent, oldObj, newObj, subObjectName.getString().c_str());
450
if(r.first != result.resolvedDocumentObject) {
451
if(r.first->getDocument()!=owner->getDocument()) {
452
auto doc = r.first->getDocument();
453
bool useLabel = res.documentName.isRealString();
454
const char *name = useLabel?doc->Label.getValue():doc->getName();
455
res.setDocumentName(String(name, useLabel), true);
457
if(documentObjectName.isRealString())
458
res.documentObjectName = String(r.first->Label.getValue(),true);
460
res.documentObjectName = String(r.first->getNameInDocument(),false,true);
462
res.subObjectName = String(r.second,true);
464
res.shadowSub.first.clear();
465
res.shadowSub.second.clear();
470
* @brief Escape toString representation so it is suitable for being embedded in a python command.
471
* @return Escaped string.
474
std::string ObjectIdentifier::toEscapedString() const
476
return Base::Tools::escapedUnicodeFromUtf8(toString().c_str());
479
bool ObjectIdentifier::updateLabelReference(
480
App::DocumentObject *obj, const std::string &ref, const char *newLabel)
485
ResolveResults result(*this);
487
if(!subObjectName.getString().empty() && result.resolvedDocumentObject) {
488
std::string sub = PropertyLinkBase::updateLabelReference(
489
result.resolvedDocumentObject, subObjectName.getString().c_str(), obj,ref,newLabel);
491
subObjectName = String(sub,true);
497
if(result.resolvedDocument != obj->getDocument())
500
if(!documentObjectName.getString().empty()) {
501
if(documentObjectName.isForceIdentifier())
504
if(!documentObjectName.isRealString() &&
505
documentObjectName.getString()==obj->getNameInDocument())
508
if(documentObjectName.getString()!=obj->Label.getValue())
511
documentObjectName = ObjectIdentifier::String(newLabel, true);
517
if (result.resolvedDocumentObject==obj &&
518
result.propertyIndex == 1 &&
519
result.resolvedDocumentObjectName.isRealString() &&
520
result.resolvedDocumentObjectName.getString()==obj->Label.getValue())
522
components[0].name = ObjectIdentifier::String(newLabel, true);
527
// If object identifier uses the label then resolving the document object will fail.
528
// So, it must be checked if using the new label will succeed
529
if (components.size()>1 && components[0].getName()==obj->Label.getValue()) {
530
ObjectIdentifier id(*this);
531
id.components[0].name.str = newLabel;
533
ResolveResults result(id);
535
if (result.propertyIndex == 1 && result.resolvedDocumentObject == obj) {
536
components[0].name = id.components[0].name;
545
bool ObjectIdentifier::relabeledDocument(ExpressionVisitor &v,
546
const std::string &oldLabel, const std::string &newLabel)
548
if (documentNameSet && documentName.isRealString() && documentName.getString()==oldLabel) {
550
documentName = String(newLabel,true);
558
* @brief Get sub field part of a property as a string.
559
* @return String representation of path.
562
void ObjectIdentifier::getSubPathStr(std::ostream &s, const ResolveResults &result, bool toPython) const
564
std::vector<Component>::const_iterator i = components.begin() + result.propertyIndex + 1;
565
while (i != components.end()) {
568
i->toString(s,toPython);
573
std::string ObjectIdentifier::getSubPathStr(bool toPython) const {
574
std::ostringstream ss;
575
getSubPathStr(ss,ResolveResults(*this),toPython);
581
* @brief Construct a Component part
582
* @param _name Name of component
583
* @param _type Type; simple, array, range or map
584
* @param _begin Array index or beginning of a Range, or INT_MAX for other type.
585
* @param _end ending of a Range, or INT_MAX for other type.
588
ObjectIdentifier::Component::Component(const String &_name,
589
ObjectIdentifier::Component::typeEnum _type, int _begin, int _end, int _step)
598
ObjectIdentifier::Component::Component(String &&_name,
599
ObjectIdentifier::Component::typeEnum _type, int _begin, int _end, int _step)
600
: name(std::move(_name))
609
size_t ObjectIdentifier::Component::getIndex(size_t count) const {
614
int idx = begin + (int)count;
618
FC_THROWM(Base::IndexError, "Array out of bound: " << begin << ", " << count);
621
Py::Object ObjectIdentifier::Component::get(const Py::Object &pyobj) const {
624
if(!pyobj.hasAttr(getName()))
625
FC_THROWM(Base::AttributeError, "No attribute named '" << getName() << "'");
626
res = pyobj.getAttr(getName());
627
} else if(isArray()) {
628
if(pyobj.isMapping())
629
res = Py::Mapping(pyobj).getItem(Py::Int(begin));
631
res = Py::Sequence(pyobj).getItem(begin);
633
res = Py::Mapping(pyobj).getItem(getName());
636
Py::Object slice(PySlice_New(Py::Int(begin).ptr(),
637
end!=INT_MAX?Py::Int(end).ptr():nullptr,
638
step!=1?Py::Int(step).ptr():nullptr),true);
639
PyObject *r = PyObject_GetItem(pyobj.ptr(),slice.ptr());
641
Base::PyException::ThrowException();
642
res = Py::asObject(r);
645
Base::PyException::ThrowException();
646
if(PyModule_Check(res.ptr()) && !ExpressionParser::isModuleImported(res.ptr()))
647
FC_THROWM(Base::RuntimeError, "Module '" << getName() << "' access denied.");
651
void ObjectIdentifier::Component::set(Py::Object &pyobj, const Py::Object &value) const {
653
if(PyObject_SetAttrString(*pyobj, getName().c_str(), *value ) == -1)
654
Base::PyException::ThrowException();
655
} else if(isArray()) {
656
if(pyobj.isMapping())
657
Py::Mapping(pyobj).setItem(Py::Int(begin),value);
659
Py::Sequence(pyobj).setItem(begin,value);
661
Py::Mapping(pyobj).setItem(getName(),value);
664
Py::Object slice(PySlice_New(Py::Int(begin).ptr(),
665
end!=INT_MAX?Py::Int(end).ptr():nullptr,
666
step!=1?Py::Int(step).ptr():nullptr),true);
667
if(PyObject_SetItem(pyobj.ptr(),slice.ptr(),value.ptr())<0)
668
Base::PyException::ThrowException();
672
void ObjectIdentifier::Component::del(Py::Object &pyobj) const {
674
pyobj.delAttr(getName());
676
if(pyobj.isMapping())
677
Py::Mapping(pyobj).delItem(Py::Int(begin));
679
PySequence_DelItem(pyobj.ptr(),begin);
681
Py::Mapping(pyobj).delItem(getName());
684
Py::Object slice(PySlice_New(Py::Int(begin).ptr(),
685
end!=INT_MAX?Py::Int(end).ptr():nullptr,
686
step!=1?Py::Int(step).ptr():nullptr),true);
687
if(PyObject_DelItem(pyobj.ptr(),slice.ptr())<0)
688
Base::PyException::ThrowException();
693
* @brief Create a simple component part with the given name
694
* @param _component Name of component.
695
* @return A new Component object.
698
ObjectIdentifier::Component ObjectIdentifier::Component::SimpleComponent(const char *_component)
700
return Component(String(_component));
704
* @brief Create a simple component part with the given name
705
* @param _component Name of component.
706
* @return A new Component object.
709
ObjectIdentifier::Component ObjectIdentifier::Component::SimpleComponent(const ObjectIdentifier::String &_component)
711
return Component(_component);
714
ObjectIdentifier::Component ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String &&_component)
716
return Component(std::move(_component));
720
* @brief Create an array component with given name and index.
721
* @param _component Name of component
722
* @param _index Index of component
723
* @return A new Component object.
726
ObjectIdentifier::Component ObjectIdentifier::Component::ArrayComponent(int _index)
728
return Component(String(), Component::ARRAY, _index);
732
* @brief Create a map component with given name and key.
733
* @param _component Name of component
734
* @param _key Key of component
735
* @return A new Component object.
738
ObjectIdentifier::Component ObjectIdentifier::Component::MapComponent(const String & _key)
740
return Component(_key, Component::MAP);
743
ObjectIdentifier::Component ObjectIdentifier::Component::MapComponent(String &&_key)
745
return Component(std::move(_key), Component::MAP);
750
* @brief Create a range component with given begin and end.
751
* @param _begin beginning index of the range
752
* @param _end ending index of the range
753
* @return A new Component object.
756
ObjectIdentifier::Component ObjectIdentifier::Component::RangeComponent(int _begin, int _end, int _step)
758
return Component(String(), Component::RANGE, _begin, _end, _step);
762
* @brief Comparison operator for Component objects.
763
* @param other The object we want to compare to.
764
* @return true if they are equal, false if not.
767
bool ObjectIdentifier::Component::operator ==(const ObjectIdentifier::Component &other) const
769
if (type != other.type)
775
return name == other.name;
777
return begin == other.begin;
779
return begin == other.begin && end == other.end && step==other.step;
787
* @brief Create a string representation of a component.
788
* @return A string representing the component.
791
void ObjectIdentifier::Component::toString(std::ostream &ss, bool toPython) const
794
case Component::SIMPLE:
795
ss << name.getString();
798
ss << "[" << name.toString(toPython) << "]";
800
case Component::ARRAY:
801
ss << "[" << begin << "]";
803
case Component::RANGE:
826
* @brief Search for the document object given by name in doc.
828
* Name might be the internal name or a label. In any case, it must uniquely define
829
* the document object.
831
* @param doc Document to search
832
* @param name Name to search for.
833
* @return Pointer to document object if a unique pointer is found, 0 otherwise.
836
App::DocumentObject * ObjectIdentifier::getDocumentObject(const App::Document * doc,
837
const String & name, std::bitset<32> &flags)
839
DocumentObject * objectById = nullptr;
840
DocumentObject * objectByLabel = nullptr;
842
if(!name.isRealString()) {
843
// No object found with matching label, try using name directly
844
objectById = doc->getObject(static_cast<const char*>(name));
847
flags.set(ResolveByIdentifier);
850
if(name.isForceIdentifier())
854
std::vector<DocumentObject*> docObjects = doc->getObjects();
855
for (auto docObject : docObjects) {
856
if (strcmp(docObject->Label.getValue(), static_cast<const char*>(name)) == 0) {
857
// Found object with matching label
859
FC_WARN("duplicate object label " << doc->getName() << '#' << static_cast<const char*>(name));
862
objectByLabel = docObject;
866
if (!objectByLabel && !objectById) // Not found at all
868
else if (!objectByLabel) { // Found by name
869
flags.set(ResolveByIdentifier);
872
else if (!objectById) { // Found by label
873
flags.set(ResolveByLabel);
874
return objectByLabel;
876
else if (objectByLabel == objectById) { // Found by both name and label, same object
877
flags.set(ResolveByIdentifier);
878
flags.set(ResolveByLabel);
879
return objectByLabel;
882
flags.set(ResolveAmbiguous);
883
return nullptr; // Found by both name and label, two different objects
888
* @brief Resolve the object identifier to a concrete document, documentobject, and property.
890
* This method is a helper method that fills out data in the given ResolveResults object.
894
void ObjectIdentifier::resolve(ResolveResults &results) const
899
bool docAmbiguous = false;
901
/* Document name specified? */
902
if (!documentName.getString().empty()) {
903
results.resolvedDocument = getDocument(documentName,&docAmbiguous);
904
results.resolvedDocumentName = documentName;
907
results.resolvedDocument = owner->getDocument();
908
results.resolvedDocumentName = String(results.resolvedDocument->getName(), false, true);
911
results.subObjectName = subObjectName;
912
results.propertyName = "";
913
results.propertyIndex = 0;
915
// Assume document name and object name from owner if not found
916
if (!results.resolvedDocument) {
917
if (!documentName.getString().empty()) {
919
results.flags.set(ResolveAmbiguous);
923
results.resolvedDocument = owner->getDocument();
924
if (!results.resolvedDocument)
928
results.resolvedDocumentName = String(results.resolvedDocument->getName(), false, true);
930
/* Document object name specified? */
931
if (!documentObjectName.getString().empty()) {
932
results.resolvedDocumentObjectName = documentObjectName;
933
results.resolvedDocumentObject = getDocumentObject(
934
results.resolvedDocument, documentObjectName, results.flags);
935
if (!results.resolvedDocumentObject)
938
if (components.empty())
941
results.propertyName = components[ 0 ].name.getString();
942
results.propertyIndex = 0;
943
results.getProperty( *this );
946
/* Document object name not specified, resolve from path */
949
if (components.size() == 1 || (components.size()>1 && !components[0].isSimple())) {
950
/* Yes -- then this must be a property, so we get the document object's name from the owner */
951
results.resolvedDocumentObjectName = String(owner->getNameInDocument(), false, true);
952
results.resolvedDocumentObject = owner;
953
results.propertyName = components[0].name.getString();
954
results.propertyIndex = 0;
955
results.getProperty(*this);
957
else if (components.size() >= 2) {
959
if (!components[0].isSimple())
962
results.resolvedDocumentObject = getDocumentObject(
963
results.resolvedDocument, components[0].name, results.flags);
965
/* Possible to resolve component to a document object? */
966
if (results.resolvedDocumentObject) {
968
results.resolvedDocumentObjectName = String {
969
components[0].name.getString(),
971
results.flags.test(ResolveByIdentifier)};
972
results.propertyName = components[1].name.getString();
973
results.propertyIndex = 1;
974
results.getProperty(*this);
975
if(!results.resolvedProperty) {
976
// If the second component is not a property name, try to
977
// interpret the first component as the property name.
978
DocumentObject *sobj = nullptr;
979
results.resolvedProperty = resolveProperty(
981
components[0].name.toString().c_str(),
983
results.propertyType);
984
if(results.resolvedProperty) {
985
results.propertyName = components[0].name.getString();
986
results.resolvedDocument = owner->getDocument();
987
results.resolvedDocumentName = String(results.resolvedDocument->getName(), false, true);
988
results.resolvedDocumentObjectName = String(owner->getNameInDocument(), false, true);
989
results.resolvedDocumentObject = owner;
990
results.resolvedSubObject = sobj;
991
results.propertyIndex = 0;
995
else if (documentName.getString().empty()) {
996
/* No, assume component is a property, and get document object's name from owner */
997
results.resolvedDocument = owner->getDocument();
998
results.resolvedDocumentName = String(results.resolvedDocument->getName(), false, true);
999
results.resolvedDocumentObjectName = String(owner->getNameInDocument(), false, true);
1000
results.resolvedDocumentObject = owner->getDocument()->getObject(owner->getNameInDocument());
1001
results.propertyIndex = 0;
1002
results.propertyName = components[results.propertyIndex].name.getString();
1003
results.getProperty(*this);
1012
* @brief Find a document with the given name.
1013
* @param name Name of document
1014
* @return Pointer to document, or 0 if it is not found or not uniquely defined by name.
1017
Document * ObjectIdentifier::getDocument(String name, bool *ambiguous) const
1019
if (name.getString().empty())
1020
name = getDocumentName();
1022
App::Document * docById = nullptr;
1024
if(!name.isRealString()) {
1025
docById = App::GetApplication().getDocument(name.toString().c_str());
1026
if (name.isForceIdentifier())
1030
App::Document * docByLabel = nullptr;
1031
const std::vector<App::Document*> docs = App::GetApplication().getDocuments();
1033
for (auto doc : docs) {
1034
if (doc->Label.getValue() == name.getString()) {
1035
/* Multiple hits for same label? */
1037
if(ambiguous) *ambiguous = true;
1044
/* Not found on id? */
1046
return docByLabel; // Either not found at all, or on label
1048
/* Not found on label? */
1049
if (!docByLabel) /* Then return doc by id */
1052
/* docByLabel and docById could be equal; that is ok */
1053
if (docByLabel == docById)
1062
* @brief Get the document object for the object identifier.
1063
* @return Pointer to document object, or 0 if not found or uniquely defined.
1066
DocumentObject *ObjectIdentifier::getDocumentObject() const
1068
const App::Document * doc = getDocument();
1069
std::bitset<32> dummy;
1074
ResolveResults result(*this);
1076
return getDocumentObject(doc, result.resolvedDocumentObjectName, dummy);
1080
enum PseudoPropertyType {
1085
PseudoLinkPlacement,
1098
void ObjectIdentifier::getDepLabels(std::vector<std::string> &labels) const {
1099
getDepLabels(ResolveResults(*this),labels);
1102
void ObjectIdentifier::getDepLabels(
1103
const ResolveResults &result, std::vector<std::string> &labels) const
1105
if(!documentObjectName.getString().empty()) {
1106
if(documentObjectName.isRealString())
1107
labels.push_back(documentObjectName.getString());
1108
} else if(result.propertyIndex == 1)
1109
labels.push_back(components[0].name.getString());
1110
if(!subObjectName.getString().empty())
1111
PropertyLinkBase::getLabelReferences(labels,subObjectName.getString().c_str());
1114
ObjectIdentifier::Dependencies
1115
ObjectIdentifier::getDep(bool needProps, std::vector<std::string> *labels) const
1118
getDep(deps,needProps,labels);
1122
void ObjectIdentifier::getDep(Dependencies &deps, bool needProps, std::vector<std::string> *labels) const
1124
ResolveResults result(*this);
1126
getDepLabels(result,*labels);
1128
if(!result.resolvedDocumentObject)
1132
deps[result.resolvedDocumentObject];
1136
if(!result.resolvedProperty) {
1137
if(!result.propertyName.empty())
1138
deps[result.resolvedDocumentObject].insert(result.propertyName);
1142
Base::PyGILStateLocker lock;
1144
access(result, nullptr, &deps);
1146
catch (Py::Exception& e) {
1149
catch (Base::Exception &) {
1154
* @brief Get components as a string list.
1155
* @return List of strings.
1158
std::vector<std::string> ObjectIdentifier::getStringList() const
1160
std::vector<std::string> l;
1161
ResolveResults result(*this);
1163
if(!result.resolvedProperty || result.resolvedDocumentObject != owner) {
1164
if (documentNameSet)
1165
l.push_back(documentName.toString());
1167
if (documentObjectNameSet)
1168
l.push_back(documentObjectName.toString());
1170
if(!subObjectName.getString().empty()) {
1171
l.back() += subObjectName.toString();
1173
std::vector<Component>::const_iterator i = components.begin();
1174
while (i != components.end()) {
1175
std::ostringstream ss;
1177
l.push_back(ss.str());
1185
* @brief Construct the simplest possible object identifier relative to another.
1186
* @param other The other object identifier.
1187
* @return A new simplified object identifier.
1190
ObjectIdentifier ObjectIdentifier::relativeTo(const ObjectIdentifier &other) const
1192
ObjectIdentifier result(other.getOwner());
1193
ResolveResults thisresult(*this);
1194
ResolveResults otherresult(other);
1196
if (otherresult.resolvedDocument != thisresult.resolvedDocument)
1197
result.setDocumentName(std::move(thisresult.resolvedDocumentName), true);
1198
if (otherresult.resolvedDocumentObject != thisresult.resolvedDocumentObject)
1199
result.setDocumentObjectName(
1200
std::move(thisresult.resolvedDocumentObjectName), true, String(subObjectName));
1202
for (std::size_t i = thisresult.propertyIndex; i < components.size(); ++i)
1203
result << components[i];
1209
* @brief Parse a string to create an object identifier.
1211
* This method throws an exception if the string is invalid.
1213
* @param docObj Document object that will own this object identifier.
1214
* @param str String to parse
1215
* @return A new object identifier.
1218
ObjectIdentifier ObjectIdentifier::parse(const DocumentObject *docObj, const std::string &str)
1220
std::unique_ptr<Expression> expr(ExpressionParser::parse(docObj, str.c_str()));
1221
VariableExpression * v = freecad_dynamic_cast<VariableExpression>(expr.get());
1224
return v->getPath();
1226
FC_THROWM(Base::RuntimeError,"Invalid property specification.");
1229
std::string ObjectIdentifier::resolveErrorString() const
1231
ResolveResults result(*this);
1233
return result.resolveErrorString();
1237
* @brief << operator, used to add a component to the object identifier.
1238
* @param value Component object
1239
* @return Reference to itself.
1242
ObjectIdentifier &ObjectIdentifier::operator <<(const ObjectIdentifier::Component &value)
1244
components.push_back(value);
1249
ObjectIdentifier &ObjectIdentifier::operator <<(ObjectIdentifier::Component &&value)
1251
components.push_back(std::move(value));
1258
* @brief Get pointer to property pointed to by this object identifier.
1259
* @return Point to property if it is uniquely defined, or 0 otherwise.
1262
Property *ObjectIdentifier::getProperty(int *ptype) const
1264
ResolveResults result(*this);
1266
*ptype = result.propertyType;
1267
return result.resolvedProperty;
1270
Property *ObjectIdentifier::resolveProperty(const App::DocumentObject *obj,
1271
const char *propertyName, App::DocumentObject *&sobj, int &ptype) const
1273
if(obj && !subObjectName.getString().empty()) {
1274
sobj = obj->getSubObject(subObjectName.toString().c_str());
1280
static std::unordered_map<const char*,int,CStringHasher,CStringHasher> _props = {
1281
{"_shape",PseudoShape},
1282
{"_pla",PseudoPlacement},
1283
{"_matrix",PseudoMatrix},
1284
{"__pla",PseudoLinkPlacement},
1285
{"__matrix",PseudoLinkMatrix},
1286
{"_self",PseudoSelf},
1288
{"_part",PseudoPart},
1289
{"_re",PseudoRegex},
1290
{"_py", PseudoBuiltins},
1291
{"_math", PseudoMath},
1292
{"_coll", PseudoCollections},
1294
{"_cq",PseudoCadquery},
1296
auto it = _props.find(propertyName);
1297
if(it == _props.end())
1301
if(ptype != PseudoShape &&
1302
!subObjectName.getString().empty() &&
1303
!boost::ends_with(subObjectName.getString(),"."))
1307
return &const_cast<App::DocumentObject*>(obj)->Label; //fake the property
1310
return obj->getPropertyByName(propertyName);
1316
* @brief Create a canonical representation of an object identifier.
1318
* The main work is actually done by the property's virtual canonicalPath(...) method,
1319
* which is invoked by this call.
1321
* @return A new object identifier.
1324
ObjectIdentifier ObjectIdentifier::canonicalPath() const
1326
ObjectIdentifier res(*this);
1327
ResolveResults result(res);
1328
if(result.resolvedDocumentObject && result.resolvedDocumentObject!=owner) {
1329
res.owner = result.resolvedDocumentObject;
1332
res.resolveAmbiguity(result);
1333
if(!result.resolvedProperty || result.propertyType!=PseudoNone)
1335
return result.resolvedProperty->canonicalPath(res);
1338
static const std::map<std::string,std::string> *_DocumentMap;
1339
ObjectIdentifier::DocumentMapper::DocumentMapper(const std::map<std::string,std::string> &map)
1341
assert(!_DocumentMap);
1342
_DocumentMap = ↦
1345
ObjectIdentifier::DocumentMapper::~DocumentMapper()
1347
_DocumentMap = nullptr;
1351
* @brief Set the document name for this object identifier.
1353
* If force is true, the document name will always be included in the string representation.
1355
* @param name Name of document object.
1356
* @param force Force name to be set
1359
void ObjectIdentifier::setDocumentName(ObjectIdentifier::String &&name, bool force)
1361
if(name.getString().empty())
1363
documentNameSet = force;
1365
if(!name.getString().empty() && _DocumentMap) {
1366
if(name.isRealString()) {
1367
auto iter = _DocumentMap->find(name.toString());
1368
if(iter!=_DocumentMap->end()) {
1369
documentName = String(iter->second,true);
1373
auto iter = _DocumentMap->find(name.getString());
1374
if(iter!=_DocumentMap->end()) {
1375
documentName = String(iter->second,false,true);
1380
documentName = std::move(name);
1384
* @brief Get the document name from this object identifier
1386
* @return Document name as a String object.
1389
ObjectIdentifier::String ObjectIdentifier::getDocumentName() const
1391
ResolveResults result(*this);
1393
return result.resolvedDocumentName;
1397
* @brief Set the document object name of this object identifier.
1399
* If force is true, the document object will not be resolved dynamically from the
1400
* object identifier's components, but used as given by this method.
1402
* @param name Name of document object.
1403
* @param force Force name to be set.
1406
void ObjectIdentifier::setDocumentObjectName(ObjectIdentifier::String &&name, bool force,
1407
ObjectIdentifier::String &&subname, bool checkImport)
1410
name.checkImport(owner);
1411
subname.checkImport(owner,nullptr,&name);
1414
documentObjectName = std::move(name);
1415
documentObjectNameSet = force;
1416
subObjectName = std::move(subname);
1421
void ObjectIdentifier::setDocumentObjectName(const App::DocumentObject *obj, bool force,
1422
ObjectIdentifier::String &&subname, bool checkImport)
1424
if(!owner || !obj || !obj->isAttachedToDocument() || !obj->getDocument())
1425
FC_THROWM(Base::RuntimeError,"invalid object");
1428
subname.checkImport(owner,obj);
1433
localProperty = false;
1434
if(obj->getDocument() == owner->getDocument())
1435
setDocumentName(String());
1436
else if(!documentNameSet) {
1437
if(obj->getDocument() == owner->getDocument())
1438
setDocumentName(String());
1440
documentNameSet = true;
1441
documentName = String(obj->getDocument()->getName(),false,true);
1443
}else if(documentName.isRealString())
1444
documentName = String(obj->getDocument()->Label.getStrValue(),true);
1446
documentName = String(obj->getDocument()->getName(),false,true);
1448
documentObjectNameSet = force;
1449
documentObjectName = String(obj->getNameInDocument(),false,true);
1450
subObjectName = std::move(subname);
1457
* @brief Get the document object name
1458
* @return String with name of document object as resolved by object identifier.
1461
ObjectIdentifier::String ObjectIdentifier::getDocumentObjectName() const
1463
ResolveResults result(*this);
1465
return result.resolvedDocumentObjectName;
1468
bool ObjectIdentifier::hasDocumentObjectName(bool forced) const {
1469
return !documentObjectName.getString().empty() && (!forced || documentObjectNameSet);
1473
* @brief Get a string representation of this object identifier.
1474
* @return String representation.
1477
std::string ObjectIdentifier::String::toString(bool toPython) const
1480
return quote(str,toPython);
1485
void ObjectIdentifier::String::checkImport(const App::DocumentObject *owner,
1486
const App::DocumentObject *obj, String *objName)
1488
if(owner && owner->getDocument() && !str.empty() &&
1489
ExpressionParser::ExpressionImporter::reader()) {
1490
auto reader = ExpressionParser::ExpressionImporter::reader();
1491
if (obj || objName) {
1492
bool restoreLabel = false;
1493
str = PropertyLinkBase::importSubName(*reader,str.c_str(),restoreLabel);
1496
std::bitset<32> flags;
1497
obj = getDocumentObject(owner->getDocument(),*objName,flags);
1499
FC_ERR("Cannot find object " << objName->toString());
1504
PropertyLinkBase::restoreLabelReference(obj,str);
1508
else if (str.back()!='@') {
1509
str = reader->getName(str.c_str());
1512
str.resize(str.size()-1);
1513
auto mapped = reader->getName(str.c_str());
1514
auto objForMapped = owner->getDocument()->getObject(mapped);
1515
if (!objForMapped) {
1516
FC_ERR("Cannot find object " << str);
1520
forceIdentifier = false;
1521
str = objForMapped->Label.getValue();
1527
Py::Object ObjectIdentifier::access(const ResolveResults &result,
1528
Py::Object *value, Dependencies *deps) const
1530
if(!result.resolvedDocumentObject || !result.resolvedProperty ||
1531
(!subObjectName.getString().empty() && !result.resolvedSubObject))
1533
FC_THROWM(Base::RuntimeError, result.resolveErrorString()
1534
<< " in '" << toString() << "'");
1538
int ptype = result.propertyType;
1540
// NOTE! We do not keep reference of the imported module, assuming once
1541
// imported they'll live (because of sys.modules) till the application
1543
#define GET_MODULE(_name) do {\
1544
static PyObject *pymod;\
1546
pymod = PyImport_ImportModule(#_name);\
1548
Base::PyException::ThrowException();\
1552
pyobj = Py::Object(pymod);\
1555
size_t idx = result.propertyIndex+1;
1558
GET_MODULE(FreeCAD);
1561
GET_MODULE(FreeCADGui);
1566
case PseudoCadquery:
1567
GET_MODULE(freecad.fc_cadquery);
1572
case PseudoBuiltins:
1573
GET_MODULE(builtins);
1578
case PseudoCollections:
1579
GET_MODULE(collections);
1583
Py::Callable func(pyobj.getAttr("getShape"));
1585
tuple.setItem(0,Py::Object(result.resolvedDocumentObject->getPyObject(),true));
1586
if(result.subObjectName.getString().empty())
1587
pyobj = func.apply(tuple);
1590
dict.setItem("subname",Py::String(result.subObjectName.getString()));
1591
dict.setItem("needSubElement",Py::True());
1592
pyobj = func.apply(tuple,dict);
1597
auto obj = result.resolvedDocumentObject;
1599
case PseudoPlacement:
1601
case PseudoLinkPlacement:
1602
case PseudoLinkMatrix:
1603
obj->getSubObject(result.subObjectName.getString().c_str(),nullptr,&mat);
1608
if(result.resolvedSubObject)
1609
obj = result.resolvedSubObject;
1611
case PseudoPlacement:
1612
pyobj = Py::Placement(Base::Placement(mat));
1615
pyobj = Py::Matrix(mat);
1617
case PseudoLinkPlacement:
1618
case PseudoLinkMatrix: {
1619
auto linked = obj->getLinkedObject(true,&mat,false);
1620
if(!linked || linked==obj) {
1621
auto ext = obj->getExtensionByType<App::LinkBaseExtension>(true);
1623
ext->getTrueLinkedObject(true,&mat);
1625
if(ptype == PseudoLinkPlacement)
1626
pyobj = Py::Placement(Base::Placement(mat));
1628
pyobj = Py::Matrix(mat);
1632
pyobj = Py::Object(obj->getPyObject(),true);
1635
// NOTE! We cannot directly call Property::getPyObject(), but
1636
// instead, must obtain the property's python object through
1637
// DocumentObjectPy::getAttr(). Because, PyObjectBase has internal
1638
// attribute tracking only if we obtain attribute through
1639
// getAttr(). Without attribute tracking, we can't do things like
1641
// obj.Placement.Base.x = 10.
1643
// What happens is that the when Python interpreter calls
1645
// Base.setAttr('x', 10),
1647
// PyObjectBase will lookup Base's parent, i.e. Placement, and call
1649
// Placement.setAttr('Base', Base),
1651
// and in turn calls
1653
// obj.setAttr('Placement',Placement)
1655
// The tracking logic is implemented in PyObjectBase::__getattro/__setattro
1657
auto container = result.resolvedProperty->getContainer();
1659
&& container!=result.resolvedDocumentObject
1660
&& container!=result.resolvedSubObject)
1662
if(!container->isDerivedFrom(DocumentObject::getClassTypeId()))
1663
FC_WARN("Invalid property container");
1665
obj = static_cast<DocumentObject*>(container);
1667
pyobj = Py::Object(obj->getPyObject(),true);
1668
idx = result.propertyIndex;
1673
auto setPropDep = [deps](DocumentObject *obj, Property *prop, const char *propName) {
1676
if(prop && prop->getContainer()!=obj) {
1677
auto linkTouched = Base::freecad_dynamic_cast<PropertyBool>(
1678
obj->getPropertyByName("_LinkTouched"));
1680
propName = linkTouched->getName();
1682
auto propOwner = Base::freecad_dynamic_cast<DocumentObject>(prop->getContainer());
1689
auto &propset = (*deps)[obj];
1690
// inserting a blank name in the propset indicates the dependency is
1691
// on all properties of the corresponding object.
1692
if (propset.size() != 1 || !propset.begin()->empty()) {
1698
propset.insert(propName);
1704
App::DocumentObject *lastObj = result.resolvedDocumentObject;
1705
if(result.resolvedSubObject) {
1706
setPropDep(lastObj,nullptr,nullptr);
1707
lastObj = result.resolvedSubObject;
1709
if(ptype == PseudoNone)
1710
setPropDep(lastObj, result.resolvedProperty, result.resolvedProperty->getName());
1712
setPropDep(lastObj,nullptr,nullptr);
1715
if(components.empty())
1718
size_t count = components.size();
1722
for(;idx<count;++idx) {
1723
if(PyObject_TypeCheck(*pyobj, &DocumentObjectPy::Type))
1724
lastObj = static_cast<DocumentObjectPy*>(*pyobj)->getDocumentObjectPtr();
1726
const char *attr = components[idx].getName().c_str();
1727
auto prop = lastObj->getPropertyByName(attr);
1728
setPropDep(lastObj,prop,attr);
1731
pyobj = components[idx].get(pyobj);
1734
components[idx].set(pyobj,*value);
1735
return Py::Object();
1741
* @brief Get the value of the property or field pointed to by this object identifier.
1743
* All type of objects are supported. Some types are casted to FC native
1744
* type, including: Int, Float, String, Unicode String, and Quantities. Others
1745
* are just kept as Python object wrapped by App::any.
1747
* @param pathValue: if true, calls the property's getPathValue(), which is
1748
* necessary for Qunatities to work.
1750
* @return The value of the property or field.
1753
App::any ObjectIdentifier::getValue(bool pathValue, bool *isPseudoProperty) const
1755
ResolveResults rs(*this);
1757
if(isPseudoProperty) {
1758
*isPseudoProperty = rs.propertyType!=PseudoNone;
1759
if(rs.propertyType == PseudoSelf
1760
&& isLocalProperty()
1761
&& rs.propertyIndex+1 < (int)components.size()
1762
&& owner->getPropertyByName(components[rs.propertyIndex+1].getName().c_str()))
1764
*isPseudoProperty = false;
1768
if(rs.resolvedProperty && rs.propertyType==PseudoNone && pathValue)
1769
return rs.resolvedProperty->getPathValue(*this);
1771
Base::PyGILStateLocker lock;
1773
return pyObjectToAny(access(rs));
1774
}catch(Py::Exception &) {
1775
Base::PyException::ThrowException();
1780
Py::Object ObjectIdentifier::getPyValue(bool pathValue, bool *isPseudoProperty) const
1782
ResolveResults rs(*this);
1784
if(isPseudoProperty) {
1785
*isPseudoProperty = rs.propertyType!=PseudoNone;
1786
if(rs.propertyType == PseudoSelf
1787
&& isLocalProperty()
1788
&& rs.propertyIndex+1 < (int)components.size()
1789
&& owner->getPropertyByName(components[rs.propertyIndex+1].getName().c_str()))
1791
*isPseudoProperty = false;
1795
if(rs.resolvedProperty && rs.propertyType==PseudoNone && pathValue) {
1797
if(rs.resolvedProperty->getPyPathValue(*this,res))
1803
}catch(Py::Exception &) {
1804
Base::PyException::ThrowException();
1806
return Py::Object();
1810
* @brief Set value of a property or field pointed to by this object identifier.
1812
* This method uses Python to do the actual work. and a limited set of types that
1813
* can be in the App::any variable is supported: Base::Quantity, double,
1814
* char*, const char*, int, unsigned int, short, unsigned short, char, and unsigned char.
1816
* @param value Value to set
1819
void ObjectIdentifier::setValue(const App::any &value) const
1821
std::stringstream ss;
1822
ResolveResults rs(*this);
1824
FC_THROWM(Base::RuntimeError,"Cannot set pseudo property");
1826
Base::PyGILStateLocker lock;
1828
Py::Object pyvalue = pyObjectFromAny(value);
1829
access(rs,&pyvalue);
1830
}catch(Py::Exception &) {
1831
Base::PyException::ThrowException();
1835
const std::string &ObjectIdentifier::getSubObjectName(bool newStyle) const {
1836
if(newStyle && !shadowSub.first.empty())
1837
return shadowSub.first;
1838
if(!shadowSub.second.empty())
1839
return shadowSub.second;
1840
return subObjectName.getString();
1843
const std::string &ObjectIdentifier::getSubObjectName() const {
1844
return subObjectName.getString();
1847
void ObjectIdentifier::importSubNames(const ObjectIdentifier::SubNameMap &subNameMap)
1849
if(!owner || !owner->getDocument())
1851
ResolveResults result(*this);
1852
auto it = subNameMap.find(std::make_pair(result.resolvedDocumentObject,std::string()));
1853
if(it!=subNameMap.end()) {
1854
auto obj = owner->getDocument()->getObject(it->second.c_str());
1856
FC_ERR("Failed to find import object " << it->second << " from "
1857
<< result.resolvedDocumentObject->getFullName());
1860
documentNameSet = false;
1861
documentName.str.clear();
1862
if(documentObjectName.isRealString())
1863
documentObjectName.str = obj->Label.getValue();
1865
documentObjectName.str = obj->getNameInDocument();
1868
if(subObjectName.getString().empty())
1870
it = subNameMap.find(std::make_pair(
1871
result.resolvedDocumentObject,subObjectName.str));
1872
if(it==subNameMap.end())
1874
subObjectName = String(it->second,true);
1876
shadowSub.first.clear();
1877
shadowSub.second.clear();
1880
bool ObjectIdentifier::updateElementReference(ExpressionVisitor &v,
1881
App::DocumentObject *feature, bool reverse)
1883
assert(v.getPropertyLink());
1884
if(subObjectName.getString().empty())
1887
ResolveResults result(*this);
1888
if(!result.resolvedSubObject)
1890
if(v.getPropertyLink()->_updateElementReference(
1891
feature,result.resolvedDocumentObject,subObjectName.str,shadowSub,reverse)) {
1899
bool ObjectIdentifier::adjustLinks(ExpressionVisitor &v, const std::set<App::DocumentObject *> &inList) {
1900
ResolveResults result(*this);
1901
if(!result.resolvedDocumentObject)
1903
if(result.resolvedSubObject) {
1904
PropertyLinkSub prop;
1905
prop.setValue(result.resolvedDocumentObject, {subObjectName.getString()});
1906
if(prop.adjustLink(inList)) {
1908
documentObjectName = String(prop.getValue()->getNameInDocument(),false,true);
1909
subObjectName = String(prop.getSubValues().front(),true);
1917
bool ObjectIdentifier::isTouched() const {
1919
ResolveResults result(*this);
1920
if(result.resolvedProperty) {
1921
if(result.propertyType==PseudoNone)
1922
return result.resolvedProperty->isTouched();
1924
return result.resolvedDocumentObject->isTouched();
1930
void ObjectIdentifier::resolveAmbiguity() {
1931
if(!owner || !owner->isAttachedToDocument() || isLocalProperty() ||
1932
(documentObjectNameSet && !documentObjectName.getString().empty() &&
1933
(documentObjectName.isRealString() || documentObjectName.isForceIdentifier())))
1938
ResolveResults result(*this);
1939
resolveAmbiguity(result);
1942
void ObjectIdentifier::resolveAmbiguity(ResolveResults &result) {
1944
if(!result.resolvedDocumentObject)
1947
if(result.propertyIndex==1)
1948
components.erase(components.begin());
1950
String subname = subObjectName;
1951
if(result.resolvedDocumentObject == owner) {
1952
setDocumentObjectName(owner,false,std::move(subname));
1953
}else if(result.flags.test(ResolveByIdentifier))
1954
setDocumentObjectName(result.resolvedDocumentObject,true,std::move(subname));
1956
setDocumentObjectName(
1957
String(result.resolvedDocumentObject->Label.getStrValue(),true,false),true,std::move(subname));
1959
if(result.resolvedDocumentObject->getDocument() == owner->getDocument())
1960
setDocumentName(String());
1963
/** Construct and initialize a ResolveResults object, given an ObjectIdentifier instance.
1965
* The constructor will invoke the ObjectIdentifier's resolve() method to initialize the object's data.
1968
ObjectIdentifier::ResolveResults::ResolveResults(const ObjectIdentifier &oi)
1969
: propertyType(PseudoNone)
1974
std::string ObjectIdentifier::ResolveResults::resolveErrorString() const
1976
std::ostringstream ss;
1977
if (!resolvedDocument) {
1978
if(flags.test(ResolveAmbiguous))
1979
ss << "Ambiguous document name/label '"
1980
<< resolvedDocumentName.getString() << "'";
1982
ss << "Document '" << resolvedDocumentName.toString() << "' not found";
1983
} else if (!resolvedDocumentObject) {
1984
if(flags.test(ResolveAmbiguous))
1985
ss << "Ambiguous document object name '"
1986
<< resolvedDocumentObjectName.getString() << "'";
1988
ss << "Document object '" << resolvedDocumentObjectName.toString()
1990
} else if (!subObjectName.getString().empty() && !resolvedSubObject) {
1991
ss << "Sub-object '" << resolvedDocumentObjectName.getString()
1992
<< '.' << subObjectName.toString() << "' not found";
1993
} else if (!resolvedProperty) {
1994
if(propertyType != PseudoShape &&
1995
!subObjectName.getString().empty() &&
1996
!boost::ends_with(subObjectName.getString(),"."))
1998
ss << "Non geometry subname reference must end with '.'";
2000
ss << "Property '" << propertyName << "' not found";
2006
void ObjectIdentifier::ResolveResults::getProperty(const ObjectIdentifier &oi) {
2007
resolvedProperty = oi.resolveProperty(
2008
resolvedDocumentObject,propertyName.c_str(),resolvedSubObject,propertyType);