Skip to content

Commit

Permalink
Support geometry element reference auto update, import and export
Browse files Browse the repository at this point in the history
Because element mapping auto generation uses the object ID as Tag, and
possibly document depended string hash indexes, the mapping must be
able to rebuilt when importing objects, i.e. during copy&paste. The
mechanism is here is,

* During exporting, strip out all element maps.
* Property link sub now include shadow copy of both the new and old
  style element referece. It will store the old style reference as
  before, and store the any new reference in a new XML attribute.
  GeoFeature will signal any element mapping change, and allows all
  link sub to keep an up-to-date element reference
* During importing, only import old style reference.
* Touch all geo feature objects, and recompute the imported objects.
* Regenerate mapped references, and geo feature will signal property
  link sub to update their reference as before

Necessary Core API changes includes,

* Move utility function resolveObject() from Gui.Selection to
  DocumentObject as resolve().
* Add getElementName() API to GeoFeature.
* Add utility function resolveElement() to GeoFeature
  • Loading branch information
realthunder committed Mar 5, 2018
1 parent c38ef04 commit 579dee2
Show file tree
Hide file tree
Showing 14 changed files with 670 additions and 244 deletions.
21 changes: 20 additions & 1 deletion src/App/Document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ struct DocumentP
int iTransactionMode;
bool rollback;
bool undoing; ///< document in the middle of undo or redo
std::vector<DocumentObject *> pendingRecomputes;
std::bitset<32> StatusBits;
int iUndoMode;
unsigned int UndoMemSize;
Expand Down Expand Up @@ -1792,12 +1793,18 @@ Document::readObjects(Base::XMLReader& reader)
return objs;
}

void Document::addRecomputeObject(DocumentObject *obj) {
if(testStatus(Status::Importing) && obj)
d->pendingRecomputes.push_back(obj);
}

std::vector<App::DocumentObject*>
Document::importObjects(Base::XMLReader& reader)
{
d->hashers.clear();
Base::ObjectStatusLocker<Status, Document> restoreBit(Status::Restoring, this);
Base::ObjectStatusLocker<Status, Document> restoreBit2(Status::Importing, this);
d->pendingRecomputes.clear();
reader.readElement("Document");
long scheme = reader.getAttributeAsInteger("SchemaVersion");
reader.DocumentSchema = scheme;
Expand All @@ -1822,9 +1829,21 @@ Document::importObjects(Base::XMLReader& reader)
}

reader.readEndElement("Document");
signalImportObjects(objs, reader);

signalImportObjects(objs, reader);
afterRestore(objs);

if(d->pendingRecomputes.size()) {
for(auto obj : d->pendingRecomputes) {
if(obj) {
FC_LOG("recompute '" << obj->getNameInDocument() << "' after restore");
obj->touch();
}
}
recompute(objs);
}
d->pendingRecomputes.clear();

signalFinishImportObjects(objs);
d->hashers.clear();
return objs;
Expand Down
3 changes: 2 additions & 1 deletion src/App/Document.h
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,8 @@ class AppExport Document : public App::PropertyContainer
return !links.empty();
}

void addRemapProperty(Property *prop);
/// Called by objects during restore to ask for recompute
void addRecomputeObject(DocumentObject *obj);

/// Function called to signal that an object identifier has been renamed
void renameObjectIdentifiers(const std::map<App::ObjectIdentifier, App::ObjectIdentifier> & paths, const std::function<bool(const App::DocumentObject*)> &selector = [](const App::DocumentObject *) { return true; });
Expand Down
65 changes: 65 additions & 0 deletions src/App/DocumentObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -856,3 +856,68 @@ bool DocumentObject::hasChildElement() const {
}
return false;
}

