diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py index 2f74011914..7c2f1339c9 100644 --- a/src/ifcopenshell-python/ifcopenshell/file.py +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -28,11 +28,11 @@ class file(object): self.wrapped_data = f or ifcopenshell_wrapper.file() def create_entity(self,type,*args,**kwargs): e = entity_instance(type) + self.wrapped_data.add(e.wrapped_data) + e.wrapped_data.this.disown() attrs = list(enumerate(args)) + \ [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()] for idx, arg in attrs: e[idx] = arg - self.wrapped_data.add(e.wrapped_data) - e.wrapped_data.this.disown() return e def __getattr__(self, attr): if attr[0:6] == 'create': return functools.partial(self.create_entity,attr[6:]) diff --git a/src/ifcparse/IfcFile.h b/src/ifcparse/IfcFile.h index 1c6c941431..2ae5dca8c8 100644 --- a/src/ifcparse/IfcFile.h +++ b/src/ifcparse/IfcFile.h @@ -55,7 +55,7 @@ private: IfcSpfHeader _header; void setDefaultHeaderValues(); - void register_inverse(unsigned, Token); + public: IfcParse::IfcSpfLexer* tokens; IfcParse::IfcSpfStream* stream; @@ -130,6 +130,10 @@ public: void load(const IfcEntityInstanceData&); void load(unsigned entity_instance_name, std::vector& attributes); + + void register_inverse(unsigned, Token); + void register_inverse(unsigned, IfcUtil::IfcBaseClass*); + void unregister_inverse(unsigned, IfcUtil::IfcBaseClass*); }; } diff --git a/src/ifcparse/IfcParse.cpp b/src/ifcparse/IfcParse.cpp index 8fed95cfc0..4fcd22ffee 100644 --- a/src/ifcparse/IfcParse.cpp +++ b/src/ifcparse/IfcParse.cpp @@ -915,6 +915,20 @@ void IfcParse::IfcFile::register_inverse(unsigned id_from, Token t) { byref[t.value_int].push_back(id_from); } +void IfcParse::IfcFile::register_inverse(unsigned id_from, IfcUtil::IfcBaseClass* inst) { + byref[inst->entity->id()].push_back(id_from); +} + +void IfcParse::IfcFile::unregister_inverse(unsigned id_from, IfcUtil::IfcBaseClass* inst) { + std::vector& ids = byref[inst->entity->id()]; + std::vector::const_iterator it = std::find(ids.begin(), ids.end(), id_from); + if (it == ids.end()) { + throw IfcParse::IfcException("Instance not found among inverses"); + } else { + ids.erase(it); + } +} + // // Returns a string representation of the entity // Note that this initializes the entity if it is not initialized @@ -1008,6 +1022,97 @@ Argument* IfcEntityInstanceData::getArgument(unsigned int i) const { } } +class unregister_inverse_visitor { +private: + IfcFile& file_; + const IfcEntityInstanceData& data_; + +public: + unregister_inverse_visitor(IfcFile& file, const IfcEntityInstanceData& data) + : file_(file), data_(data) + {} + + void operator()(IfcUtil::IfcBaseClass* inst) { + file_.unregister_inverse(data_.id(), inst); + } +}; + +class register_inverse_visitor { +private: + IfcFile& file_; + const IfcEntityInstanceData& data_; + +public: + register_inverse_visitor(IfcFile& file, const IfcEntityInstanceData& data) + : file_(file), data_(data) + {} + + void operator()(IfcUtil::IfcBaseClass* inst) { + file_.register_inverse(data_.id(), inst); + } +}; + +class add_to_instance_list_visitor { +private: + IfcEntityList::ptr& list_; + +public: + add_to_instance_list_visitor(IfcEntityList::ptr& list) + : list_(list) + {} + + void operator()(IfcUtil::IfcBaseClass* inst) { + list_->push(inst); + } +}; + +class apply_individual_instance_visitor { +private: + Argument* attribute_; + IfcEntityInstanceData* data_; + + template + void apply_attribute_(T& t, Argument* attr) const { + if (attr->type() == IfcUtil::Argument_ENTITY_INSTANCE) { + IfcUtil::IfcBaseClass* inst = *attr; + t(inst); + } else if (attr->type() == IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE) { + IfcEntityList::ptr entity_list_attribute = *attr; + for (IfcEntityList::it it = entity_list_attribute->begin(); it != entity_list_attribute->end(); ++it) { + t(*it); + } + } else if (attr->type() == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE) { + IfcEntityListList::ptr entity_list_attribute = *attr; + for (IfcEntityListList::outer_it it = entity_list_attribute->begin(); it != entity_list_attribute->end(); ++it) { + for (IfcEntityListList::inner_it jt = it->begin(); jt != it->end(); ++jt) { + t(*jt); + } + } + } + }; +public: + apply_individual_instance_visitor(Argument* attribute) + : attribute_(attribute), data_(0) + {} + + apply_individual_instance_visitor(IfcEntityInstanceData* data) + : attribute_(0), data_(data) + {} + + template + void apply(T& t) const { + if (attribute_) { + apply_attribute_(t, attribute_); + } else { + for (unsigned i = 0; i < data_->getArgumentCount(); ++i) { + Argument* attr = data_->getArgument(i); + apply_attribute_(t, attr); + } + } + }; + +}; + void IfcEntityInstanceData::setArgument(unsigned int i, Argument* a, IfcUtil::ArgumentType attr_type) { if (!initialized_) { load(); @@ -1017,10 +1122,6 @@ void IfcEntityInstanceData::setArgument(unsigned int i, Argument* a, IfcUtil::Ar attributes_.push_back(new NullArgument()); } - if (i < attributes_.size()) { - delete attributes_[i]; - } - if (attr_type == IfcUtil::Argument_UNKNOWN) { attr_type = a->type(); } @@ -1117,6 +1218,20 @@ void IfcEntityInstanceData::setArgument(unsigned int i, Argument* a, IfcUtil::Ar break; } + if (i < attributes_.size()) { + Argument* current_attribute = attributes_[i]; + if (this->file) { + unregister_inverse_visitor visitor(*this->file, *this); + apply_individual_instance_visitor(current_attribute).apply(visitor); + } + delete attributes_[i]; + } + + if (this->file) { + register_inverse_visitor visitor(*this->file, *this); + apply_individual_instance_visitor(copy).apply(visitor); + } + if (i < attributes_.size()) { attributes_[i] = copy; } else { @@ -1271,25 +1386,8 @@ void traverse_(IfcUtil::IfcBaseClass* instance, std::set if (level >= max_level && max_level > 0) return; - for (unsigned i = 0; i < instance->getArgumentCount(); ++i) { - Argument* arg = instance->getArgument(i); - - if (arg->type() == IfcUtil::Argument_ENTITY_INSTANCE) { - traverse_(*arg, visited, list, level + 1, max_level); - } else if (arg->type() == IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE) { - IfcEntityList::ptr entity_list_attribute = *arg; - for (IfcEntityList::it it = entity_list_attribute->begin(); it != entity_list_attribute->end(); ++it) { - traverse_(*it, visited, list, level + 1, max_level); - } - } else if (arg->type() == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE) { - IfcEntityListList::ptr entity_list_attribute = *arg; - for (IfcEntityListList::outer_it it = entity_list_attribute->begin(); it != entity_list_attribute->end(); ++it) { - for (IfcEntityListList::inner_it jt = it->begin(); jt != it->end(); ++jt) { - traverse_(*jt, visited, list, level + 1, max_level); - } - } - } - } + add_to_instance_list_visitor visit(list); + apply_individual_instance_visitor(instance->entity).apply(visit); } IfcEntityList::ptr IfcParse::traverse(IfcUtil::IfcBaseClass* instance, int max_level) { diff --git a/test/tests.py b/test/tests.py index ae0161e710..256ed3b8b9 100644 --- a/test/tests.py +++ b/test/tests.py @@ -52,6 +52,16 @@ f[22].MiddleNames = 'John', 'Matthew' assert "('John','Matthew')" in str(f[22]) assert ("Id", "123") in list(f[22].get_info().items()) +# Assignment of instances and implications on inverse attributes +assert f[288].ConnectedTo[0].RelatingElement == f[288] +num_connections_1 = len(f[340].ConnectedTo + f[340].ConnectedFrom) +f[288].ConnectedTo[0].RelatingElement = f[340] +num_connections_2 = len(f[340].ConnectedTo + f[340].ConnectedFrom) +assert num_connections_2 == num_connections_1 + 1 +assert f[288].ConnectedTo == () +rel = f.createIfcRelConnectsPathElements(RelatingElement=f[288]) +assert f[288].ConnectedTo == (rel,) + # Some operations on ifcopenshell.guid assert len(ifcopenshell.guid.compress(uuid.uuid1().hex)) == 22