/*************************************************************************** * Copyright (c) 2015 Eivind Kvedalen * * * * 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 #include #include #include #include #include #include #include "ElementNamingUtils.h" namespace Py { class Object; } namespace App { using any = boost::any; template inline const T& any_cast(const boost::any& value) { return boost::any_cast(value); } template inline T& any_cast(boost::any& value) { return boost::any_cast(value); } class Property; class Document; class PropertyContainer; class DocumentObject; class ExpressionVisitor; 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) class AppExport ObjectIdentifier { public: class AppExport DocumentMapper { public: explicit DocumentMapper(const std::map&); ~DocumentMapper(); }; class String { friend class ObjectIdentifier; public: String(const std::string& s = "", bool _isRealString = false, bool _forceIdentifier = false) : str(s) , isString(_isRealString) , forceIdentifier(_forceIdentifier) {} // explicit bombs 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 /** Returns the string */ const std::string& getString() const { return str; } /** Return true is string need to be quoted */ bool isRealString() const { return isString; } bool isForceIdentifier() const { return forceIdentifier; } /** Returns a possibly quoted string */ std::string toString(bool toPython = false) const; // Operators explicit operator std::string() const { return str; } explicit operator const char*() const { return str.c_str(); } bool operator==(const String& other) const { return str == other.str; } bool operator!=(const String& other) const { return str != other.str; } bool operator>=(const String& other) const { return str >= other.str; } bool operator<(const String& other) const { return str < other.str; } bool operator>(const String& other) const { return str > other.str; } void checkImport(const App::DocumentObject* owner, const App::DocumentObject* obj = nullptr, String* objName = nullptr); private: std::string str; bool isString; bool forceIdentifier; }; /** * @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. */ 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; } Component(const String& _name = String(), typeEnum _type = SIMPLE, int begin = INT_MAX, int end = INT_MAX, int step = 1); // explicit bombs Component(String&& _name, typeEnum _type = SIMPLE, int begin = INT_MAX, int end = INT_MAX, int step = 1); // explicit bombs static Component SimpleComponent(const char* _component); static Component SimpleComponent(const String& _component); static Component SimpleComponent(String&& _component); static Component ArrayComponent(int _index); static Component RangeComponent(int _begin, int _end = INT_MAX, int _step = 1); static Component MapComponent(const String& _key); static Component MapComponent(String&& _key); // Type queries bool isSimple() const { return type == SIMPLE; } bool isMap() const { return type == MAP; } bool isArray() const { return type == ARRAY; } bool isRange() const { return type == RANGE; } // Accessors void toString(std::ostream& ss, bool toPython = false) const; const std::string& getName() const { return name.getString(); } int getIndex() const { return begin; } size_t getIndex(size_t count) const; int getBegin() const { return begin; } int getEnd() const { return end; } int getStep() const { return step; } // Operators bool operator==(const Component& other) const; bool operator<(const Component& other) const; Py::Object get(const Py::Object& pyobj) const; void set(Py::Object& pyobj, const Py::Object& value) const; void del(Py::Object& pyobj) const; private: String name; typeEnum type; int begin; int end; int step; friend class ObjectIdentifier; }; static Component SimpleComponent(const char* _component) { return Component::SimpleComponent(_component); } static Component SimpleComponent(const String& _component) { return Component::SimpleComponent(_component); } static Component SimpleComponent(String&& _component) { return Component::SimpleComponent(std::move(_component)); } static Component SimpleComponent(const std::string _component) { return Component::SimpleComponent(_component.c_str()); } static Component ArrayComponent(int _index) { return Component::ArrayComponent(_index); } static Component RangeComponent(int _begin, int _end = INT_MAX, int _step = 1) { return Component::RangeComponent(_begin, _end, _step); } static Component MapComponent(const String& _key) { return Component::MapComponent(_key); } static Component MapComponent(String&& _key) { return Component::MapComponent(_key); } explicit ObjectIdentifier(const App::PropertyContainer* _owner = nullptr, const std::string& property = std::string(), int index = INT_MAX); ObjectIdentifier(const App::PropertyContainer* _owner, bool localProperty); ObjectIdentifier(const App::Property& prop, int index = 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; } virtual ~ObjectIdentifier() = default; App::DocumentObject* getOwner() const { return owner; } // Components void addComponent(const Component& c) { components.push_back(c); _cache.clear(); } // Components void addComponent(Component&& c) { components.push_back(std::move(c)); _cache.clear(); } std::string getPropertyName() const; template void addComponents(const C& cs) { components.insert(components.end(), cs.begin(), cs.end()); } const Component& getPropertyComponent(int i, int* idx = nullptr) const; void setComponent(int idx, Component&& comp); void setComponent(int idx, const Component& comp); std::vector getPropertyComponents() const; const std::vector& getComponents() const { return components; } std::string getSubPathStr(bool toPython = false) const; int numComponents() const; int numSubComponents() const; const std::string& toString() const; std::string toPersistentString() const; std::string toEscapedString() const; bool isTouched() const; App::Property* getProperty(int* ptype = nullptr) const; App::ObjectIdentifier canonicalPath() const; // Document-centric functions void setDocumentName(String&& name, bool force = false); String getDocumentName() const; void setDocumentObjectName(String&& name, bool force = false, String&& subname = String(), bool checkImport = false); void setDocumentObjectName(const App::DocumentObject* obj, bool force = false, String&& subname = String(), bool checkImport = false); bool hasDocumentObjectName(bool forced = false) const; bool isLocalProperty() const { return localProperty; } String getDocumentObjectName() const; const std::string& getSubObjectName(bool newStyle) const; const std::string& getSubObjectName() const; using SubNameMap = std::map, std::string>; void importSubNames(const SubNameMap& subNameMap); bool updateLabelReference(App::DocumentObject*, const std::string&, const char*); bool relabeledDocument(ExpressionVisitor& v, const std::string& oldLabel, const std::string& newLabel); /** Type for storing dependency 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'. * * Also, we use set of string instead of set of Property pointer, because * the property may not exist at the time this ObjectIdentifier is * constructed. */ using Dependencies = std::map>; /** Get dependencies of this object identifier * * @param needProps: whether need property dependencies. * @param 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* labels = nullptr) const; /** Get dependencies of this object identifier * * @param deps: returns the dependencies. * @param needProps: whether need property dependencies. * @param 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. */ void getDep(Dependencies& deps, bool needProps, std::vector* labels = nullptr) const; /// Returns all label references void getDepLabels(std::vector& labels) const; App::Document* getDocument(String name = String(), bool* ambiguous = nullptr) const; App::DocumentObject* getDocumentObject() const; std::vector getStringList() const; App::ObjectIdentifier relativeTo(const App::ObjectIdentifier& other) const; bool replaceObject(ObjectIdentifier& res, const App::DocumentObject* parent, App::DocumentObject* oldObj, App::DocumentObject* newObj) const; // Operators App::ObjectIdentifier& operator<<(const Component& value); App::ObjectIdentifier& operator<<(Component&& value); bool operator==(const ObjectIdentifier& other) const; bool operator!=(const ObjectIdentifier& other) const; bool operator<(const ObjectIdentifier& other) const; // Getter App::any getValue(bool pathValue = false, bool* isPseudoProperty = nullptr) const; 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. void setValue(const App::any& value) const; // Static functions static ObjectIdentifier parse(const App::DocumentObject* docObj, const std::string& str); std::string resolveErrorString() const; bool adjustLinks(ExpressionVisitor& v, const std::set& inList); bool updateElementReference(ExpressionVisitor& v, App::DocumentObject* feature = nullptr, bool reverse = false); void resolveAmbiguity(); bool verify(const App::Property& prop, bool silent = false) const; std::size_t hash() const; protected: struct ResolveResults { 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; std::string resolveErrorString() const; void getProperty(const ObjectIdentifier& oi); }; friend struct ResolveResults; App::Property* resolveProperty(const App::DocumentObject* obj, const char* propertyName, App::DocumentObject*& sobj, int& ptype) const; void getSubPathStr(std::ostream& ss, const ResolveResults& result, bool toPython = false) const; Py::Object access(const ResolveResults& rs, Py::Object* value = nullptr, Dependencies* deps = nullptr) const; void resolve(ResolveResults& results) const; void resolveAmbiguity(ResolveResults& results); static App::DocumentObject* getDocumentObject(const App::Document* doc, const String& name, std::bitset<32>& flags); void getDepLabels(const ResolveResults& result, std::vector& labels) const; App::DocumentObject* owner; String documentName; String documentObjectName; String subObjectName; ElementNamePair shadowSub; std::vector components; bool documentNameSet; bool documentObjectNameSet; bool localProperty; private: std::string _cache; // Cached string represstation of this identifier std::size_t _hash; // Cached hash of this string }; 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 */ //@{ App::any AppExport pyObjectToAny(Py::Object pyobj, bool check = true); Py::Object AppExport pyObjectFromAny(const App::any& value); //@} } // namespace App namespace std { template<> struct hash { 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