/*************************************************************************** * 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 EXPRESSIONENGINE_H #define EXPRESSIONENGINE_H #include #include #include #include #include #include #include namespace Base { class Writer; class XMLReader; } // namespace Base namespace App { class DocumentObject; class DocumentObjectExecReturn; class ObjectIdentifier; class Expression; using ExpressionPtr = std::unique_ptr; class AppExport PropertyExpressionContainer: public App::PropertyXLinkContainer { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: PropertyExpressionContainer(); ~PropertyExpressionContainer() override; virtual std::map getExpressions() const = 0; virtual void setExpressions(std::map&& exprs) = 0; protected: virtual void onRelabeledDocument(const App::Document& doc) = 0; private: static void slotRelabelDocument(const App::Document& doc); }; class AppExport PropertyExpressionEngine : public App::PropertyExpressionContainer, private App::AtomicPropertyChangeInterface { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: void updateElementReference(App::DocumentObject* feature, bool reverse = false, bool notify = false) override; bool referenceChanged() const override; bool adjustLink(const std::set& inList) override; Property* CopyOnImportExternal(const std::map& nameMap) const override; Property* CopyOnLabelChange(App::DocumentObject* obj, const std::string& ref, const char* newLabel) const override; Property* CopyOnLinkReplace(const App::DocumentObject* parent, App::DocumentObject* oldObj, App::DocumentObject* newObj) const override; using ValidatorFunc = std::function expr)>; /** * @brief The ExpressionInfo struct encapsulates an expression. */ struct ExpressionInfo { std::shared_ptr expression; /**< The actual expression tree */ bool busy; explicit ExpressionInfo( std::shared_ptr expression = std::shared_ptr()) { this->expression = expression; this->busy = false; } ExpressionInfo(const ExpressionInfo&) = default; ExpressionInfo& operator=(const ExpressionInfo&) = default; }; PropertyExpressionEngine(); ~PropertyExpressionEngine() override; unsigned int getMemSize() const override; std::map getExpressions() const override; void setExpressions(std::map&& exprs) override; void onRelabeledDocument(const App::Document& doc) override; void setValue() {} // Dummy Property* Copy() const override; void Paste(const Property& from) override; void Save(Base::Writer& writer) const override; void Restore(Base::XMLReader& reader) override; void setValue(const App::ObjectIdentifier& path, std::shared_ptr expr); const boost::any getPathValue(const App::ObjectIdentifier& path) const override; /// Execute options enum ExecuteOption { /// Execute all expression ExecuteAll, /// Execute only output property bindings ExecuteOutput, /// Execute only non-output property bindings ExecuteNonOutput, /// Execute on document restore ExecuteOnRestore, }; /** Evaluate the expressions * * @param option: execution option, see ExecuteOption. */ DocumentObjectExecReturn* execute(ExecuteOption option = ExecuteAll, bool* touched = nullptr); void getPathsToDocumentObject(DocumentObject*, std::vector& paths) const; bool depsAreTouched() const; /* Expression validator */ void setValidator(ValidatorFunc f) { validator = f; } std::string validateExpression(const App::ObjectIdentifier& path, std::shared_ptr expr) const; void renameExpressions(const std::map& paths); void renameObjectIdentifiers(const std::map& paths); App::ObjectIdentifier canonicalPath(const App::ObjectIdentifier& p) const override; size_t numExpressions() const; /// signal called when an expression was changed boost::signals2::signal expressionChanged; void afterRestore() override; void onContainerRestored() override; void getLinksTo(std::vector& identifiers, App::DocumentObject* obj, const char* subname = nullptr, bool all = false) const override; /* Python interface */ PyObject* getPyObject() override; void setPyObject(PyObject*) override; protected: void hasSetValue() override; private: using DiGraph = boost::adjacency_list; using Edge = std::pair; // Note: use std::map instead of unordered_map to keep the binding order stable #if defined(FC_OS_MACOSX) || defined(FC_OS_BSD) || defined(_LIBCPP_VERSION) using ExpressionMap = std::map; #else using ExpressionMap = std::map; #endif std::vector computeEvaluationOrder(ExecuteOption option); void buildGraphStructures(const App::ObjectIdentifier& path, const std::shared_ptr expression, boost::unordered_map& nodes, boost::unordered_map& revNodes, std::vector& edges) const; void buildGraph(const ExpressionMap& exprs, boost::unordered_map& revNodes, DiGraph& g, ExecuteOption option = ExecuteAll) const; void slotChangedObject(const App::DocumentObject& obj, const App::Property& prop); void slotChangedProperty(const App::DocumentObject& obj, const App::Property& prop); void updateHiddenReference(const std::string& key); bool running = false; /**< Boolean used to avoid loops */ bool restoring = false; ExpressionMap expressions; /**< Stored expressions */ ValidatorFunc validator; /**< Valdiator functor */ struct RestoredExpression { std::string path; std::string expr; std::string comment; }; /**< Expressions are read from file to this map first before they are validated and inserted * into the actual map */ std::unique_ptr> restoredExpressions; struct Private; std::unique_ptr pimpl; friend class AtomicPropertyChange; }; } // namespace App #endif // EXPRESSIONENGINE_H