DocumentObject *DocumentObject::resolve(const char *subname,
App::DocumentObject **parent, std::string *childName, const char **subElement) const
{
auto self = const_cast<DocumentObject*>(this);
if(parent) *parent = 0;
if(subElement) *subElement = 0;
if(!subname || *subname==0)
return self;

auto obj = getSubObject(subname);
if(!obj)
return self;

if(!parent && !subElement)
return obj;

// NOTE, the convension of '.' separated SubName demands a mandatory ending
// '.' for each object name in SubName, even if there is no subelement
// following it. So finding the last dot will give us the end of the last
// object name.
const char *dot = strrchr(subname,'.');
if(!dot || dot == subname) {
if(subElement)
*subElement = dot?dot+1:subname;
return obj; // this means no parent object reference in SubName
}

if(parent)
*parent = self;

bool elementMapChecked = false;
const char *lastDot = dot;
for(--dot;;--dot) {
// check for the second last dot, which is the end of the last parent object
if(*dot == '.' || dot == subname) {
// We can't get parent object by its name, because the object may be
// externally linked (i.e. in a different document). So go through
// getSubObject again.
if(!elementMapChecked) {
elementMapChecked = true;
const char *sub = dot==subname?dot:dot+1;
if(Data::ComplexGeoData::isMappedElement(sub))
lastDot = dot;
}
if(dot==subname)
break;
auto sobj = getSubObject(std::string(subname,dot-subname+1).c_str());
if(parent) *parent = sobj;
if(sobj!=this)
break;
}
}
if(childName && lastDot!=dot) {
if(*dot == '.')
++dot;
const char *nextDot = strchr(dot,'.');
assert(nextDot);
*childName = std::string(dot,nextDot-dot);
}
if(subElement)
*subElement = *lastDot=='.'?lastDot+1:lastDot;
return obj;
}

14 changes: 14 additions & 0 deletions src/App/DocumentObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,20 @@ class AppExport DocumentObject: public App::TransactionalObject
return _pcViewProviderName.c_str();
}

/** Resolve the last document object referenced in the subname
*
* @param subname: dot separated subname
* @param parent: return the direct parent of the object
* @param childName: return child name to be passed to is/setElementVisible()
* @param subElement: return non-object sub-element name if found. The
* pointer is guaranteed to be within the buffer pointed to by 'subname'
*
* @return Returns the last referenced document object in the subname. If no
* such object in subname, return pObject.
*/
App::DocumentObject *resolve(const char *subname, App::DocumentObject **parent=0,
std::string *childName=0, const char **subElement=0) const;

protected:
/// recompute only this object
virtual App::DocumentObjectExecReturn *recompute(void);
Expand Down
26 changes: 26 additions & 0 deletions src/App/DocumentObjectPy.xml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,32 @@ referencing subobject.
<UserDocu>Get all paths from this object to another object following the OutList.</UserDocu>
</Documentation>
</Methode>
<Methode Name="resolve" Const="true">
<Documentation>
<UserDocu>
resolve(subname) -- resolve the sub object

Returns a tuple (subobj,parent,elementName,subElement), where 'subobj' is the
last object referenced in 'subname', and 'parent' is the direct parent of
'subobj', and 'elementName' is the child name of the subobj, which can be used
to call parent.isElementVisible/setElementVisible(). 'subElement' is the
non-object sub-element name if any.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="resolveSubElement" Const="true">
<Documentation>
<UserDocu>
resolveSubElement(subname,append,type) -- resolve both new and old style sub element

subname: subname reference contianing object hierarchy
append: Whether to append object hierarchy prefix inside subname to returned element name
type: 0: normal, 1: for import, 2: for export

Return tuple(obj,newElementName,oldElementName)
</UserDocu>
</Documentation>
</Methode>
<Attribute Name="OutList" ReadOnly="true">
<Documentation>
<UserDocu>A list of all objects this object links to.</UserDocu>
Expand Down
46 changes: 46 additions & 0 deletions src/App/DocumentObjectPyImp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "DocumentObject.h"
#include "Document.h"
#include "Expression.h"
#include "GeoFeature.h"
#include "GroupExtension.h"
#include "GeoFeatureGroupExtension.h"

Expand Down Expand Up @@ -690,3 +691,48 @@ int DocumentObjectPy::setCustomAttributes(const char* attr, PyObject *obj)
Py::Int DocumentObjectPy::getID() const {
return Py::Int(getDocumentObjectPtr()->getID());
}

PyObject *DocumentObjectPy::resolve(PyObject *args)
{
const char *subname;
if (!PyArg_ParseTuple(args, "s",&subname))
return NULL; // NULL triggers exception

PY_TRY {
std::string elementName;
const char *subElement = 0;
App::DocumentObject *parent = 0;
auto obj = getDocumentObjectPtr()->resolve(subname,&parent,&elementName,&subElement);

Py::Tuple ret(4);
ret.setItem(0,Py::Object(obj->getPyObject(),true));
ret.setItem(1,parent?Py::Object(parent->getPyObject(),true):Py::None());
ret.setItem(2,Py::String(elementName.c_str()));
ret.setItem(3,Py::String(subElement?subElement:""));
return Py::new_reference_to(ret);
} PY_CATCH;

Py_Return;
}

