PropertyExpressionEngine: convert to link type property
PropertyExpressionEngine is changed to derived from a new class PropertyExpressionContainer, which is in turn derives from PropertyXLinkContainer. This makes PropertyExpressionEngine a link type property that is capable of external linking. It now uses the unified link property APIs for dependency management and tracking of object life time, re-labeling, etc. ObjectIdentifier is modified to support sub-object reference, but is not exposed to end-user, because expression syntax is kept mostly unchanged, which will be submitted in future PR. There is, however, one small change in expression syntax (ExpressionParser.y) to introduce local property reference to avoid ambiguity mentioned in FreeCAD/FreeCAD#1619 Modified Expression/ExpressionModifier interface to support various link property API for link modification.
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
#include <Base/Reader.h>
|
||||
#include <Base/Writer.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/QuantityPy.h>
|
||||
#include <App/ObjectIdentifier.h>
|
||||
#include <App/DocumentObject.h>
|
||||
|
||||
@@ -68,13 +69,13 @@ PropertyConstraintList::~PropertyConstraintList()
|
||||
|
||||
App::ObjectIdentifier PropertyConstraintList::makeArrayPath(int idx)
|
||||
{
|
||||
return App::ObjectIdentifier(getContainer()) << App::ObjectIdentifier::Component::ArrayComponent(ObjectIdentifier::String(getName()), idx);
|
||||
return App::ObjectIdentifier(*this,idx);
|
||||
}
|
||||
|
||||
App::ObjectIdentifier PropertyConstraintList::makeSimplePath(const Constraint * c)
|
||||
{
|
||||
return App::ObjectIdentifier(getContainer()) << App::ObjectIdentifier::Component::SimpleComponent(getName())
|
||||
<< App::ObjectIdentifier::Component::SimpleComponent(App::ObjectIdentifier::String(c->Name, !ExpressionParser::isTokenAnIndentifier(c->Name)));
|
||||
return App::ObjectIdentifier(*this) << App::ObjectIdentifier::SimpleComponent(
|
||||
App::ObjectIdentifier::String(c->Name, !ExpressionParser::isTokenAnIndentifier(c->Name)));
|
||||
}
|
||||
|
||||
App::ObjectIdentifier PropertyConstraintList::makePath(int idx, const Constraint * c)
|
||||
@@ -175,43 +176,52 @@ void PropertyConstraintList::setValue(const Constraint* lValue)
|
||||
|
||||
void PropertyConstraintList::setValues(const std::vector<Constraint*>& lValue)
|
||||
{
|
||||
auto copy = lValue;
|
||||
for(auto &cstr : copy)
|
||||
cstr = cstr->clone();
|
||||
aboutToSetValue();
|
||||
applyValues(lValue);
|
||||
applyValues(std::move(copy));
|
||||
hasSetValue();
|
||||
}
|
||||
|
||||
void PropertyConstraintList::applyValues(const std::vector<Constraint*>& lValue)
|
||||
void PropertyConstraintList::setValues(std::vector<Constraint*>&& lValue) {
|
||||
aboutToSetValue();
|
||||
applyValues(std::move(lValue));
|
||||
hasSetValue();
|
||||
}
|
||||
|
||||
void PropertyConstraintList::applyValues(std::vector<Constraint*>&& lValue)
|
||||
{
|
||||
std::vector<Constraint*> oldVals(_lValueList);
|
||||
std::set<Constraint*> oldVals(_lValueList.begin(),_lValueList.end());
|
||||
std::map<App::ObjectIdentifier, App::ObjectIdentifier> renamed;
|
||||
std::set<App::ObjectIdentifier> removed;
|
||||
boost::unordered_map<boost::uuids::uuid, std::size_t> newValueMap;
|
||||
|
||||
/* Check for renames */
|
||||
for (unsigned int i = 0; i < lValue.size(); i++) {
|
||||
boost::unordered_map<boost::uuids::uuid, std::size_t>::const_iterator j = valueMap.find(lValue[i]->tag);
|
||||
|
||||
if (j != valueMap.end() && (i != j->second || _lValueList[j->second]->Name != lValue[i]->Name) ) {
|
||||
App::ObjectIdentifier old_oid(makePath(j->second, _lValueList[j->second] ));
|
||||
App::ObjectIdentifier new_oid(makePath(i, lValue[i]));
|
||||
|
||||
renamed[old_oid] = new_oid;
|
||||
if (j != valueMap.end()) {
|
||||
if(i != j->second || _lValueList[j->second]->Name != lValue[i]->Name) {
|
||||
App::ObjectIdentifier old_oid(makePath(j->second, _lValueList[j->second] ));
|
||||
App::ObjectIdentifier new_oid(makePath(i, lValue[i]));
|
||||
renamed[old_oid] = new_oid;
|
||||
}
|
||||
valueMap.erase(j);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update value map with new tags from new array */
|
||||
valueMap.clear();
|
||||
for (std::size_t i = 0; i < lValue.size(); i++)
|
||||
valueMap[lValue[i]->tag] = i;
|
||||
newValueMap[lValue[i]->tag] = i;
|
||||
|
||||
// safety insurance in case new new values contain some pointers of the old values
|
||||
oldVals.erase(lValue[i]);
|
||||
}
|
||||
|
||||
/* Collect info about removed elements */
|
||||
for (std::size_t i = 0; i < oldVals.size(); i++) {
|
||||
boost::unordered_map<boost::uuids::uuid, std::size_t>::const_iterator j = valueMap.find(oldVals[i]->tag);
|
||||
App::ObjectIdentifier oid(makePath(i, oldVals[i]));
|
||||
for(auto &v : valueMap)
|
||||
removed.insert(makePath(v.second,_lValueList[v.second]));
|
||||
|
||||
/* If not found in new values, place it in the set to be removed */
|
||||
if (j == valueMap.end())
|
||||
removed.insert(oid);
|
||||
}
|
||||
/* Update value map with new tags from new array */
|
||||
valueMap = std::move(newValueMap);
|
||||
|
||||
/* Signal removes first, in case renamed values below have the same names as some of the removed ones. */
|
||||
if (removed.size() > 0)
|
||||
@@ -221,16 +231,11 @@ void PropertyConstraintList::applyValues(const std::vector<Constraint*>& lValue)
|
||||
if (renamed.size() > 0)
|
||||
signalConstraintsRenamed(renamed);
|
||||
|
||||
/* Resize array to new size */
|
||||
_lValueList.resize(lValue.size());
|
||||
|
||||
/* copy all objects */
|
||||
for (unsigned int i = 0; i < lValue.size(); i++)
|
||||
_lValueList[i] = lValue[i]->clone();
|
||||
_lValueList = std::move(lValue);
|
||||
|
||||
/* Clean-up; remove old values */
|
||||
for (unsigned int i = 0; i < oldVals.size(); i++)
|
||||
delete oldVals[i];
|
||||
for(auto &v : oldVals)
|
||||
delete v;
|
||||
}
|
||||
|
||||
PyObject *PropertyConstraintList::getPyObject(void)
|
||||
@@ -241,6 +246,32 @@ PyObject *PropertyConstraintList::getPyObject(void)
|
||||
return list;
|
||||
}
|
||||
|
||||
bool PropertyConstraintList::getPyPathValue(const App::ObjectIdentifier &path, Py::Object &res) const {
|
||||
if(path.numSubComponents()!=2 || path.getPropertyComponent(0).getName()!=getName())
|
||||
return false;
|
||||
|
||||
const ObjectIdentifier::Component & c1 = path.getPropertyComponent(1);
|
||||
|
||||
const Constraint *cstr = 0;
|
||||
|
||||
if (c1.isArray())
|
||||
cstr = _lValueList[c1.getIndex(_lValueList.size())];
|
||||
else if (c1.isSimple()) {
|
||||
ObjectIdentifier::Component c1 = path.getPropertyComponent(1);
|
||||
for(auto c : _lValueList) {
|
||||
if(c->Name == c1.getName()) {
|
||||
cstr = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!cstr)
|
||||
return false;
|
||||
Quantity q = cstr->getPresentationValue();
|
||||
res = new Base::QuantityPy(new Base::Quantity(q));
|
||||
return true;
|
||||
}
|
||||
|
||||
void PropertyConstraintList::setPyObject(PyObject *value)
|
||||
{
|
||||
if (PyList_Check(value)) {
|
||||
@@ -307,26 +338,21 @@ void PropertyConstraintList::Restore(Base::XMLReader &reader)
|
||||
reader.readEndElement("ConstraintList");
|
||||
|
||||
// assignment
|
||||
setValues(values);
|
||||
for (Constraint* it : values)
|
||||
delete it;
|
||||
setValues(std::move(values));
|
||||
}
|
||||
|
||||
Property *PropertyConstraintList::Copy(void) const
|
||||
{
|
||||
PropertyConstraintList *p = new PropertyConstraintList();
|
||||
p->applyValidGeometryKeys(validGeometryKeys);
|
||||
p->applyValues(_lValueList);
|
||||
p->setValues(_lValueList);
|
||||
return p;
|
||||
}
|
||||
|
||||
void PropertyConstraintList::Paste(const Property &from)
|
||||
{
|
||||
const PropertyConstraintList& FromList = dynamic_cast<const PropertyConstraintList&>(from);
|
||||
aboutToSetValue();
|
||||
applyValues(FromList._lValueList);
|
||||
applyValidGeometryKeys(FromList.validGeometryKeys);
|
||||
hasSetValue();
|
||||
setValues(FromList._lValueList);
|
||||
}
|
||||
|
||||
unsigned int PropertyConstraintList::getMemSize(void) const
|
||||
@@ -414,8 +440,7 @@ bool PropertyConstraintList::validConstraintName(const std::string & name)
|
||||
|
||||
ObjectIdentifier PropertyConstraintList::createPath(int ConstrNbr) const
|
||||
{
|
||||
return App::ObjectIdentifier(getContainer())
|
||||
<< App::ObjectIdentifier::Component::ArrayComponent(App::ObjectIdentifier::String(getName()), ConstrNbr);
|
||||
return App::ObjectIdentifier(*this,ConstrNbr);
|
||||
}
|
||||
|
||||
int PropertyConstraintList::getIndexFromConstraintName(const string &name)
|
||||
@@ -425,21 +450,27 @@ int PropertyConstraintList::getIndexFromConstraintName(const string &name)
|
||||
|
||||
void PropertyConstraintList::setPathValue(const ObjectIdentifier &path, const boost::any &value)
|
||||
{
|
||||
const ObjectIdentifier::Component & c0 = path.getPropertyComponent(0);
|
||||
if(path.numSubComponents()!=2 || path.getPropertyComponent(0).getName()!=getName())
|
||||
FC_THROWM(Base::ValueError,"invalid constraint path " << path.toString());
|
||||
|
||||
const ObjectIdentifier::Component & c1 = path.getPropertyComponent(1);
|
||||
double dvalue;
|
||||
|
||||
if (value.type() == typeid(double))
|
||||
dvalue = boost::any_cast<double>(value);
|
||||
else if (value.type() == typeid(float))
|
||||
dvalue = App::any_cast<float>(value);
|
||||
else if (value.type() == typeid(long))
|
||||
dvalue = App::any_cast<long>(value);
|
||||
else if (value.type() == typeid(int))
|
||||
dvalue = App::any_cast<int>(value);
|
||||
else if (value.type() == typeid(Quantity))
|
||||
dvalue = (boost::any_cast<Quantity>(value)).getValue();
|
||||
dvalue = (App::any_cast<const Quantity &>(value)).getValue();
|
||||
else
|
||||
throw std::bad_cast();
|
||||
|
||||
if (c0.isArray() && path.numSubComponents() == 1) {
|
||||
int index = c0.getIndex();
|
||||
|
||||
if (c0.getIndex() >= _lValueList.size())
|
||||
throw Base::IndexError("Array out of bounds");
|
||||
if (c1.isArray()) {
|
||||
size_t index = c1.getIndex(_lValueList.size());
|
||||
switch (_lValueList[index]->Type) {
|
||||
case Angle:
|
||||
dvalue = Base::toRadians<double>(dvalue);
|
||||
@@ -452,9 +483,7 @@ void PropertyConstraintList::setPathValue(const ObjectIdentifier &path, const bo
|
||||
hasSetValue();
|
||||
return;
|
||||
}
|
||||
else if (c0.isSimple() && path.numSubComponents() == 2) {
|
||||
ObjectIdentifier::Component c1 = path.getPropertyComponent(1);
|
||||
|
||||
else if (c1.isSimple()) {
|
||||
for (std::vector<Constraint *>::const_iterator it = _lValueList.begin(); it != _lValueList.end(); ++it) {
|
||||
int index = it - _lValueList.begin();
|
||||
|
||||
@@ -473,20 +502,20 @@ void PropertyConstraintList::setPathValue(const ObjectIdentifier &path, const bo
|
||||
}
|
||||
}
|
||||
}
|
||||
throw Base::ValueError("Invalid constraint");
|
||||
FC_THROWM(Base::ValueError,"invalid constraint path " << path.toString());
|
||||
}
|
||||
|
||||
const Constraint * PropertyConstraintList::getConstraint(const ObjectIdentifier &path) const
|
||||
{
|
||||
const ObjectIdentifier::Component & c0 = path.getPropertyComponent(0);
|
||||
if(path.numSubComponents()!=2 || path.getPropertyComponent(0).getName()!=getName())
|
||||
FC_THROWM(Base::ValueError,"Invalid constraint path " << path.toString());
|
||||
|
||||
if (c0.isArray() && path.numSubComponents() == 1) {
|
||||
if (c0.getIndex() >= _lValueList.size())
|
||||
throw Base::IndexError("Array out of bounds");
|
||||
const ObjectIdentifier::Component & c1 = path.getPropertyComponent(1);
|
||||
|
||||
return _lValueList[c0.getIndex()];
|
||||
if (c1.isArray()) {
|
||||
return _lValueList[c1.getIndex(_lValueList.size())];
|
||||
}
|
||||
else if (c0.isSimple() && path.numSubComponents() == 2) {
|
||||
else if (c1.isSimple()) {
|
||||
ObjectIdentifier::Component c1 = path.getPropertyComponent(1);
|
||||
|
||||
for (std::vector<Constraint *>::const_iterator it = _lValueList.begin(); it != _lValueList.end(); ++it) {
|
||||
@@ -494,7 +523,7 @@ const Constraint * PropertyConstraintList::getConstraint(const ObjectIdentifier
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
throw Base::ValueError("Invalid constraint");
|
||||
FC_THROWM(Base::ValueError,"Invalid constraint path " << path.toString());
|
||||
}
|
||||
|
||||
const boost::any PropertyConstraintList::getPathValue(const ObjectIdentifier &path) const
|
||||
@@ -502,31 +531,30 @@ const boost::any PropertyConstraintList::getPathValue(const ObjectIdentifier &pa
|
||||
return boost::any(getConstraint(path)->getPresentationValue());
|
||||
}
|
||||
|
||||
const ObjectIdentifier PropertyConstraintList::canonicalPath(const ObjectIdentifier &p) const
|
||||
ObjectIdentifier PropertyConstraintList::canonicalPath(const ObjectIdentifier &p) const
|
||||
{
|
||||
const ObjectIdentifier::Component & c0 = p.getPropertyComponent(0);
|
||||
if(p.numSubComponents()!=2 || p.getPropertyComponent(0).getName()!=getName())
|
||||
FC_THROWM(Base::ValueError,"Invalid constraint path " << p.toString());
|
||||
|
||||
if (c0.isArray() && p.numSubComponents() == 1) {
|
||||
if (c0.getIndex() < _lValueList.size() && _lValueList[c0.getIndex()]->Name.size() > 0)
|
||||
return ObjectIdentifier(getContainer()) << ObjectIdentifier::Component::SimpleComponent(getName())
|
||||
<< ObjectIdentifier::Component::SimpleComponent(_lValueList[c0.getIndex()]->Name);
|
||||
const ObjectIdentifier::Component & c1 = p.getPropertyComponent(1);
|
||||
|
||||
if (c1.isArray()) {
|
||||
size_t idx = c1.getIndex();
|
||||
if (idx < _lValueList.size() && _lValueList[idx]->Name.size() > 0)
|
||||
return ObjectIdentifier(*this) << ObjectIdentifier::SimpleComponent(_lValueList[idx]->Name);
|
||||
return p;
|
||||
}
|
||||
else if (c0.isSimple() && p.numSubComponents() == 2) {
|
||||
ObjectIdentifier::Component c1 = p.getPropertyComponent(1);
|
||||
|
||||
if (c1.isSimple())
|
||||
return p;
|
||||
else if (c1.isSimple()) {
|
||||
return p;
|
||||
}
|
||||
throw Base::ValueError("Invalid constraint");
|
||||
FC_THROWM(Base::ValueError,"Invalid constraint path " << p.toString());
|
||||
}
|
||||
|
||||
void PropertyConstraintList::getPaths(std::vector<ObjectIdentifier> &paths) const
|
||||
{
|
||||
for (std::vector<Constraint *>::const_iterator it = _lValueList.begin(); it != _lValueList.end(); ++it) {
|
||||
if ((*it)->Name.size() > 0)
|
||||
paths.push_back(ObjectIdentifier(getContainer()) << ObjectIdentifier::Component::SimpleComponent(getName())
|
||||
<< ObjectIdentifier::Component::SimpleComponent((*it)->Name));
|
||||
paths.push_back(ObjectIdentifier(*this) << ObjectIdentifier::SimpleComponent((*it)->Name));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user