diff --git a/src/App/ObjectIdentifier.cpp b/src/App/ObjectIdentifier.cpp index 8e80b17ba0..b650897073 100644 --- a/src/App/ObjectIdentifier.cpp +++ b/src/App/ObjectIdentifier.cpp @@ -442,7 +442,7 @@ std::string ObjectIdentifier::toEscapedString() const return Base::Tools::escapedUnicodeFromUtf8(toString().c_str()); } -bool ObjectIdentifier::updateLabelReference(App::DocumentObject* obj, +bool ObjectIdentifier::updateLabelReference(const App::DocumentObject* obj, const std::string& ref, const char* newLabel) { @@ -574,7 +574,6 @@ ObjectIdentifier::Component::Component(String&& _name, , step(_step) {} - size_t ObjectIdentifier::Component::getIndex(size_t count) const { if (begin >= 0) { @@ -1412,7 +1411,7 @@ std::string ObjectIdentifier::String::toString(bool toPython) const void ObjectIdentifier::String::checkImport(const App::DocumentObject* owner, const App::DocumentObject* obj, - String* objName) + const String* objName) { if (owner && owner->getDocument() && !str.empty() && ExpressionParser::ExpressionImporter::reader()) { @@ -1454,7 +1453,7 @@ void ObjectIdentifier::String::checkImport(const App::DocumentObject* owner, } Py::Object -ObjectIdentifier::access(const ResolveResults& result, Py::Object* value, Dependencies* deps) const +ObjectIdentifier::access(const ResolveResults& result, const Py::Object* value, Dependencies* deps) const { if (!result.resolvedDocumentObject || !result.resolvedProperty || (!subObjectName.getString().empty() && !result.resolvedSubObject)) { @@ -1886,7 +1885,7 @@ void ObjectIdentifier::resolveAmbiguity() resolveAmbiguity(result); } -void ObjectIdentifier::resolveAmbiguity(ResolveResults& result) +void ObjectIdentifier::resolveAmbiguity(const ResolveResults& result) { if (!result.resolvedDocumentObject) { diff --git a/src/App/ObjectIdentifier.h b/src/App/ObjectIdentifier.h index 5b69c7ec0b..3484331d81 100644 --- a/src/App/ObjectIdentifier.h +++ b/src/App/ObjectIdentifier.h @@ -44,12 +44,38 @@ namespace App using any = boost::any; +/** + * @brief Extract a const reference from a boost::any object. + * + * This function is a wrapper around boost::any_cast that allows + * to extract a const reference from a boost::any object. + * + * @tparam T The type to extract. + * + * @param[in] value The boost::any object to extract from. + * + * @return A const reference to the extracted value. + * @throws boost::bad_any_cast if the type of the value does not match T. + */ template inline const T& any_cast(const boost::any& value) { return boost::any_cast(value); } +/** + * @brief Extract a mutable reference from a boost::any object. + * + * This function is a wrapper around boost::any_cast that allows + * to extract a mutable reference from a boost::any object. + * + * @tparam T The type to extract. + * + * @param[in] value The boost::any object to extract from. + * + * @return A const reference to the extracted value. + * @throws boost::bad_any_cast if the type of the value does not match T. + */ template inline T& any_cast(boost::any& value) { @@ -64,14 +90,14 @@ class ExpressionVisitor; /** * @brief Quote a string. - * + * * Quote an input string according to quoting rules for an expression: because * " and ' are used to designate inch and foot units, strings are quoted as - * <>. + * `<>`. * * @param[in] input The string to quote. - * @param[in] toPython If true, use Python quoting rules. Otherwise, use - * FreeCAD quoting rules. + * @param[in] toPython If true, use Python quoting rules. Otherwise, use + * FreeCAD quoting rules. * * @return The string quoted. */ @@ -89,28 +115,80 @@ AppExport std::string quote(const std::string& input, bool toPython = false); _t& operator=(_t&& other) +/** + * @brief A class that identifies properties in document objects. + * + * An object identifier is a data structure that identifies a (sub)properties + * in a document or document objects, or documents or document objects + * themselves. + */ class AppExport ObjectIdentifier { public: + /** + * @brief A helper class to maintain a mapping between document names. + * + * This class maps old document names to new document names and is used to + * provide a scope in which calls to setDocumentName() make use of the + * mapping stored here. + */ class AppExport DocumentMapper { public: + /** + * @brief Construct a DocumentMapper object. + * + * The constructor takes a map of document names and stores a pointer + * to it in a variable local to the compilation unit. The idea is to + * create a %DocumentMapper on the stack and when it goes out of scope, + * all is reset. During that time calls to setDocumentName() can be + * done, making use of the mapping provided with this constructor. + */ explicit DocumentMapper(const std::map&); + + /** + * @brief Destroy the DocumentMapper object. + * + * When the DocumentMapper goes out of scope, the pointer to the + * variable local to the compilation unit is set to `nullptr`. + */ ~DocumentMapper(); }; + /** + * @brief A class that represents a string in an ObjectIdentifier. + */ class String { friend class ObjectIdentifier; public: + /** + * @brief Construct a String object for object identifiers. + * + * @param[in] s The string to be used. + * @param[in] _isRealString If true, the string is a real string and should be + * quoted. Otherwise, it is a simple identifier. + * @param[in] _forceIdentifier If true, the string is a forced identifier. + */ String(const std::string& s = "", bool _isRealString = false, bool _forceIdentifier = false) : str(s) , isString(_isRealString) , forceIdentifier(_forceIdentifier) {} // explicit bombs + /** + * @brief Explicit move‐construct a String object for object identifiers. + * + * This constructor takes ownership of the provided string via move semantics, + * preventing an implicit conversion from temporary std::string. + * + * @param[in,out] s The string to be used (will be moved-from). + * @param[in] _isRealString If true, the string is a real string and should be + * quoted. Otherwise, it is a simple identifier. + * @param[in] _forceIdentifier If true, the string is a forced identifier. + */ explicit String(std::string&& s, bool _isRealString = false, bool _forceIdentifier = false) : str(std::move(s)) , isString(_isRealString) @@ -127,70 +205,166 @@ public: // Accessors - /** Returns the string */ + /** + * @brief Get the string as a `std::string`. + * + * @return The string as a `std::string`. + */ const std::string& getString() const { return str; } - /** Return true is string need to be quoted */ + /** + * @brief Test whether the string is a real string. + * + * A real string is a string that should be quoted. A simple identifier + * is a string that should not be quoted. + * + * @return True if the string is a real string, false otherwise. + */ bool isRealString() const { return isString; } + /** + * @brief Test whether the string is a forced identifier. + * + * A forced identifier is a string that should be treated as an + * identifier. + * + * @return True if the string is a forced identifier, false otherwise. + */ bool isForceIdentifier() const { return forceIdentifier; } - /** Returns a possibly quoted string */ /** * @brief Get a string representation of this object identifier. - * @return String representation. + * + * @param[in] toPython If true, use Python quoting rules. Otherwise, use + * FreeCAD quoting rules. + * @return The string representation. */ std::string toString(bool toPython = false) const; // Operators + /** + * @brief Explicitly convert to `std::string`. + * + * Returns a copy of the internal std::string. Because this operator is + * marked explicit, it is required to use a cast: + * `static_cast(myString)`. + * + * @return A copy of the string as `std::string`. + */ explicit operator std::string() const { return str; } + /** + * @brief Explicitly convert to C-style string. + * + * Returns a pointer to the internal null-terminated character array. + * The pointer remains valid as long as the String object is alive and + * unmodified. This operator is explicit, so it is required to use: + * `static_cast(myString)`. + * + * @return A const char* pointing at the internal string buffer. + */ explicit operator const char*() const { return str.c_str(); } + /** + * @brief Test for equality. + * + * Compares the underlying string values for exact equality. + * + * @param[in] other The %String to compare against. + * @return true if both strings have identical content; false otherwise. + */ bool operator==(const String& other) const { return str == other.str; } + /** + * @brief Test for inequality. + * + * Compares the underlying string values for inequality. + * + * @param[in] other The %String we want to compare to. + * @return true if this string does not have identical content to the + * other string; false otherwise. + */ bool operator!=(const String& other) const { return str != other.str; } + /** + * @brief Lexicographical greater-than-or-equal-to comparison. + * + * Determines if this string is lexicographically greater than or equal + * to the other string. + * + * @param[in] other The %String to compare against. + * @return true if this string is lexicographically greater than or + * equal to `other`; false otherwise. + */ bool operator>=(const String& other) const { return str >= other.str; } + /** + * @brief Lexicographical less-than comparison. + * + * Determines if this string precedes `other` in lexicographical order. + * + * @param[in] other The %String to compare against. + * @return true if this string is lexicographically less than `other`; false otherwise. + */ bool operator<(const String& other) const { return str < other.str; } + /** + * @brief Lexicographical greater-than comparison. + * + * Determines if this string follows `other` in lexicographical order. + * + * @param[in] other The %String to compare against. + * @return true if this string is lexicographically greater than `other`; false otherwise. + */ bool operator>(const String& other) const { return str > other.str; } + /** + * @brief Remaps and resolves identifier strings during import. + * + * Applies name mapping and, if “@” markers are present, defers label + * substitution to import-time via PropertyLinkBase::importSubName() and + * PropertyLinkBase::restoreLabelReference(). + * + * @param[in] owner The importing document’s owner object. + * @param[in] obj (Optional) Direct pointer to the target object. + * @param[in] objName (Optional) Name to look up the object if @p obj is null. + * + * @sa importSubName(), restoreLabelReference() + */ void checkImport(const App::DocumentObject* owner, const App::DocumentObject* obj = nullptr, - String* objName = nullptr); + const String* objName = nullptr); private: std::string str; @@ -199,11 +373,12 @@ public: }; /** - * @brief A component is a part of a Path object, and is used to either - * name a property or a field within a property. A component can be either - * a single entry, and array, or a map to other sub-fields. + * @brief A component is a part of an ObjectIdentifier. + * + * It is used to either name a property or a field within a property. A + * component can be either a single entry, and array, or a map to other + * sub-fields. */ - class AppExport Component { @@ -229,17 +404,29 @@ public: } /** - * @brief Construct a Component part - * @param _name Name of component - * @param _type Type; simple, array, range or map - * @param _begin Array index or beginning of a Range, or INT_MAX for other type. - * @param _end ending of a Range, or INT_MAX for other type. + * @brief Construct a Component part. + * + * @param[in] _name The name of the component. + * @param[in] _type The type: `SIMPLE`, `ARRAY`, `RANGE` or `MAP`. + * @param[in] begin The array index or beginning of a range, or `INT_MAX` for other types. + * @param[in] end The ending of a range, or `INT_MAX` for other types. + * @param[in] step The step of a range, or `1` for other types. */ Component(const String& _name = String(), typeEnum _type = SIMPLE, int begin = std::numeric_limits::max(), int end = std::numeric_limits::max(), int step = 1); // explicit bombs + + /** + * @brief Construct a Component with move semantics. + * + * @param[in,out] _name The name of the component. + * @param[in] _type The type: `SIMPLE`, `ARRAY`, `RANGE` or `MAP`. + * @param[in] begin The array index or beginning of a range, or `INT_MAX` for other types. + * @param[in] end The ending of a range, or `INT_MAX` for other types. + * @param[in] step The step of a range, or `1` for other types. + */ Component(String&& _name, typeEnum _type = SIMPLE, int begin = std::numeric_limits::max(), @@ -247,32 +434,43 @@ public: int step = 1); // explicit bombs /** - * @brief Create a simple component part with the given name - * @param _component Name of component. + * @brief Create a simple component with the given name. + * + * @param[in] _component The name of the component. * @return A new Component object. */ static Component SimpleComponent(const char* _component); /** - * @brief Create a simple component part with the given name - * @param _component Name of component. + * @brief Create a simple component with the given name. + * + * @param[in] _component The name of the component. * @return A new Component object. */ static Component SimpleComponent(const String& _component); + + /** + * @brief Create a simple component with move semantics. + * + * @param[in,out] _component The name of the component. + * @return A new Component object. + */ static Component SimpleComponent(String&& _component); /** - * @brief Create an array component with given name and index. - * @param _component Name of component - * @param _index Index of component + * @brief Create an array component an index. + * + * @param[in] _index The index of the component. * @return A new Component object. */ static Component ArrayComponent(int _index); /** * @brief Create a range component with given begin and end. - * @param _begin beginning index of the range - * @param _end ending index of the range + * + * @param[in] _begin The begin index of the range. + * @param[in] _end The end index of the range. + * @param[in] _step The step of the range. * @return A new Component object. */ static Component RangeComponent(int _begin, @@ -280,31 +478,60 @@ public: int _step = 1); /** - * @brief Create a map component with given name and key. - * @param _component Name of component - * @param _key Key of component + * @brief Create a map component with a given key. + * + * @param[in] _key The key of the component. * @return A new Component object. */ static Component MapComponent(const String& _key); + + /** + * @brief Create a map component with move semantics. + * + * Create a map component with a given key. + * + * @param[in] _key The key of the component. + * @return A new Component object. + */ static Component MapComponent(String&& _key); // Type queries + /** + * @brief Check if the component is a simple component. + * + * @return true if the component is a simple component, false otherwise. + */ bool isSimple() const { return type == SIMPLE; } + /** + * @brief Check if the component is a map component. + * + *@return true if the component is a map component, false otherwise. + */ bool isMap() const { return type == MAP; } + /** + * @brief Check if the component is an array component. + * + * @return true if the component is an array component, false otherwise. + */ bool isArray() const { return type == ARRAY; } + /** + * @brief Check if the component is a range component. + * + * @return true if the component is a range component, false otherwise. + */ bool isRange() const { return type == RANGE; @@ -314,29 +541,73 @@ public: /** * @brief Create a string representation of a component. - * @return A string representing the component. + * + * The string is appended to the output stream. + * + * @param[in,out] ss The output stream to write to. + * @param[in] toPython If true, use Python quoting rules. Otherwise, use + * FreeCAD quoting rules. */ void toString(std::ostream& ss, bool toPython = false) const; + /** + * @brief Get the name of the component. + * + * @return The name of the component. + */ const std::string& getName() const { return name.getString(); } + /** + * @brief Get the index of the component. + * + * @return The index of the component. + */ int getIndex() const { return begin; } + + /** + * @brief Get the index given a maximum count. + * + * This method interprets the member `begin` as a Python-style index + * that may be negative. Given the maximum count it returns a valid + * index. + * + * @param[in] count The number of elements in the target collection. + * @return The index in the range [0, count). + * @throws Base::IndexError If `begin` is out of bounds. + */ size_t getIndex(size_t count) const; + /** + * @brief Get the begin index of the component. + * + * @return The begin index of the component. + */ int getBegin() const { return begin; } + + /** + * @brief Get the end index of the component. + * + * @return The end index of the component. + */ int getEnd() const { return end; } + + /** + * @brief Get the step of the component. + * + * @return The step of the component. + */ int getStep() const { return step; @@ -345,15 +616,42 @@ public: // Operators /** - * @brief Comparison operator for Component objects. - * @param other The object we want to compare to. - * @return true if they are equal, false if not. + * @brief Test the component for equality. + * + * @param[in] other The object we want to compare to. + * @return true if the components are equal, false if not. */ bool operator==(const Component& other) const; + + /** + * @brief Lexicographical less-than comparison. + * + * @param[in] other The object we want to compare to. + * @return true if this component is lexicographically less than the other. + */ bool operator<(const Component& other) const; + /** + * @brief Get the value of the component given a Python object. + * + * @param[in] pyobj The Python object to get the value from. + * @return The value of the component. + */ Py::Object get(const Py::Object& pyobj) const; + + /** + * @brief Set the value of the component given a Python object. + * + * @param[in,out] pyobj The Python object to set the value to. + * @param[in] value The value to set. + */ void set(Py::Object& pyobj, const Py::Object& value) const; + + /** + * @brief Delete the value of the component given a Python object. + * + * @param[in,out] pyobj The Python object to delete the value from. + */ void del(Py::Object& pyobj) const; private: @@ -365,31 +663,69 @@ public: friend class ObjectIdentifier; }; + /** + * @brief Create a simple component with the given name. + * + * @param[in] _component The name of the component. + * @return A new Component object. + */ static Component SimpleComponent(const char* _component) { return Component::SimpleComponent(_component); } + /** + * @brief Create a simple component with the given name. + * + * @param[in] _component The name of the component. + * @return A new Component object. + */ static Component SimpleComponent(const String& _component) { return Component::SimpleComponent(_component); } + /** + * @brief Create a simple component with move semantics. + * + * @param[in,out] _component The name of the component. + * @return A new Component object. + */ static Component SimpleComponent(String&& _component) { return Component::SimpleComponent(std::move(_component)); } + /** + * @brief Create a simple component with the given name. + * + * @param[in] _component The name of the component. + * @return A new Component object. + */ static Component SimpleComponent(const std::string _component) { return Component::SimpleComponent(_component.c_str()); } + /** + * @brief Create an array component with the given index. + * + * @param[in] _index The index of the component. + * @return A new Component object. + */ static Component ArrayComponent(int _index) { return Component::ArrayComponent(_index); } + /** + * @brief Create a range component with given begin and end. + * + * @param[in] _begin The begin index of the range. + * @param[in] _end The end index of the range. + * @param[in] _step The step of the range. + * @return A new Component object. + */ static Component RangeComponent(int _begin, int _end = std::numeric_limits::max(), int _step = 1) @@ -397,31 +733,62 @@ public: return Component::RangeComponent(_begin, _end, _step); } + /** + * @brief Create a map component with a given key. + * + * @param[in] _key The key of the component. + * @return A new Component object. + */ static Component MapComponent(const String& _key) { return Component::MapComponent(_key); } + /** + * @brief Create a map component with move semantics. + * + * @param[in,out] _key The key of the component. + * @return A new Component object. + */ static Component MapComponent(String&& _key) { return Component::MapComponent(_key); } /** - * @brief Construct an ObjectIdentifier object, given an owner and a single-value property. - * @param _owner Owner of property. - * @param property Name of property. + * @brief Construct an ObjectIdentifier object. + * + * Construct an ObjectIdentifier object given an owner and a single-value + * property, possibly with an array index. + * + * @param[in] _owner The owner of the property. + * @param[in] property The name of the property. + * @param[in] index The index into the array. + * @throw Base::RuntimeError if the owner is not a document object. */ explicit ObjectIdentifier(const App::PropertyContainer* _owner = nullptr, const std::string& property = std::string(), int index = std::numeric_limits::max()); + /** + *@brief Construct an ObjectIdentifier object. + * + * @param[in] _owner The owner of the property. + * @param[in] localProperty If true, the property is a local property. + * @throw Base::RuntimeError if the owner is not a document object. + */ ObjectIdentifier(const App::PropertyContainer* _owner, bool localProperty); /** - * @brief Construct an ObjectIdentifier object given a property. The property is assumed to be - * single-valued. - * @param prop Property to construct object identifier for. + * @brief Construct an ObjectIdentifier object given a property. + * + * The property is assumed to be single-valued but may have an array index. + * + * @param[in] prop The property to construct object identifier for. + * @param[in] index The index into the array. + * + * @throw Base::RuntimeError if the owner is not a document object or if + * the property does not have a name. */ ObjectIdentifier(const App::Property& prop, int index = std::numeric_limits::max()); // explicit bombs @@ -442,21 +809,35 @@ public: return *this; } + /// Destruct an ObjectIdentifier object. virtual ~ObjectIdentifier() = default; + /** + * @brief Get the owner of this object identifier. + * + * @return The owner of this object identifier. + */ App::DocumentObject* getOwner() const { return owner; } - // Components + /** + * @brief Add a component to this object identifier. + * + * @param[in] c The component to add. + */ void addComponent(const Component& c) { components.push_back(c); _cache.clear(); } - // Components + /** + * @brief Add a component to this object identifier with move semantics. + * + * @param[in] c The component to add. + */ void addComponent(Component&& c) { components.push_back(std::move(c)); @@ -465,10 +846,16 @@ public: /** * @brief Get the name of the property. - * @return Name + * @return The name of the property. */ std::string getPropertyName() const; + /** + * @brief Add components to this object identifier. + * + * @tparam C A container type that supports iterators. + * @param[in] cs The components to add. + */ template void addComponents(const C& cs) { @@ -476,32 +863,69 @@ public: } /** - * @brief Get Component at given index \a i. - * @param i: Index to get - * @param idx: optional return of adjusted component index + * @brief Get a component given an index. + * + * @param[in] i: The index of the component. + * @param[out] idx: Optional return of an adjusted component index. * @return A component. */ const Component& getPropertyComponent(int i, int* idx = nullptr) const; + /** + * @brief Set a component at an index with move semantics. + * + * @param[in] idx: The index to store the component. + * @param[in,out] comp: The component to set. + */ void setComponent(int idx, Component&& comp); + + /** + * @brief Set a component at an index. + * + * @param[in] idx: The index to store the component. + * @param[in] comp: The component to set. + */ void setComponent(int idx, const Component& comp); + /** + * @brief Get the property components of this object identifier. + * + * @return A vector of components of properties. + */ std::vector getPropertyComponents() const; + + /** + * @brief Get the components of this object identifier. + * + * @return A vector of components. + */ const std::vector& getComponents() const { return components; } + /** + * @brief Get a string representation of the subpath. + * + * @param[in] toPython If true, use Python quoting rules. Otherwise, use + * FreeCAD quoting rules. + * + * @return The string representation of the subpath. + */ std::string getSubPathStr(bool toPython = false) const; /** - * @brief Return number of components. - * @return Number of components in this identifier. + * @brief Return the number of components. + * + * @return The number of components in this identifier. */ int numComponents() const; /** - * @brief Compute number of sub components, i.e excluding the property. + * @brief Compute the number of sub components. + * + * Compute the number of sub components, meaning that this excludes the property. + * * @return Number of components. */ int numSubComponents() const; @@ -509,36 +933,64 @@ public: /** * @brief Create a string representation of this object identifier. * - * An identifier is written as document#documentobject.property.subproperty1...subpropertyN - * document# may be dropped; it is assumed to be within owner's document. If documentobject is - * dropped, the property is assumed to be owned by the owner specified in the object identifiers + * An identifier is written as + * `document#documentobject.property.subproperty1...subpropertyN`. The + * string `document#` may be dropped; in that case it is assumed to be + * within owner's document. If `documentobject` is dropped, the property + * is assumed to be owned by the owner specified in the object identifiers * constructor. * - * @return A string + * @return A string representation of the object identifier. */ const std::string& toString() const; + /** + * @brief Create a persistent string representation of this object identifier. + * + * The persistent string representation is used where the object identifier + * is required to survive import and export. + * + * @see toString() + * + * @return A persistent string representation of the object identifier. + */ std::string toPersistentString() const; /** - * @brief Escape toString representation so it is suitable for being embedded in a python command. - * @return Escaped string. + * @brief Create an escapedstring representation of this object identifier. + * + * The escaped string representation is suitable for being embedded in a + * Python command. + * + * @return The escaped string representation. */ std::string toEscapedString() const; + /** + * @brief Wether the property of the object identifier is touched. + * + * This method is used to determine if the property that this object + * identifier represents is touched. + * + * @return true if the property of the object identifier is touched, false + * otherwise. + */ bool isTouched() const; /** - * @brief Get pointer to property pointed to by this object identifier. - * @return Point to property if it is uniquely defined, or 0 otherwise. + * @brief Get the property this object identifier represents. + * + * @param[out] ptype Optional return of the property type. + * + * @return A pointer to property if it is uniquely defined, or `nullptr` otherwise. */ App::Property* getProperty(int* ptype = nullptr) const; /** - * @brief Create a canonical representation of an object identifier. + * @brief Create a canonical representation of the object identifier. * - * The main work is actually done by the property's virtual canonicalPath(...) method, - * which is invoked by this call. + * The main work is actually done by the property's virtual + * Property::canonicalPath() method that is invoked by this call. * * @return A new object identifier. */ @@ -549,131 +1001,249 @@ public: /** * @brief Set the document name for this object identifier. * - * If force is true, the document name will always be included in the string representation. + * If @p force is true, the document name will always be included in the + * string representation. * - * @param name Name of document object. - * @param force Force name to be set + * @param[in,out] name The name of the document. + * @param[in] force Force the name to be set. */ void setDocumentName(String&& name, bool force = false); /** * @brief Get the document name from this object identifier * - * @return Document name as a String object. + * @return The document name as a String object. */ String getDocumentName() const; /** * @brief Set the document object name of this object identifier. * - * If force is true, the document object will not be resolved dynamically from the - * object identifier's components, but used as given by this method. + * If force is true, the document object will not be resolved dynamically + * from the object identifier's components, but used as given by this + * method. * - * @param name Name of document object. - * @param force Force name to be set. + * This function uses move semantics on @p name and @p subname. + * + * @param[in,out] name The name of document object. + * @param[in] force Force the name to be set. + * @param[in,out] subname The name of the subobject. + * @param[in] checkImport If true, check for import. */ void setDocumentObjectName(String&& name, bool force = false, String&& subname = String(), bool checkImport = false); + /** + * @brief Set the document object name of this object identifier. + * + * If force is true, the document object will not be resolved dynamically + * from the object identifier's components, but used as given by this method. + * + * @param[in] obj The document object which name is set in the object identifier. + * @param[in] force Force the name to be set. + * @param[in,out] subname The name of the subobject using move semantics. + * @param[in] checkImport If true, check for import. + */ void setDocumentObjectName(const App::DocumentObject* obj, bool force = false, String&& subname = String(), bool checkImport = false); + /** + * @brief Whether the object identifier has a document object name. + * + * This method checks if the object identifier has a document object name + * and optionally checks if the name was forced. + * + * @param[in] forced If true, check if the name was forced. + * @return true if the object identifier has a document object name, false + * otherwise. + */ bool hasDocumentObjectName(bool forced = false) const; + /** + * @brief Test whether the property is local. + * + * This method checks whether the property this object identifier + * represents is a local property. + * + * @return true if the property is local, false otherwise. + */ bool isLocalProperty() const { return localProperty; } /** - * @brief Get the document object name - * @return String with name of document object as resolved by object identifier. + * @brief Get the document object name. + * + * @return String with name of document object as resolved by the object + * identifier. */ String getDocumentObjectName() const; + /** + * @brief Get the subobject name. + * + * @param[in] newStyle If true, use the new style of subobject name. + * @return The subobject name. + */ const std::string& getSubObjectName(bool newStyle) const; + + /** + * @brief Get the subobject name. + * + * @return The subobject name. + */ const std::string& getSubObjectName() const; + /** + * @brief A type for a map of subobject names. + * + * The map is a map with keys of a pair of document object and a + * subobject name to the subobject name for the imported object. + */ using SubNameMap = std::map, std::string>; + + /** + * @brief Import subnames from a map of subnames. + * + * Given a map of subnames from the linked subobject to the imported + * subobject, this method will update the subnames in the object + * identifier. + * + * @param[in] subNameMap The map of subnames to import. + */ void importSubNames(const SubNameMap& subNameMap); - bool updateLabelReference(App::DocumentObject*, const std::string&, const char*); + /** + * @brief Update the label reference. + * + * This method updates the label reference of the object identifier. + * + * @param[in] obj The document object that owns the label. + * @param[in] ref The old label reference. + * @param[in] newLabel The new label to set. + */ + bool updateLabelReference(const App::DocumentObject* obj, + const std::string& ref, + const char* newLabel); + /** + * @brief Relabel the document name. + * + * This method relabels the document name of the object identifier as part + * of a visit of the ExpressionVisitor. + * + * @param[in,out] v The expression visitor. + * @param[in] oldLabel The old label to relabel. + * @param[in] newLabel The new label to set. + */ bool relabeledDocument(ExpressionVisitor& v, const std::string& oldLabel, const std::string& newLabel); - /** Type for storing dependency of an ObjectIdentifier + /** + * @brief A type for storing dependencies of an ObjectIdentifier. * - * The dependency is a map from document object to a set of property names. - * An object identifier may references multiple objects using syntax like - * 'Part.Group[0].Width'. + * It is a map from document object to a set of property names. An object + * identifier may references multiple objects using syntax like + * `%Part.%Group[0].Width`. * - * Also, we use set of string instead of set of Property pointer, because - * the property may not exist at the time this ObjectIdentifier is + * Additionally, we use a set of strings instead of set of Properties, + * because the property may not exist at the time this ObjectIdentifier is * constructed. */ using Dependencies = std::map>; - /** Get dependencies of this object identifier + /** + * @brief Get the dependencies of this object identifier. * - * @param needProps: whether need property dependencies. - * @param labels: optional return of any label references. + * @param[in] needProps: Whether we need property dependencies. + * @param[out] labels: Optional return of any label references. * - * In case of multi-object references, like 'Part.Group[0].Width', if no + * In case of multi-object references, like `%Part.%Group[0].Width`, if no * property dependency is required, then this function will only return the * first referred object dependency. Or else, all object and property * dependencies will be returned. */ Dependencies getDep(bool needProps, std::vector* labels = nullptr) const; - /** Get dependencies of this object identifier + /** + * @brief Get the dependencies of this object identifier. * - * @param deps: returns the dependencies. - * @param needProps: whether need property dependencies. - * @param labels: optional return of any label references. + * @param[in,out] deps: Returns the dependencies. + * @param[in] needProps: Whether need property dependencies. + * @param[out] labels: Optional return of any label references. * - * In case of multi-object references, like 'Part.Group[0].Width', if no - * property dependency is required, then this function will only return the - * first referred object dependency. Or else, all object and property - * dependencies will be returned. + * @see ObjectIdentifier::getDep(bool,std::vector*) const */ void getDep(Dependencies& deps, bool needProps, std::vector* labels = nullptr) const; - /// Returns all label references + /** + * @brief Returns all label references in the object identifier. + * + * @param[in,out] labels The container in which the labels are returned. + */ void getDepLabels(std::vector& labels) const; /** * @brief Find a document with the given name. - * @param name Name of document - * @return Pointer to document, or 0 if it is not found or not uniquely defined by name. + * + * This method will search for a document with the given name. If @p name + * is not provided, the document of the object identifier is returned. If + * @p ambiguous is not `nullptr`, the method will return whether the found + * document is unique. + * + * @param[in] name The name of the document. + * @param[out] ambiguous If true, the document is not uniquely defined by name. + * + * @return The found document, or `nullptr` if it is not found or not + * uniquely defined by name. */ App::Document* getDocument(String name = String(), bool* ambiguous = nullptr) const; /** * @brief Get the document object for the object identifier. - * @return Pointer to document object, or 0 if not found or uniquely defined. + * @return The document object, or `nullptr` if not found or uniquely defined. */ App::DocumentObject* getDocumentObject() const; /** - * @brief Get components as a string list. - * @return List of strings. + * @brief Get the parts of the object identifier as a string list. + * + * The parts include the document name, the document object name, the + * subobject name, and the various components. + * + * @return The list of strings representing the various components. */ std::vector getStringList() const; /** * @brief Construct the simplest possible object identifier relative to another. - * @param other The other object identifier. + * + * @param[in] other The other object identifier. * @return A new simplified object identifier. */ App::ObjectIdentifier relativeTo(const App::ObjectIdentifier& other) const; + /** + * @brief Replace an object in the object identifier. + * + * This method replaces an object in the object identifier with another + * object. It is used to create a new object identifier when the document + * object is replaced. + * + * @param[out] res The resulting object identifier. + * @param[in] parent The parent document object. + * @param[in] oldObj The old document object to replace. + * @param[in] newObj The new document object to replace with. + * + * @return true if the object was replaced, false otherwise. + */ bool replaceObject(ObjectIdentifier& res, const App::DocumentObject* parent, App::DocumentObject* oldObj, @@ -682,30 +1252,48 @@ public: // Operators /** - * @brief << operator, used to add a component to the object identifier. - * @param value Component object - * @return Reference to itself. + * @brief Operator to add a component to the object identifier. + * + * @param[in] value Component object + * + * @return A reference to itself. */ App::ObjectIdentifier& operator<<(const Component& value); + + /** + * @brief Operator to add a component to the object identifier. + * + * This method uses move semantics. + * + * @param[in,out] value Component object + * + * @return A reference to itself. + */ App::ObjectIdentifier& operator<<(Component&& value); /** - * @brief Compare object identifier with \a other. - * @param other Other object identifier. - * @return true if they are equal. + * @brief Compare object identifiers for equality. + * + * @param[in] other The other object identifier. + * + * @return true if they are equal, false otherwise. */ bool operator==(const ObjectIdentifier& other) const; /** - * @brief Compare object identifier with \a other. - * @param other Other object identifier - * @return true if they differ from each other. + * @brief Compare object identifiers for inequality. + * + * @param[in] other The other object identifier. + * + * @return true if they differ from each other, false otherwise. */ bool operator!=(const ObjectIdentifier& other) const; /** - * @brief Compare object identifier with other. - * @param other Other object identifier. + * @brief Lexicographical less-than comparison. + * + * @param[in] other The other object identifier. + * * @return true if this object is less than the other. */ bool operator<(const ObjectIdentifier& other) const; @@ -713,32 +1301,50 @@ public: // Getter /** - * @brief Get the value of the property or field pointed to by this object identifier. + * @brief Get the value of the property or field pointed to by this object + * identifier. * * All type of objects are supported. Some types are casted to FC native - * type, including: Int, Float, String, Unicode String, and Quantities. Others + * type, including: Int, Float, %String, Unicode %String, and Quantities. Others * are just kept as Python object wrapped by App::any. * - * @param pathValue: if true, calls the property's getPathValue(), which is - * necessary for Qunatities to work. + * @param[in] pathValue: if true, calls the property's getPathValue(), which is + * necessary for quantities to work. + * @param[in] isPseudoProperty: if not `nullptr`, set to true if the property is a + * pseudo property. * * @return The value of the property or field. */ App::any getValue(bool pathValue = false, bool* isPseudoProperty = nullptr) const; + /** + * @brief Get the value of the property or field pointed to by this object + * identifier. + * + * @see ObjectIdentifier::getValue(). In contrast, this method + * returns a Python %object. + * + * @param[in] pathValue: if true, calls the property's getPyPathValue(), which is + * necessary for quantities to work. + * @param[in] isPseudoProperty: if not `nullptr`, set to true if the property is a + * pseudo property. + * + * @return The value of the property or field. + */ Py::Object getPyValue(bool pathValue = false, bool* isPseudoProperty = nullptr) const; // Setter: is const because it does not alter the object state, // but does have an aiding effect. /** - * @brief Set value of a property or field pointed to by this object identifier. + * @brief Set the value of a property or field pointed to by this object identifier. * - * This method uses Python to do the actual work. and a limited set of types that - * can be in the App::any variable is supported: Base::Quantity, double, - * char*, const char*, int, unsigned int, short, unsigned short, char, and unsigned char. + * This method uses Python to do the actual work and a limited set of types + * that can be in the `App::any` variable are supported: `Base::Quantity`, + * `double`, `char*`, `const char*`, `int`, `unsigned int`, `short`, `unsigned short`, + * `char`, and `unsigned char`. * - * @param value Value to set + * @param[in] value The value to set. */ void setValue(const App::any& value) const; @@ -747,36 +1353,99 @@ public: /** * @brief Parse a string to create an object identifier. * - * This method throws an exception if the string is invalid. + * @param[in] docObj Document object that will own this object identifier. + * @param[in] str String to parse * - * @param docObj Document object that will own this object identifier. - * @param str String to parse * @return A new object identifier. + * @throw Base::RuntimeError if the string is invalid. */ static ObjectIdentifier parse(const App::DocumentObject* docObj, const std::string& str); + /** + * @brief Acquire the error string after name resolution of the object identifier. + * + * @return The error string. + */ std::string resolveErrorString() const; + /** + * @brief Adjust the links of the object identifier. + * + * This method will adjust the links of the object identifier and is part + * of a visit from an expression visitor. This is typically necessary when + * a link object is moved. The @p inList is a list of dependencies of the + * object for which the visitor was started. + * + * @param[in,out] v The expression visitor. + * @param[in] inList The list of dependencies. + * + * @return true if the links were adjusted, false otherwise. + */ bool adjustLinks(ExpressionVisitor& v, const std::set& inList); + /** + * @brief Update the element reference of the object identifier. + * + * This method is part of the expression visitor and is used to update the + * element reference of object identifiers in case of a geometry element + * reference change due to geometry model changes. + * + * @see PropertyLinkBase::_updateElementReference() + * + * @param[in,out] v The expression visitor. + * @param[in,out] feature If given, then only update element references for this + * feature, otherwise update geometry element references. + * @param[in] reverse If true, use the old style before the new style. If + * false the other way around. + * + * @return true if the element reference was updated, false otherwise. + */ bool updateElementReference(ExpressionVisitor& v, App::DocumentObject* feature = nullptr, bool reverse = false); + + /// Resolve ambiguity in the object identifier. void resolveAmbiguity(); + /** + * @brief Verify the object identifier. + * + * This method will verify the object identifier against property @p prop. + * + * @param[in] prop The property to verify against. + * @param[in] silent If true, do not throw an exception. + * + * @return true if verification succeeded, false otherwise. + * @throw Base::ValueError if there is an invalid property path. + */ bool verify(const App::Property& prop, bool silent = false) const; + /** + * @brief Compute the hash of the object identifier. + * + * This method computes the hash of the object identifier. The hash is + * computed from the string representation of the object identifier. + * + * @return The hash of the object identifier. + */ std::size_t hash() const; protected: + /** + * @brief A structure to hold the results of resolving an ObjectIdentifier. + * + * This structure is used to hold the results of resolving an object + * identifier. + */ struct ResolveResults { - /** Construct and initialize a ResolveResults object, given an ObjectIdentifier instance. + /** + * @brief Construct and initialize a ResolveResults object, given an ObjectIdentifier instance. * - * The constructor will invoke the ObjectIdentifier's resolve() method to initialize the object's - * data. + * The constructor will invoke the ObjectIdentifier's resolve() method + * to initialize the object's data. */ explicit ResolveResults(const ObjectIdentifier& oi); @@ -792,12 +1461,38 @@ protected: int propertyType {0}; std::bitset<32> flags; + /** + * @brief Acquire the error string after name resolution. + * + * @return The error string. + */ std::string resolveErrorString() const; + + /** + * @brief Obtain the property of the object identifier for this ResolveResults. + * + * @param[in] oi The object identifier. + */ void getProperty(const ObjectIdentifier& oi); }; + + /// Provide access to the internals of ObjectIdentifier. friend struct ResolveResults; + /** + * @brief Resolve the property of the object identifier. + * + * This method will resolve the property of the object identifier and + * return the property type. + * + * @param[in] obj The document object that owns the property. + * @param[in] propertyName The name of the property to resolve. + * @param[out] sobj The sub object. + * @param[out] ptype The type of the property. + * + * @return The property or `nullptr` if not resolved. + */ App::Property* resolveProperty(const App::DocumentObject* obj, const char* propertyName, App::DocumentObject*& sobj, @@ -805,65 +1500,128 @@ protected: /** * @brief Get sub field part of a property as a string. - * @return String representation of path. + * + * @param[in,out] ss The output stream to write to. + * @param[in] result The resolve results. + * @param[in] toPython If true, use Python quoting rules. Otherwise, use + * FreeCAD quoting rules. */ void getSubPathStr(std::ostream& ss, const ResolveResults& result, bool toPython = false) const; + /** + * @brief Access the value of the property or field pointed to by this + * object. + * + * This method can either set or get a value of the property or field of + * the object identifier given a ResolveResults. If @p deps is not + * `nullptr`, the dependencies are updated. + * + * @param[in] rs The resolve results. + * @param[in] value The value to set. + * @param[in,out] deps The dependencies to set. + */ Py::Object access(const ResolveResults& rs, - Py::Object* value = nullptr, + const Py::Object* value = nullptr, Dependencies* deps = nullptr) const; /** - * @brief Resolve the object identifier to a concrete document, documentobject, and property. + * @brief Resolve the object identifier to a concrete document, document + * object, and property. * - * This method is a helper method that fills out data in the given ResolveResults object. + * This method is a helper method that fills out data in the given + * ResolveResults object. * + * @param[in,out] results The ResolveResults object to fill out. */ void resolve(ResolveResults& results) const; - void resolveAmbiguity(ResolveResults& results); /** - * @brief Search for the document object given by name in doc. + * @brief Resolve ambiguity in the object identifier. * - * Name might be the internal name or a label. In any case, it must uniquely define - * the document object. + * Given a ResolveResults, the ambiguities in the object identifier are resolved. * - * @param doc Document to search - * @param name Name to search for. - * @return Pointer to document object if a unique pointer is found, 0 otherwise. + * @param[in] results The result of the resolve method. */ - static App::DocumentObject* - getDocumentObject(const App::Document* doc, const String& name, std::bitset<32>& flags); + void resolveAmbiguity(const ResolveResults& results); + /** + * @brief Returns all label references in the object identifier. + * + * @param[in] result The result of the resolve method. + * @param[in,out] labels The container in which the labels are returned. + */ void getDepLabels(const ResolveResults& result, std::vector& labels) const; + /// The owner of the object identifier. App::DocumentObject* owner; + /// The document name that this object identifier refers to. String documentName; + /// The document object name that this object identifier refers to. String documentObjectName; + /// The sub object name that this object identifier refers to. String subObjectName; + /** + * @brief The shadow sub-element names that this object identifier refers to. + * + * This contains both the new and old style sub-element names. + */ ElementNamePair shadowSub; + /// The components of the object identifier. std::vector components; + /// Whether a document name is forced set. bool documentNameSet; + /// Whether a document object name is forced set. bool documentObjectNameSet; + /// Whether the property is local. bool localProperty; +private: + static App::DocumentObject* + getDocumentObject(const App::Document* doc, const String& name, std::bitset<32>& flags); + private: std::string _cache; // Cached string represstation of this identifier std::size_t _hash; // Cached hash of this string }; +/** + * @brief Hash function for ObjectIdentifier. + * + * @param[in] path The object identifier to hash. + * + * @return The hash value of the object identifier. + */ inline std::size_t hash_value(const App::ObjectIdentifier& path) { return path.hash(); } -/** Helper function to convert Python object to/from App::any - * - * WARNING! Must hold Python global interpreter lock before calling these - * functions - */ //@{ +/** + * @brief Helper function to convert Python object to App::any + * + * @warning Must hold Python global interpreter lock before calling these + * functions + * + * @param[in] pyobj The Python object to convert. + * @param[in] check If true, check if the object is convertible to App::any, + * otherwise just return as is. + * + * @return The converted App::any object. + * @throw Base::ValueError if the object is not convertible to a unicode string. + */ App::any AppExport pyObjectToAny(Py::Object pyobj, bool check = true); + +/** + * @brief Helper function to convert Python object from App::any + * + * @warning Must hold Python global interpreter lock before calling these + * functions + * + * @param[in] value The App::any object to convert. + * @return The converted Python object. + * @throw Base::ExpressionError if the value has an unknown type. + */ Py::Object AppExport pyObjectFromAny(const App::any& value); //@} } // namespace App diff --git a/src/App/PropertyExpressionEngine.cpp b/src/App/PropertyExpressionEngine.cpp index dfbab08ea0..9f4a9dd56b 100644 --- a/src/App/PropertyExpressionEngine.cpp +++ b/src/App/PropertyExpressionEngine.cpp @@ -406,7 +406,7 @@ void PropertyExpressionEngine::buildGraphStructures( * @return New ObjectIdentifier */ -ObjectIdentifier PropertyExpressionEngine::canonicalPath(const ObjectIdentifier& p) const +ObjectIdentifier PropertyExpressionEngine::canonicalPath(const ObjectIdentifier& oid) const { DocumentObject* docObj = freecad_cast(getContainer()); @@ -416,24 +416,24 @@ ObjectIdentifier PropertyExpressionEngine::canonicalPath(const ObjectIdentifier& } int ptype; - Property* prop = p.getProperty(&ptype); + Property* prop = oid.getProperty(&ptype); - // p pointing to a property...? + // oid pointing to a property...? if (!prop) { - throw Base::RuntimeError(p.resolveErrorString().c_str()); + throw Base::RuntimeError(oid.resolveErrorString().c_str()); } if (ptype || prop->getContainer() != getContainer()) { - return p; + return oid; } // In case someone calls this with p pointing to a PropertyExpressionEngine for some reason if (prop->isDerivedFrom(PropertyExpressionEngine::classTypeId)) { - return p; + return oid; } // Dispatch call to actual canonicalPath implementation - return p.canonicalPath(); + return oid.canonicalPath(); } /** diff --git a/src/App/PropertyLinks.cpp b/src/App/PropertyLinks.cpp index 3a1d4f8133..128c07a274 100644 --- a/src/App/PropertyLinks.cpp +++ b/src/App/PropertyLinks.cpp @@ -189,7 +189,7 @@ void PropertyLinkBase::checkLabelReferences(const std::vector& subs std::string PropertyLinkBase::updateLabelReference(const App::DocumentObject* parent, const char* subname, - App::DocumentObject* obj, + const App::DocumentObject* obj, const std::string& ref, const char* newLabel) { diff --git a/src/App/PropertyLinks.h b/src/App/PropertyLinks.h index edf2d6b098..587f0d22dd 100644 --- a/src/App/PropertyLinks.h +++ b/src/App/PropertyLinks.h @@ -583,7 +583,7 @@ public: */ static std::string updateLabelReference(const App::DocumentObject* linked, const char* subname, - App::DocumentObject* obj, + const App::DocumentObject* obj, const std::string& ref, const char* newLabel); //@}