Files
create/src/App/ObjectIdentifier.h

1648 lines
54 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2015 Eivind Kvedalen <eivind@kvedalen.name> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#ifndef APP_PATH_H
#define APP_PATH_H
#include <bitset>
#include <climits>
#include <map>
#include <limits>
#include <set>
#include <string>
#include <vector>
#include <boost/any.hpp>
#include <FCConfig.h>
#include "ElementNamingUtils.h"
namespace Py
{
class Object;
}
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<class T>
inline const T& any_cast(const boost::any& value)
{
return boost::any_cast<const T&>(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<class T>
inline T& any_cast(boost::any& value)
{
return boost::any_cast<T&>(value);
}
class Property;
class Document;
class PropertyContainer;
class DocumentObject;
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
* `<<string>>`.
*
* @param[in] input The string to quote.
* @param[in] toPython If true, use Python quoting rules. Otherwise, use
* FreeCAD quoting rules.
*
* @return The string quoted.
*/
AppExport std::string quote(const std::string& input, bool toPython = false);
// Unfortunately VS2013 does not support default move constructor, so we have
// to implement them manually
#define FC_DEFAULT_CTORS(_t) \
_t(const _t&) = default; \
_t& operator=(const _t&) = default; \
_t(_t&& other) \
{ \
*this = std::move(other); \
} \
_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<std::string, std::string>&);
/**
* @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 moveconstruct 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)
, forceIdentifier(_forceIdentifier)
{}
FC_DEFAULT_CTORS(String)
{
str = std::move(other.str);
isString = other.isString;
forceIdentifier = other.forceIdentifier;
return *this;
}
// Accessors
/**
* @brief Get the string as a `std::string`.
*
* @return The string as a `std::string`.
*/
const std::string& getString() const
{
return str;
}
/**
* @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;
}
/**
* @brief Get a string representation of this object identifier.
*
* @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<std::string>(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<const char*>(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 documents 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,
const String* objName = nullptr);
private:
std::string str;
bool isString;
bool forceIdentifier;
};
/**
* @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
{
private:
enum typeEnum
{
SIMPLE,
MAP,
ARRAY,
RANGE,
};
public:
// Constructors
FC_DEFAULT_CTORS(Component)
{
name = std::move(other.name);
type = other.type;
begin = other.begin;
end = other.end;
step = other.step;
return *this;
}
/**
* @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<int>::max(),
int end = std::numeric_limits<int>::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<int>::max(),
int end = std::numeric_limits<int>::max(),
int step = 1); // explicit bombs
/**
* @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 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 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[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<int>::max(),
int _step = 1);
/**
* @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;
}
// Accessors
/**
* @brief Create a string representation of a 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;
}
// Operators
/**
* @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:
String name;
typeEnum type;
int begin;
int end;
int step;
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<int>::max(),
int _step = 1)
{
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.
*
* 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<int>::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 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<int>::max()); // explicit bombs
FC_DEFAULT_CTORS(ObjectIdentifier)
{
owner = other.owner;
documentName = std::move(other.documentName);
documentObjectName = std::move(other.documentObjectName);
subObjectName = std::move(other.subObjectName);
shadowSub = std::move(other.shadowSub);
components = std::move(other.components);
documentNameSet = other.documentNameSet;
documentObjectNameSet = other.documentObjectNameSet;
localProperty = other.localProperty;
_cache = std::move(other._cache);
_hash = other._hash;
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;
}
/**
* @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();
}
/**
* @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));
_cache.clear();
}
/**
* @brief Get the name of the property.
* @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<typename C>
void addComponents(const C& cs)
{
components.insert(components.end(), cs.begin(), cs.end());
}
/**
* @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<Component> getPropertyComponents() const;
/**
* @brief Get the components of this object identifier.
*
* @return A vector of components.
*/
const std::vector<Component>& 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 the number of components.
*
* @return The number of components in this identifier.
*/
int numComponents() const;
/**
* @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;
/**
* @brief Create a string representation of this object identifier.
*
* 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 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 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 Whether 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 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 the object identifier.
*
* 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.
*/
App::ObjectIdentifier canonicalPath() const;
// Document-centric functions
/**
* @brief Set the document name for this object identifier.
*
* If @p force is true, the document name will always be included in the
* string representation.
*
* @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 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.
*
* 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 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::pair<App::DocumentObject*, std::string>, 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);
/**
* @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);
/**
* @brief A type for storing dependencies of an ObjectIdentifier.
*
* 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`.
*
* 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<App::DocumentObject*, std::set<std::string>>;
/**
* @brief Get the dependencies of this object identifier.
*
* @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
* 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<std::string>* labels = nullptr) const;
/**
* @brief Get the dependencies of this object identifier.
*
* @param[in,out] deps: Returns the dependencies.
* @param[in] needProps: Whether need property dependencies.
* @param[out] labels: Optional return of any label references.
*
* @see ObjectIdentifier::getDep(bool,std::vector<std::string>*) const
*/
void
getDep(Dependencies& deps, bool needProps, std::vector<std::string>* labels = nullptr) const;
/**
* @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<std::string>& labels) const;
/**
* @brief Find a document with the given 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 The document object, or `nullptr` if not found or uniquely defined.
*/
App::DocumentObject* getDocumentObject() const;
/**
* @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<std::string> getStringList() const;
/**
* @brief Construct the simplest possible object identifier relative to another.
*
* @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,
App::DocumentObject* newObj) const;
// Operators
/**
* @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 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 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 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;
// Getter
/**
* @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
* are just kept as Python object wrapped by App::any.
*
* @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 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 are supported: `Base::Quantity`,
* `double`, `char*`, `const char*`, `int`, `unsigned int`, `short`, `unsigned short`,
* `char`, and `unsigned char`.
*
* @param[in] value The value to set.
*/
void setValue(const App::any& value) const;
// Static functions
/**
* @brief Parse a string to create an object identifier.
*
* @param[in] docObj Document object that will own this object identifier.
* @param[in] 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<App::DocumentObject*>& 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
{
/**
* @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.
*/
explicit ResolveResults(const ObjectIdentifier& oi);
int propertyIndex {0};
App::Document* resolvedDocument {nullptr};
String resolvedDocumentName;
App::DocumentObject* resolvedDocumentObject {nullptr};
String resolvedDocumentObjectName;
String subObjectName;
App::DocumentObject* resolvedSubObject {nullptr};
App::Property* resolvedProperty {nullptr};
std::string propertyName;
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,
int& ptype) const;
/**
* @brief Get sub field part of a property as a string.
*
* @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,
const Py::Object* value = nullptr,
Dependencies* deps = nullptr) const;
/**
* @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.
*
* @param[in,out] results The ResolveResults object to fill out.
*/
void resolve(ResolveResults& results) const;
/**
* @brief Resolve ambiguity in the object identifier.
*
* Given a ResolveResults, the ambiguities in the object identifier are resolved.
*
* @param[in] results The result of the resolve method.
*/
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<std::string>& 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<Component> 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();
}
//@{
/**
* @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
namespace std
{
template<>
struct hash<App::ObjectIdentifier>
{
using argument_type = App::ObjectIdentifier;
using result_type = std::size_t;
inline result_type operator()(argument_type const& s) const
{
return s.hash();
}
};
} // namespace std
#endif