PyObject *DocumentObjectPy::resolveSubElement(PyObject *args)
{
const char *subname;
PyObject *append = Py_False;
int type = 0;
if (!PyArg_ParseTuple(args, "s|Oi",&subname,&append,&type))
return NULL; // NULL triggers exception

PY_TRY {
std::pair<std::string,std::string> elementName;
auto obj = GeoFeature::resolveElement(getDocumentObjectPtr(), subname,elementName,
PyObject_IsTrue(append),(GeoFeature::ElementNameType)type);
Py::Tuple ret(3);
ret.setItem(0,obj?Py::Object(obj->getPyObject(),true):Py::None());
ret.setItem(1,Py::String(elementName.first));
ret.setItem(2,Py::String(elementName.second));
return Py::new_reference_to(ret);
} PY_CATCH;

Py_Return;
}
107 changes: 107 additions & 0 deletions src/App/GeoFeature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@
#ifndef _PreComp_
#endif

#include <Base/Console.h>
#include "Document.h"
#include "GeoFeature.h"
#include "GeoFeatureGroupExtension.h"
#include <App/GeoFeaturePy.h>

FC_LOG_LEVEL_INIT("GeoFeature",true,true);

using namespace App;


Expand Down Expand Up @@ -79,3 +83,106 @@ PyObject* GeoFeature::getPyObject(void)
}
return Py::new_reference_to(PythonObject);
}


std::pair<std::string,std::string> GeoFeature::getElementName(
const char *name, ElementNameType type) const
{
(void)type;

std::pair<std::string,std::string> ret;
if(!name) return ret;

auto prop = getPropertyOfGeometry();
if(!prop) return ret;

auto geo = prop->getComplexData();
if(!geo) return ret;

if(Data::ComplexGeoData::isMappedElement(name)) {
const char *oldName = geo->getElementName(name);
const char *dot = strrchr(name,'.');
if(oldName != name) {
if(!dot) {
ret.first = name;
ret.first += '.';
}else
ret.first.assign(name,dot-name+1);
ret.first += oldName;
ret.second = oldName;
}else{
if(FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG))
FC_WARN("element mapped name not found " << name);
ret.first = name;
if(dot)
ret.second = dot+1;
}
return ret;
}
const char *newName = geo->getElementName(name,true);
if(newName != name) {
std::ostringstream ss;
ss << Data::ComplexGeoData::elementMapPrefix() << newName << '.' << name;
ret.first = ss.str();
ret.second = name;
}else
ret.second = name;
return ret;
}

DocumentObject *GeoFeature::resolveElement(DocumentObject *obj, const char *subname,
std::pair<std::string,std::string> &elementName, bool append,
ElementNameType type, const DocumentObject *filter)
{
const char *element = 0;
if(!obj || !obj->getNameInDocument())
return 0;
obj = obj->resolve(subname,0,0,&element);
if(!obj)
return 0;
obj = obj->getLinkedObject(true);
if(!obj || (filter && obj!=filter))
return 0;
if(!element || !element[0])
return obj;

auto geo = dynamic_cast<GeoFeature*>(obj);
if(!geo)
return obj;
if(!append)
elementName = geo->getElementName(element,type);
else{
const auto &names = geo->getElementName(element,type);
if(names.first.size() && names.second.size()) {
std::string prefix(subname,element-subname);
elementName.first = prefix + names.first;
elementName.second = prefix + names.second;
}
}
return obj;
}

void GeoFeature::updateElementReference() {
auto prop = getPropertyOfGeometry();
if(!prop) return;
auto geo = prop->getComplexData();
if(!geo || !geo->getElementMapSize()) return;
auto elementMap = geo->getElementMap();
if(_elementMapCache != elementMap) {
_elementMapCache.swap(elementMap);
for(auto obj : getInListEx(true))
PropertyLinkSub::updateElementReferences(this,obj);
}
}

void GeoFeature::onChanged(const Property *prop) {
if(!isRestoring() && prop==getPropertyOfGeometry())
updateElementReference();
DocumentObject::onChanged(prop);
}

void GeoFeature::onDocumentRestored() {
if(!getDocument()->testStatus(Document::Status::Importing))
updateElementReference();
DocumentObject::onDocumentRestored();
}
Loading

0 comments on commit 579dee2

Please sign in to comment.