/*************************************************************************** * Copyright (c) 2002 Jürgen Riegel * * * * 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 * * * ***************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ #include #endif #include #include #include #include #include "Property.h" #include "ObjectIdentifier.h" #include "PropertyContainer.h" using namespace App; //************************************************************************** //************************************************************************** // Property //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TYPESYSTEM_SOURCE_ABSTRACT(App::Property, Base::Persistence) //************************************************************************** // Construction/Destruction static std::atomic _PropID; // Here is the implementation! Description should take place in the header file! Property::Property() : _id(++_PropID) {} Property::~Property() = default; const char* Property::getName() const { return myName ? myName : ""; } bool Property::hasName() const { return isValidName(myName); } bool Property::isValidName(const char* name) { return name && name[0] != '\0'; } std::string Property::getFullName() const { std::string name; if (myName) { if (father) { name = father->getFullName() + "."; } else { name = "?."; } name += myName; } else { return "?"; } return name; } std::string Property::getFileName(const char* postfix, const char* prefix) const { std::ostringstream ss; if (prefix) { ss << prefix; } if (!myName) { ss << "Property"; } else { std::string name = getFullName(); auto pos = name.find('#'); if (pos == std::string::npos) { ss << name; } else { ss << (name.c_str() + pos + 1); } } if (postfix) { ss << postfix; } return ss.str(); } short Property::getType() const { short type = 0; #define GET_PTYPE(_name) \ do { \ if (testStatus(App::Property::Prop##_name)) \ type |= Prop_##_name; \ } while (0) GET_PTYPE(ReadOnly); GET_PTYPE(Hidden); GET_PTYPE(Output); GET_PTYPE(Transient); GET_PTYPE(NoRecompute); GET_PTYPE(NoPersist); return type; } void Property::syncType(unsigned type) { #define SYNC_PTYPE(_name) \ do { \ if (type & Prop_##_name) \ StatusBits.set((size_t)Prop##_name); \ } while (0) SYNC_PTYPE(ReadOnly); SYNC_PTYPE(Transient); SYNC_PTYPE(Hidden); SYNC_PTYPE(Output); SYNC_PTYPE(NoRecompute); SYNC_PTYPE(NoPersist); } const char* Property::getGroup() const { return father->getPropertyGroup(this); } const char* Property::getDocumentation() const { return father->getPropertyDocumentation(this); } void Property::setContainer(PropertyContainer* Father) { father = Father; } void Property::setPathValue(const ObjectIdentifier& path, const boost::any& value) { path.setValue(value); } const boost::any Property::getPathValue(const ObjectIdentifier& path) const { return path.getValue(); } void Property::getPaths(std::vector& paths) const { paths.emplace_back(getContainer(), getName()); } ObjectIdentifier Property::canonicalPath(const ObjectIdentifier& p) const { return p; } namespace App { /*! * \brief The PropertyCleaner struct * Make deleting dynamic property safer by postponing its destruction. * * Dynamic property can be removed at any time, even during triggering of * onChanged() signal of the removing property. This patch introduced * static function Property::destroy() to make it safer by queueing any * removed property, and only deleting them when no onChanged() call is * active. */ struct PropertyCleaner { explicit PropertyCleaner(Property* p) : prop(p) { ++_PropCleanerCounter; } ~PropertyCleaner() { if (--_PropCleanerCounter) { return; } bool found = false; while (!_RemovedProps.empty()) { auto p = _RemovedProps.back(); _RemovedProps.pop_back(); if (p != prop) { p->setContainer(nullptr); delete p; } else { found = true; } } if (found) { _RemovedProps.push_back(prop); } } static void add(Property* prop) { _RemovedProps.push_back(prop); } Property* prop; static std::vector _RemovedProps; static int _PropCleanerCounter; }; } // namespace App std::vector PropertyCleaner::_RemovedProps; int PropertyCleaner::_PropCleanerCounter = 0; void Property::destroy(Property* p) { if (p) { // Is it necessary to nullify the container? May cause crash if any // onChanged() caller assumes a non-null container. // // p->setContainer(0); PropertyCleaner::add(p); } } void Property::touch() { PropertyCleaner guard(this); if (father) { father->onEarlyChange(this); father->onChanged(this); } StatusBits.set(Touched); } void Property::setReadOnly(bool readOnly) { this->setStatus(App::Property::ReadOnly, readOnly); } void Property::hasSetValue() { PropertyCleaner guard(this); if (father) { father->onChanged(this); if (!testStatus(Busy)) { Base::BitsetLocker guard(StatusBits, Busy); signalChanged(*this); } } StatusBits.set(Touched); } void Property::aboutToSetValue() { if (father) { father->onBeforeChange(this); } } void Property::verifyPath(const ObjectIdentifier& p) const { p.verify(*this); } Property* Property::Copy() const { // have to be reimplemented by a subclass! assert(0); return nullptr; } void Property::Paste(const Property& /*from*/) { // have to be reimplemented by a subclass! assert(0); } void Property::setStatusValue(unsigned long status) { // clang-format off static const unsigned long mask = (1<onPropertyStatusChanged(*this, oldStatus); } } } void Property::setStatus(Status pos, bool on) { auto bits = StatusBits; bits.set(pos, on); setStatusValue(bits.to_ulong()); } bool Property::isSame(const Property& other) const { if (&other == this) { return true; } if (other.getTypeId() != getTypeId() || getMemSize() != other.getMemSize()) { return false; } Base::StringWriter writer, writer2; Save(writer); other.Save(writer2); return writer.getString() == writer2.getString(); } //************************************************************************** //************************************************************************** // PropertyListsBase //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void PropertyListsBase::_setPyObject(PyObject* value) { std::vector indices; std::vector vals; Py::Object pySeq; if (PyDict_Check(value)) { Py::Dict dict(value); auto size = dict.size(); vals.reserve(size); indices.reserve(size); int listSize = getSize(); for (auto it = dict.begin(); it != dict.end(); ++it) { const auto& item = *it; PyObject* key = item.first.ptr(); if (!PyLong_Check(key)) { throw Base::TypeError("expect key type to be integer"); } long idx = PyLong_AsLong(key); if (idx < -1 || idx > listSize) { throw Base::ValueError("index out of bound"); } if (idx == -1 || idx == listSize) { idx = listSize; ++listSize; } indices.push_back(idx); vals.push_back(item.second.ptr()); } } else { if (PySequence_Check(value)) { pySeq = value; } else { PyObject* iter = PyObject_GetIter(value); if (iter) { Py::Object pyIter(iter, true); pySeq = Py::asObject(PySequence_Fast(iter, "")); } else { PyErr_Clear(); vals.push_back(value); } } if (!pySeq.isNone()) { Py::Sequence seq(pySeq); vals.reserve(seq.size()); for (auto it = seq.begin(); it != seq.end(); ++it) { vals.push_back((*it).ptr()); } } } setPyValues(vals, indices); } //************************************************************************** //************************************************************************** // PropertyLists //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TYPESYSTEM_SOURCE_ABSTRACT(App::PropertyLists, App::Property)