diff --git a/src/ifcexpressparser/bootstrap.py b/src/ifcexpressparser/bootstrap.py index 77e7f7131e..f9d71130db 100644 --- a/src/ifcexpressparser/bootstrap.py +++ b/src/ifcexpressparser/bootstrap.py @@ -121,7 +121,7 @@ actions = { 'general_aggregation_types' : "lambda t: AggregationType(t)", 'select_type' : "lambda t: SelectType(t)", 'binary_type' : "lambda t: BinaryType(t)", - 'subtype_declaration' : "lambda t: SubtypeExpression(t)", + 'subtype_declaration' : "lambda t: SubTypeExpression(t)", 'derive_clause' : "lambda t: AttributeList('derive', t)", 'derived_attr' : "lambda t: DerivedAttribute(t)", 'inverse_clause' : "lambda t: AttributeList('inverse', t)", @@ -170,32 +170,45 @@ for id in to_emit: stmt = "Suppress%s" % stmt statements.append("%s << %s" % (id, stmt)) -print ("""import sys -from pyparsing import * -from nodes import * +print ("""import os +import sys +import pickle -%s +cache_file = sys.argv[1] + ".cache.dat" +if os.path.exists(cache_file): + with open(cache_file, "rb") as f: + mapping = pickle.load(f) +else: + from pyparsing import * + from nodes import * + + import schema + import mapping + + %s -import schema -import mapping + syntax.ignore("--" + restOfLine) + syntax.ignore(Regex(r"\((?:\*(?:[^*]*\*+)+?\))")) + ast = syntax.parseFile(sys.argv[1]) + schema = schema.Schema(ast) + mapping = mapping.Mapping(schema) + + with open(cache_file, "wb") as f: + pickle.dump(mapping, f, protocol=0) import header import enum_header import implementation import latebound_header import latebound_implementation - -syntax.ignore("--" + restOfLine) -syntax.ignore(Regex(r"\((?:\*(?:[^*]*\*+)+?\))")) -ast = syntax.parseFile(sys.argv[1]) -schema = schema.Schema(ast) -mapping = mapping.Mapping(schema) +import schema_class header.Header(mapping).emit() enum_header.EnumHeader(mapping).emit() implementation.Implementation(mapping).emit() latebound_header.LateBoundHeader(mapping).emit() latebound_implementation.LateBoundImplementation(mapping).emit() +schema_class.SchemaClass(mapping).emit() sys.stdout.write(schema.name) -"""%('\n'.join(statements))) +"""%('\n '.join(statements))) diff --git a/src/ifcexpressparser/implementation.py b/src/ifcexpressparser/implementation.py index 69e4115092..80a2e729fa 100644 --- a/src/ifcexpressparser/implementation.py +++ b/src/ifcexpressparser/implementation.py @@ -210,16 +210,16 @@ class Implementation(codegen.Base): simple_type_impl.append(templates.simpletype_impl_comment % {'name': class_name}) simple_type_impl.extend(map(compose, map(lambda x: (class_name, attr_type, superclass, "(IfcEntityInstanceData*)0")+x, ( - ('getArgumentType', templates.const_function, 'IfcUtil::ArgumentType', ('unsigned int i',), templates.simpletype_impl_argument_type ), - ('getArgument', templates.const_function, 'Argument*', ('unsigned int i',), templates.simpletype_impl_argument ), - ('is', templates.const_function, 'bool', ('Type::Enum v',), simpletype_impl_is ), - ('type', templates.const_function, 'Type::Enum', (), templates.simpletype_impl_type ), ('Class', templates.function, 'Type::Enum', (), templates.simpletype_impl_class ), + ('declaration', templates.const_function, 'const IfcParse::type_declaration&', (), templates.simpletype_impl_declaration ), ('', constructor, '', ('IfcEntityInstanceData* e',), templates.simpletype_impl_explicit_constructor), ('', constructor, '', ("%s v" % type_str,), simpletype_impl_constructor ), ('', templates.cast_function, type_str, (), simpletype_impl_cast ) )))) simple_type_impl.append('') + + external_definitions = ["extern entity* %s_type;" % n for n in mapping.schema.entities.keys() ] + \ + ["extern type_declaration* %s_type;" % n for n in mapping.schema.simpletypes.keys()] self.str = templates.implementation % { 'schema_name_upper' : mapping.schema.name.upper(), @@ -232,7 +232,8 @@ class Implementation(codegen.Base): 'simple_type_statement' : simple_type_statements, 'parent_type_statements' : parent_type_statements, 'entity_implementations' : catnl(entity_implementations), - 'simple_type_impl' : catnl(simple_type_impl) + 'simple_type_impl' : catnl(simple_type_impl), + 'external_definitions' : catnl(external_definitions) } self.schema_name = mapping.schema.name.capitalize() diff --git a/src/ifcexpressparser/nodes.py b/src/ifcexpressparser/nodes.py index 4a16a536da..cc480e84bf 100644 --- a/src/ifcexpressparser/nodes.py +++ b/src/ifcexpressparser/nodes.py @@ -44,15 +44,17 @@ class TypeDeclaration(Node): class EntityDeclaration(Node): name = property(lambda self: self.tokens[1]) attributes = property(lambda self: self.tokens_of_type(ExplicitAttribute)) + abstract = property(lambda self: self.single_token_of_type(SuperTypeExpression) is not None and \ + self.single_token_of_type(SuperTypeExpression).abstract) def init(self): assert self.tokens[0] == 'entity' - s = self.single_token_of_type(SubtypeExpression) + s = self.single_token_of_type(SubTypeExpression) self.inverse = self.single_token_of_type(AttributeList, 'type', 'inverse') self.derive = self.single_token_of_type(AttributeList, 'type', 'derive') self.supertypes = s.types if s else [] def __repr__(self): builder = "" - builder += "Entity(%s)" % (self.name) + builder += "%sEntity(%s)" % ("Abstract " if self.abstract else "", self.name) if len(self.supertypes): builder += "\n Supertypes: %s"%(",".join(self.supertypes)) if len(self.attributes): @@ -106,12 +108,20 @@ class SelectType(Node): class SubSuperTypeExpression(Node): type = property(lambda self: self.tokens[0]) types = property(lambda self: self.tokens[3::2]) + abstract = False def init(self): - assert self.type == self.class_type + if self.tokens[0] == 'abstract': + self.tokens = self.tokens[1:] + self.abstract = True + assert self.type == self.type_relationship -class SubtypeExpression(SubSuperTypeExpression): - class_type = 'subtype' +class SubTypeExpression(SubSuperTypeExpression): + type_relationship = 'subtype' + + +class SuperTypeExpression(SubSuperTypeExpression): + type_relationship = 'supertype' class AttributeList(Node): diff --git a/src/ifcexpressparser/schema.py b/src/ifcexpressparser/schema.py index 4bd87a7c8b..f29ac016c9 100644 --- a/src/ifcexpressparser/schema.py +++ b/src/ifcexpressparser/schema.py @@ -27,25 +27,27 @@ if tuple(map(int, platform.python_version_tuple())) < (2, 7): # According to ISO 10303-11 7.1.2: Letters: "... The case of # letters is significant only within explicit string literals." +class OrderedCaseInsensitiveDict_KeyObject(str): + def __eq__(self, other): + return self.lower() == other.lower() + def __hash__(self): + return hash(self.lower()) + + class OrderedCaseInsensitiveDict(collections.OrderedDict): - class KeyObject(str): - def __eq__(self, other): - return self.lower() == other.lower() - def __hash__(self): - return hash(self.lower()) - def __init__(self, *args, **kwargs): collections.OrderedDict.__init__(self) for key, value in collections.OrderedDict(*args, **kwargs).items(): - self[OrderedCaseInsensitiveDict.KeyObject(key)] = value + self[OrderedCaseInsensitiveDict_KeyObject(key)] = value def __setitem__(self, key, value): - return collections.OrderedDict.__setitem__(self, OrderedCaseInsensitiveDict.KeyObject(key), value) + return collections.OrderedDict.__setitem__(self, OrderedCaseInsensitiveDict_KeyObject(key), value) def __getitem__(self, key): - return collections.OrderedDict.__getitem__(self, OrderedCaseInsensitiveDict.KeyObject(key)) + return collections.OrderedDict.__getitem__(self, OrderedCaseInsensitiveDict_KeyObject(key)) def get(self, key, *args, **kwargs): - return collections.OrderedDict.get(self, OrderedCaseInsensitiveDict.KeyObject(key), *args, **kwargs) + return collections.OrderedDict.get(self, OrderedCaseInsensitiveDict_KeyObject(key), *args, **kwargs) def __contains__(self, key): - return collections.OrderedDict.__contains__(self, OrderedCaseInsensitiveDict.KeyObject(key)) + return collections.OrderedDict.__contains__(self, OrderedCaseInsensitiveDict_KeyObject(key)) + class Schema: def is_enumeration(self, v): diff --git a/src/ifcexpressparser/schema_class.py b/src/ifcexpressparser/schema_class.py new file mode 100644 index 0000000000..31aed5c73c --- /dev/null +++ b/src/ifcexpressparser/schema_class.py @@ -0,0 +1,199 @@ +############################################################################### +# # +# This file is part of IfcOpenShell. # +# # +# IfcOpenShell is free software: you can redistribute it and/or modify # +# it under the terms of the Lesser GNU General Public License as published by # +# the Free Software Foundation, either version 3.0 of the License, or # +# (at your option) any later version. # +# # +# IfcOpenShell 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 # +# Lesser GNU General Public License for more details. # +# # +# You should have received a copy of the Lesser GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### + +import operator + +import nodes +import codegen +import templates + +class SchemaClass(codegen.Base): + def __init__(self, mapping): + + class UnmetDependenciesException(Exception): pass + + schema_name = mapping.schema.name + declared_types = [] + + def get_declared_type(type, emitted_names=None): + if isinstance(type, nodes.AggregationType): + aggr_type = type.aggregate_type + make_bound = lambda b: -1 if b == '?' else int(b) + bound1, bound2 = map(make_bound, (type.bounds.lower, type.bounds.upper)) + decl_type = get_declared_type(type.type) + return "new aggregation_type(aggregation_type::%(aggr_type)s_type, %(bound1)d, %(bound2)d, %(decl_type)s)" % locals() + elif isinstance(type, nodes.BinaryType): + return "new simple_type(simple_type::binary_type)" + elif isinstance(type, str): + if mapping.schema.is_type(type) or mapping.schema.is_entity(type): + if emitted_names is None or type in emitted_types: + return "new named_type(%s_type)" % type + else: + raise UnmetDependenciesException(type) + else: + return "new simple_type(simple_type::%s_type)" % type + + def find_inverse_name_and_index(entity_name, attribute_name): + attributes_per_subtype = [] + while True: + entity = mapping.schema.entities[entity_name] + attr_names = list(map(operator.attrgetter('name'), entity.attributes)) + if len(attr_names): + attributes_per_subtype.append((entity_name, attr_names)) + if len(entity.supertypes) != 1: break + entity_name = entity.supertypes[0] + index = 0 + for et, attrs in attributes_per_subtype[::-1]: + try: return et, attrs.index(attribute_name) + except: pass + + self.schema_name = mapping.schema.name.capitalize() + + statements = ['', + '#include "../ifcparse/IfcSchema.h"', + '', + 'using namespace IfcParse;' + ''] + + collections_by_type = (('entity', mapping.schema.entities ), + ('type_declaration', mapping.schema.simpletypes ), + ('select_type', mapping.schema.selects ), + ('enumeration_type', mapping.schema.enumerations)) + + for cpp_type, collection in collections_by_type: + for name in collection.keys(): + statements.append('%(cpp_type)s* %(name)s_type = 0;' % locals()) + + statements.append('#ifdef _MSC_VER' ) + statements.append('#pragma optimize("", off)') + statements.append('#endif' ) + + statements.append('schema_definition* populate_schema() {') + + emitted_types = set() + while len(emitted_types) < len(mapping.schema.simpletypes): + for name, type in mapping.schema.simpletypes.items(): + if name in emitted_types: continue + + try: + declared_type = get_declared_type(type, emitted_types) + except UnmetDependenciesException: + continue + + statements.append(' %(name)s_type = new type_declaration(IfcSchema::Type::%(name)s, %(declared_type)s);' % locals()) + emitted_types.add(name) + + declared_types.append('%(name)s_type' % locals()) + + for name, enum in mapping.schema.enumerations.items(): + statements.append(' {') + statements.append(' std::vector items; items.reserve(%d);' % len(enum.values)) + statements.extend(map(lambda v: ' items.push_back("%s");' % v, sorted(enum.values))) + statements.append(' %(name)s_type = new enumeration_type(IfcSchema::Type::%(name)s, items);' % locals()) + statements.append(' }') + + declared_types.append('%(name)s_type' % locals()) + + emitted_entities = set() + while len(emitted_entities) < len(mapping.schema.entities): + for name, type in mapping.schema.entities.items(): + if name in emitted_entities: continue + if len(type.supertypes) == 0 or set(type.supertypes) < emitted_entities: + supertype = '0' if len(type.supertypes) == 0 else '%s_type' % type.supertypes[0] + statements.append(' %(name)s_type = new entity(IfcSchema::Type::%(name)s, %(supertype)s);' % locals()) + emitted_entities.add(name) + + declared_types.append('%(name)s_type' % locals()) + + emmited = emitted_types | emitted_entities | set(mapping.schema.enumerations.keys()) + + emitted_selects = set() + while len(emitted_selects) < len(mapping.schema.selects): + for name, type in mapping.schema.selects.items(): + if name in emitted_selects: continue + if set(type.values) < emmited: + statements.append(' {') + statements.append(' std::vector items; items.reserve(%d);' % len(type.values)) + statements.extend(map(lambda v: ' items.push_back(%s_type);' % v, sorted(type.values))) + statements.append(' %(name)s_type = new select_type(IfcSchema::Type::%(name)s, items);' % locals()) + statements.append(' }') + emitted_selects.add(name) + emmited.add(name) + + declared_types.append('%(name)s_type' % locals()) + + num_declarations = len(declared_types) + + for name, type in mapping.schema.entities.items(): + derived = set(mapping.derived_in_supertype(type)) + attribute_names = list(map(operator.attrgetter('name'), mapping.arguments(type))) + + statements.append(' {') + statements.append(' std::vector attributes; attributes.reserve(%d);' % len(type.attributes)) + for attr in type.attributes: + attr_name, optional = attr.name, str(attr.optional).lower() + decl_type = get_declared_type(attr.type) + statements.append(' attributes.push_back(new entity::attribute("%(attr_name)s", %(decl_type)s, %(optional)s));' % locals()) + statements.append(' std::vector derived; derived.reserve(%d);' % len(attribute_names)) + statements.append(' ' + " ".join(map(lambda b: 'derived.push_back(%s);' % str(b in derived).lower(), attribute_names))) + statements.append(' %(name)s_type->set_attributes(attributes, derived);' % locals()) + statements.append(' }') + + for name, type in mapping.schema.entities.items(): + if type.inverse: + statements.append(' {') + statements.append(' std::vector attributes; attributes.reserve(%d);' % len(type.inverse.elements)) + for attr in type.inverse.elements: + if attr.bounds: + make_bound = lambda b: -1 if b == '?' else int(b) + bound1, bound2 = map(make_bound, (attr.bounds.lower, attr.bounds.upper)) + else: + bound1, bound2 = -1, -1 + attr_name, aggr_type, entity_ref = attr.name, attr.type, attr.entity + if aggr_type is None: aggr_type = 'unspecified' + attribute_entity, attribute_entity_index = find_inverse_name_and_index(entity_ref, attr.attribute) + statements.append(' attributes.push_back(new entity::inverse_attribute("%(attr_name)s", entity::inverse_attribute::%(aggr_type)s_type, %(bound1)d, %(bound2)d, %(entity_ref)s_type, %(attribute_entity)s_type->attributes()[%(attribute_entity_index)d]));' % locals()) + statements.append(' %(name)s_type->set_inverse_attributes(attributes);' % locals()) + statements.append(' }') + + statements.append('') + statements.append(' std::vector declarations; declarations.reserve(%(num_declarations)d);' % locals()) + for type_name in declared_types: + statements.append(' declarations.push_back(%(type_name)s);' % locals()) + + statements.append(' return new schema_definition("%(schema_name)s", declarations, true);' % locals()) + + statements.extend(('}','')) + + statements.append('#ifdef _MSC_VER' ) + statements.append('#pragma optimize("", on)') + statements.append('#endif' ) + + statements.extend(('const schema_definition& get_schema() {', + '', + ' static const schema_definition* s = populate_schema();', + ' return *s;', + '}','','')) + + self.str = "\n".join(statements) + + self.file_name = '%s-schema.cpp'%self.schema_name + + def __repr__(self): + return self.str diff --git a/src/ifcexpressparser/templates.py b/src/ifcexpressparser/templates.py index a0b637c618..95bac58920 100644 --- a/src/ifcexpressparser/templates.py +++ b/src/ifcexpressparser/templates.py @@ -30,11 +30,14 @@ header = """ #include "../ifcparse/IfcEntityList.h" #include "../ifcparse/IfcBaseClass.h" +#include "../ifcparse/IfcSchema.h" #include "../ifcparse/IfcException.h" #include "../ifcparse/Argument.h" #include "../ifcparse/%(schema_name)senum.h" +const IfcParse::schema_definition& get_schema(); + #define IfcSchema %(schema_name)s namespace %(schema_name)s { @@ -112,6 +115,7 @@ namespace Type { implementation= """ #include "../ifcparse/%(schema_name)s.h" +#include "../ifcparse/IfcSchema.h" #include "../ifcparse/IfcException.h" #include "../ifcparse/IfcWrite.h" @@ -121,6 +125,9 @@ using namespace %(schema_name)s; using namespace IfcParse; using namespace IfcWrite; +// External definitions +%(external_definitions)s + IfcUtil::IfcBaseClass* %(schema_name)s::SchemaEntity(IfcEntityInstanceData* e) { switch(e->type()) { %(schema_entity_statements)s @@ -353,10 +360,7 @@ derived_field_statement_attrs = 'idxs.insert(%d); ' simpletype = """%(documentation)s class IFC_PARSE_API %(name)s : public %(superclass)s { public: - virtual IfcUtil::ArgumentType getArgumentType(unsigned int i) const; - virtual Argument* getArgument(unsigned int i) const; - bool is(Type::Enum v) const; - Type::Enum type() const; + virtual const IfcParse::type_declaration& declaration() const; static Type::Enum Class(); explicit %(name)s (IfcEntityInstanceData* e); %(name)s (%(type)s v); @@ -366,16 +370,17 @@ public: simpletype_impl_comment = "// Function implementations for %(name)s" simpletype_impl_argument_type = "if (i == 0) { return %(attr_type)s; } else { throw IfcParse::IfcAttributeOutOfRangeException(\"Argument index out of range\"); }" -simpletype_impl_argument = "return entity->getArgument(i);" +simpletype_impl_argument = "return data_->getArgument(i);" simpletype_impl_is_with_supertype = "return v == Type::%(class_name)s || %(superclass)s::is(v);" simpletype_impl_is_without_supertype = "return v == %(class_name)s::Class();" simpletype_impl_type = "return Type::%(class_name)s;" simpletype_impl_class = "return Type::%(class_name)s;" -simpletype_impl_explicit_constructor = "entity = e;" -simpletype_impl_constructor = "entity = new IfcEntityInstanceData(Class()); {IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument(); attr->set(v" +"); entity->setArgument(0, attr);}" -simpletype_impl_constructor_templated = "entity = new IfcEntityInstanceData(Class()); {IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument(); attr->set(v->generalize()); entity->setArgument(0, attr);}" -simpletype_impl_cast = "return *entity->getArgument(0);" -simpletype_impl_cast_templated = "IfcEntityList::ptr es = *entity->getArgument(0); return es->as<%(underlying_type)s>();" +simpletype_impl_explicit_constructor = "data_ = e;" +simpletype_impl_constructor = "data_ = new IfcEntityInstanceData(Class()); {IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument(); attr->set(v" +"); data_->setArgument(0, attr);}" +simpletype_impl_constructor_templated = "data_ = new IfcEntityInstanceData(Class()); {IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument(); attr->set(v->generalize()); data_->setArgument(0, attr);}" +simpletype_impl_cast = "return *data_->getArgument(0);" +simpletype_impl_cast_templated = "IfcEntityList::ptr es = *data_->getArgument(0); return es->as<%(underlying_type)s>();" +simpletype_impl_declaration = "return *%(class_name)s_type;" select = """%(documentation)s typedef IfcUtil::IfcBaseClass %(name)s; @@ -392,13 +397,7 @@ IFC_PARSE_API %(name)s FromString(const std::string& s); entity = """%(documentation)s class IFC_PARSE_API %(name)s %(superclass)s{ public: -%(attributes)s virtual unsigned int getArgumentCount() const { return %(argument_count)d; } - virtual IfcUtil::ArgumentType getArgumentType(unsigned int i) const {%(argument_type_function_body)s} - virtual Type::Enum getArgumentEntity(unsigned int i) const {%(argument_entity_function_body)s} - virtual const char* getArgumentName(unsigned int i) const {%(argument_name_function_body)s} - virtual Argument* getArgument(unsigned int i) const { return entity->getArgument(i); } -%(inverse)s bool is(Type::Enum v) const; - Type::Enum type() const; +%(attributes)s %(inverse)s virtual const IfcParse::entity& declaration() const; static Type::Enum Class(); %(name)s (IfcEntityInstanceData* e); %(name)s (%(constructor_arguments)s); @@ -420,11 +419,12 @@ const char* %(name)s::ToString(%(name)s v) { """ entity_implementation = """// Function implementations for %(name)s -%(attributes)s%(inverse)sbool %(name)s::is(Type::Enum v) const { return v == Type::%(name)s%(parent_type_test)s; } -Type::Enum %(name)s::type() const { return Type::%(name)s; } +%(attributes)s +%(inverse)s +const IfcParse::entity& %(name)s::declaration() const { return *%(name)s_type; } Type::Enum %(name)s::Class() { return Type::%(name)s; } -%(name)s::%(name)s(IfcEntityInstanceData* e) : %(superclass)s { if (!e) return; if (e->type() != Type::%(name)s) throw IfcException("Unable to find find keyword in schema"); entity = e; } -%(name)s::%(name)s(%(constructor_arguments)s) : %(superclass)s {entity = new IfcEntityInstanceData(Class()); %(constructor_implementation)s } +%(name)s::%(name)s(IfcEntityInstanceData* e) : %(superclass)s { if (!e) return; if (!e->is(Type::%(name)s)) throw IfcException("Unable to find find keyword in schema"); data_ = e; } +%(name)s::%(name)s(%(constructor_arguments)s) : %(superclass)s {data_ = new IfcEntityInstanceData(Class()); %(constructor_implementation)s } """ optional_attribute_description = "/// Whether the optional attribute %s is defined for this %s" @@ -450,24 +450,24 @@ parent_type_stmt = ' if(v==%(name)s%(padding)s) { return %(parent)s; }' parent_type_test = " || %s::is(v)" -optional_attr_stmt = "return !entity->getArgument(%(index)d)->isNull();" +optional_attr_stmt = "return !data_->getArgument(%(index)d)->isNull();" -get_attr_stmt = "return *entity->getArgument(%(index)d);" -get_attr_stmt_enum = "return %(type)s::FromString(*entity->getArgument(%(index)d));" -get_attr_stmt_entity = "return (%(type)s)((IfcUtil::IfcBaseClass*)(*entity->getArgument(%(index)d)));" -get_attr_stmt_array = "IfcEntityList::ptr es = *entity->getArgument(%(index)d); return es->as<%(list_instance_type)s>();" -get_attr_stmt_nested_array = "IfcEntityListList::ptr es = *entity->getArgument(%(index)d); return es->as<%(list_instance_type)s>();" +get_attr_stmt = "return *data_->getArgument(%(index)d);" +get_attr_stmt_enum = "return %(type)s::FromString(*data_->getArgument(%(index)d));" +get_attr_stmt_entity = "return (%(type)s)((IfcUtil::IfcBaseClass*)(*data_->getArgument(%(index)d)));" +get_attr_stmt_array = "IfcEntityList::ptr es = *data_->getArgument(%(index)d); return es->as<%(list_instance_type)s>();" +get_attr_stmt_nested_array = "IfcEntityListList::ptr es = *data_->getArgument(%(index)d); return es->as<%(list_instance_type)s>();" -get_inverse = "return entity->getInverse(Type::%(type)s, %(index)d)->as<%(type)s>();" +get_inverse = "return data_->getInverse(Type::%(type)s, %(index)d)->as<%(type)s>();" -set_attr_stmt = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(v" +");entity->setArgument(%(index)d,attr);}" -set_attr_stmt_enum = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(IfcWrite::IfcWriteArgument::EnumerationReference(v,%(type)s::ToString(v)));entity->setArgument(%(index)d,attr);}" -set_attr_stmt_array = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(v->generalize()" +");entity->setArgument(%(index)d,attr);}" +set_attr_stmt = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(v" +");data_->setArgument(%(index)d,attr);}" +set_attr_stmt_enum = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(IfcWrite::IfcWriteArgument::EnumerationReference(v,%(type)s::ToString(v)));data_->setArgument(%(index)d,attr);}" +set_attr_stmt_array = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(v->generalize()" +");data_->setArgument(%(index)d,attr);}" -constructor_stmt = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((%(name)s)" +");entity->setArgument(%(index)d,attr);}" -constructor_stmt_enum = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((IfcWrite::IfcWriteArgument::EnumerationReference(%(name)s,%(type)s::ToString(%(name)s)))" +");entity->setArgument(%(index)d,attr);}" -constructor_stmt_array = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((%(name)s)->generalize()" +");entity->setArgument(%(index)d,attr);}" -constructor_stmt_derived = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(IfcWrite::IfcWriteArgument::Derived()" +");entity->setArgument(%(index)d,attr);}" +constructor_stmt = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((%(name)s)" +");data_->setArgument(%(index)d,attr);}" +constructor_stmt_enum = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((IfcWrite::IfcWriteArgument::EnumerationReference(%(name)s,%(type)s::ToString(%(name)s)))" +");data_->setArgument(%(index)d,attr);}" +constructor_stmt_array = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((%(name)s)->generalize()" +");data_->setArgument(%(index)d,attr);}" +constructor_stmt_derived = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(IfcWrite::IfcWriteArgument::Derived()" +");data_->setArgument(%(index)d,attr);}" constructor_stmt_optional = " if (%(name)s) {%(stmt)s } else { IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument(); attr->set(boost::blank()); entity->setArgument(%(index)d, attr); }" diff --git a/src/ifcparse/IfcBaseClass.h b/src/ifcparse/IfcBaseClass.h index 650589973c..f7fde0967c 100644 --- a/src/ifcparse/IfcBaseClass.h +++ b/src/ifcparse/IfcBaseClass.h @@ -32,20 +32,28 @@ class Argument; +namespace IfcParse { // these have to be declared first in order for the virtual function below to be covariant. separate into different header file + class declaration; + class entity; + class type_declaration; +} + namespace IfcUtil { class IFC_PARSE_API IfcBaseClass { + protected: + IfcAbstractEntity* data_; + public: - virtual ~IfcBaseClass() {} - IfcEntityInstanceData* entity; - virtual bool is(IfcSchema::Type::Enum v) const = 0; - virtual IfcSchema::Type::Enum type() const = 0; - - virtual unsigned int getArgumentCount() const = 0; - virtual IfcUtil::ArgumentType getArgumentType(unsigned int i) const = 0; - virtual IfcSchema::Type::Enum getArgumentEntity(unsigned int i) const = 0; - virtual Argument* getArgument(unsigned int i) const = 0; - virtual const char* getArgumentName(unsigned int i) const = 0; + IfcBaseClass() : data_(0) {} + IfcBaseClass(IfcAbstractEntity* d) : data_(d) {} + virtual ~IfcBaseClass() {} + + const IfcAbstractEntity& data() const { return *data_; } + IfcAbstractEntity& data() { return *data_; } + void data(IfcAbstractEntity* d); + + virtual const IfcParse::declaration& declaration() const = 0; template T* as() { @@ -64,19 +72,19 @@ namespace IfcUtil { class IFC_PARSE_API IfcBaseEntity : public IfcBaseClass { public: - Argument* getArgumentByName(const std::string& name) const; - std::vector getAttributeNames() const; - std::vector getInverseAttributeNames() const; - unsigned id() const { return entity->id(); } + IfcBaseEntity() : IfcBaseClass() {} + IfcBaseEntity(IfcAbstractEntity* d) : IfcBaseClass(d) {} + + virtual const IfcParse::entity& declaration() const = 0; }; // TODO: Investigate whether these should be template classes instead class IFC_PARSE_API IfcBaseType : public IfcBaseEntity { public: - unsigned int getArgumentCount() const; - Argument* getArgument(unsigned int i) const; - const char* getArgumentName(unsigned int i) const; - IfcSchema::Type::Enum getArgumentEntity(unsigned int /*i*/) const { return IfcSchema::Type::UNDEFINED; } + IfcBaseType() : IfcBaseClass() {} + IfcBaseType(IfcAbstractEntity* d) : IfcBaseClass(d) {} + + virtual const IfcParse::type_declaration& declaration() const = 0; }; } diff --git a/src/ifcparse/IfcFile.h b/src/ifcparse/IfcFile.h index 8b2137514a..d168a4d904 100644 --- a/src/ifcparse/IfcFile.h +++ b/src/ifcparse/IfcFile.h @@ -65,6 +65,7 @@ private: typedef std::map entity_entity_map_t; bool parsing_complete_; + const schema_definition* schema_; entity_by_id_t byid; entities_by_type_t bytype; @@ -170,6 +171,7 @@ public: void register_inverse(unsigned, Token); void register_inverse(unsigned, IfcUtil::IfcBaseClass*); void unregister_inverse(unsigned, IfcUtil::IfcBaseClass*); + const schema_definition* schema() const { return schema_; } }; } diff --git a/src/ifcparse/IfcParse.cpp b/src/ifcparse/IfcParse.cpp index e17635ae70..91f60628a1 100644 --- a/src/ifcparse/IfcParse.cpp +++ b/src/ifcparse/IfcParse.cpp @@ -39,6 +39,7 @@ #include "../ifcparse/IfcSpfStream.h" #include "../ifcparse/IfcFile.h" #include "../ifcparse/IfcSIPrefix.h" +#include "../ifcparse/IfcSchema.h" #ifdef USE_IFC4 #include "../ifcparse/Ifc4-latebound.h" diff --git a/src/ifcparse/IfcSchema.cpp b/src/ifcparse/IfcSchema.cpp new file mode 100644 index 0000000000..c56fe1b196 --- /dev/null +++ b/src/ifcparse/IfcSchema.cpp @@ -0,0 +1,21 @@ +#include "IfcSchema.h" + +bool IfcParse::declaration::is(const std::string& name) const { + return is(IfcSchema::Type::FromString(name)); +} + +bool IfcParse::declaration::is(IfcSchema::Type::Enum name) const { + if (this->as_entity()) { + return this->as_entity()->is(name); + } else { + return this->name() == IfcSchema::Type::ToString(name); + } +} + +bool IfcParse::named_type::is(const std::string& name) const { + return declared_type()->is(name); +} + +bool IfcParse::named_type::is(IfcSchema::Type::Enum name) const { + return declared_type()->is(name); +} \ No newline at end of file diff --git a/src/ifcparse/IfcSchema.h b/src/ifcparse/IfcSchema.h new file mode 100644 index 0000000000..fc0f99acfd --- /dev/null +++ b/src/ifcparse/IfcSchema.h @@ -0,0 +1,432 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell 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 * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#ifndef IFCSCHEMA_H +#define IFCSCHEMA_H + +#include +#include +#include +#include + +#include + +#include "../ifcparse/IfcException.h" + +#ifdef USE_IFC4 +#include "../ifcparse/Ifc4enum.h" +#else +#include "../ifcparse/Ifc2x3enum.h" +#endif + +namespace IfcParse { + + class declaration; + + class type_declaration; + class select_type; + class enumeration_type; + class entity; + + class named_type; + class simple_type; + class aggregation_type; + + class parameter_type { + public: + virtual const named_type* as_named_type() const { return static_cast(0); } + virtual const simple_type* as_simple_type() const { return static_cast(0); } + virtual const aggregation_type* as_aggregation_type() const { return static_cast(0); } + + virtual bool is(const std::string& /*name*/) const { return false; } + virtual bool is(IfcSchema::Type::Enum /*name*/) const { return false; } + }; + + class named_type : public parameter_type { + protected: + declaration* declared_type_; + public: + named_type(declaration* declared_type) + : declared_type_(declared_type) {} + + declaration* declared_type() const { return declared_type_; } + + virtual const named_type* as_named_type() const { return this; } + + virtual bool is(const std::string& name) const; + virtual bool is(IfcSchema::Type::Enum name) const; + }; + + class simple_type : public parameter_type { + public: + typedef enum { binary_type, boolean_type, integer_type, logical_type, number_type, real_type, string_type, datatype_COUNT } data_type; + protected: + data_type declared_type_; + public: + simple_type(data_type declared_type) + : declared_type_(declared_type) {} + + data_type declared_type() const { return declared_type_; } + + virtual const simple_type* as_simple_type() const { return this; } + }; + + class aggregation_type : public parameter_type { + public: + typedef enum { array_type, bag_type, list_type, set_type } aggregate_type; + protected: + aggregate_type type_of_aggregation_; + int bound1_, bound2_; + parameter_type* type_of_element_; + public: + aggregation_type(aggregate_type type_of_aggregation, int bound1, int bound2, parameter_type* type_of_element) + : type_of_aggregation_(type_of_aggregation) + , bound1_(bound1) + , bound2_(bound2) + , type_of_element_(type_of_element) + {} + + aggregate_type type_of_aggregation() const { type_of_aggregation_; } + int bound1() const { return bound1_; } + int bound2() const { return bound2_; } + parameter_type* type_of_element() const { return type_of_element_; } + + virtual const aggregation_type* as_aggregation_type() const { return this; } + }; + + class declaration { + protected: + // std::string name_; + IfcSchema::Type::Enum name_; + + public: + declaration(IfcSchema::Type::Enum name) + : name_(name) {} + declaration(const std::string& name) + : name_(IfcSchema::Type::FromString(name)) {} + + std::string name() const { return IfcSchema::Type::ToString(name_); } + + virtual const type_declaration* as_type_declaration() const { return static_cast(0); } + virtual const select_type* as_select_type() const { return static_cast(0); } + virtual const enumeration_type* as_enumeration_type() const { return static_cast(0); } + virtual const entity* as_entity() const { return static_cast(0); } + + // TODO: Type checking by Enum value + bool is(const std::string& name) const; + bool is(IfcSchema::Type::Enum name) const; + + IfcSchema::Type::Enum type() const { + return name_; + } + }; + + class type_declaration : public declaration { + protected: + const parameter_type* declared_type_; + + public: + type_declaration(const std::string& name, const parameter_type* declared_type) + : declaration(name) + , declared_type_(declared_type) {} + type_declaration(IfcSchema::Type::Enum name, const parameter_type* declared_type) + : declaration(name) + , declared_type_(declared_type) {} + + const parameter_type* declared_type() const { return declared_type_; } + + virtual const type_declaration* as_type_declaration() const { return this; } + }; + + class select_type : public declaration { + protected: + std::vector select_list_; + public: + select_type(const std::string& name, const std::vector& select_list) + : declaration(name) + , select_list_(select_list) {} + select_type(IfcSchema::Type::Enum name, const std::vector& select_list) + : declaration(name) + , select_list_(select_list) {} + + const std::vector& select_list() const { return select_list_; } + + virtual const select_type* as_select_type() const { return this; } + }; + + class enumeration_type : public declaration { + protected: + std::vector enumeration_items_; + public: + enumeration_type(const std::string& name, const std::vector& enumeration_items) + : declaration(name) + , enumeration_items_(enumeration_items) {} + enumeration_type(IfcSchema::Type::Enum name, const std::vector& enumeration_items) + : declaration(name) + , enumeration_items_(enumeration_items) {} + + const std::vector& enumeration_items() const { return enumeration_items_; } + + virtual const enumeration_type* as_enumeration_type() const { return this; } + }; + + class entity : public declaration { + public: + class attribute { + protected: + std::string name_; + const parameter_type* type_of_attribute_; + bool optional_; + + public: + attribute(const std::string& name, parameter_type* type_of_attribute, bool optional) + : name_(name) + , type_of_attribute_(type_of_attribute) + , optional_(optional) {} + + const std::string& name() const { return name_; } + const parameter_type* type_of_attribute() const { return type_of_attribute_; } + bool optional() const { return optional_; } + }; + + class inverse_attribute { + public: + typedef enum { bag_type, set_type, unspecified_type } aggregate_type; + protected: + std::string name_; + aggregate_type type_of_aggregation_; + int bound1_, bound2_; + parameter_type* type_of_element_; + const entity* entity_reference_; + const attribute* attribute_reference_; + public: + inverse_attribute(const std::string& name, aggregate_type type_of_aggregation, int bound1, int bound2, const entity* entity_reference, const attribute* attribute_reference) + : name_(name) + , type_of_aggregation_(type_of_aggregation) + , bound1_(bound1) + , bound2_(bound2) + , entity_reference_(entity_reference) + , attribute_reference_(attribute_reference) + {} + + const std::string& name() const { return name_; } + aggregate_type type_of_aggregation() const { type_of_aggregation_; } + int bound1() const { return bound1_; } + int bound2() const { return bound2_; } + const entity* entity_reference() const { return entity_reference_; } + const attribute* attribute_reference() const { return attribute_reference_; } + }; + + protected: + const entity* supertype_; /* NB: IFC explicitly allows only single inheritance */ + std::vector subtypes_; + + std::vector attributes_; + std::vector derived_; + + std::vector inverse_attributes_; + + class attribute_by_name_cmp : public std::unary_function { + private: + std::string name_; + public: + attribute_by_name_cmp(const std::string name) + : name_(name) {} + bool operator()(const attribute* attr) { + return attr->name() == name_; + } + }; + public: + entity(const std::string& name, entity* supertype) + : declaration(name) + , supertype_(supertype) + {} + entity(IfcSchema::Type::Enum name, entity* supertype) + : declaration(name) + , supertype_(supertype) + {} + + bool is(const std::string& name) const { + return is(IfcSchema::Type::FromString(name)); + } + + bool is(IfcSchema::Type::Enum name) const { + if (name == name_) return true; + else if (supertype_) return supertype_->is(name); + else return false; + } + + void set_subtypes(const std::vector& subtypes) { + subtypes_ = subtypes; + } + + void set_attributes(const std::vector& attributes, const std::vector& derived) { + attributes_ = attributes; + derived_ = derived; + } + + void set_inverse_attributes(const std::vector& inverse_attributes) { + inverse_attributes_ = inverse_attributes; + } + + const std::vector& subtypes() const { return subtypes_; } + const std::vector& attributes() const { return attributes_; } + const std::vector& derived() const { return derived_; } + + const std::vector all_attributes() const { + std::vector attrs; + attrs.reserve(derived_.size()); + if (supertype_) { + const std::vector supertype_attrs = supertype_->all_attributes(); + std::copy(supertype_attrs.begin(), supertype_attrs.end(), std::back_inserter(attrs)); + } + std::copy(attributes_.begin(), attributes_.end(), std::back_inserter(attrs)); + return attrs; + } + + const std::vector all_inverse_attributes() const { + std::vector attrs; + if (supertype_) { + const std::vector supertype_inv_attrs = supertype_->all_inverse_attributes(); + std::copy(supertype_inv_attrs.begin(), supertype_inv_attrs.end(), std::back_inserter(attrs)); + } + std::copy(inverse_attributes_.begin(), inverse_attributes_.end(), std::back_inserter(attrs)); + return attrs; + } + + ptrdiff_t attribute_index(const attribute* attr) const { + const entity* current = this; + ptrdiff_t index = -1; + do { + if (index > -1) { + index += current->attributes().size(); + } else { + auto it = std::find(current->attributes().begin(), current->attributes().end(), attr); + if (it != current->attributes().end()) { + index = std::distance(current->attributes().begin(), it); + } + } + } while (current = current->supertype_); + return index; + } + + ptrdiff_t attribute_index(const std::string& attr_name) const { + const entity* current = this; + ptrdiff_t index = -1; + attribute_by_name_cmp cmp(attr_name); + do { + if (index > -1) { + index += current->attributes().size(); + } else { + auto it = std::find_if(current->attributes().begin(), current->attributes().end(), cmp); + if (it != current->attributes().end()) { + index = std::distance(current->attributes().begin(), it); + } + } + } while (current = current->supertype_); + return index; + } + + const entity* supertype() const { return supertype_; } + + virtual const entity* as_entity() const { return this; } + }; + + class schema_definition { + private: + bool built_in_; + + std::string name_; + + std::vector declarations_; + + std::vector type_declarations_; + std::vector select_types_; + std::vector enumeration_types_; + std::vector entities_; + + class declaration_by_name_cmp : public std::binary_function { + public: + bool operator()(const declaration* decl, const std::string& name) { + // TODO: Efficiency? + return boost::to_lower_copy(decl->name()) < boost::to_lower_copy(name); + } + }; + + class declaration_by_enum_cmp : public std::binary_function { + public: + bool operator()(const declaration* decl, IfcSchema::Type::Enum name) { + return decl->type() < name; + } + }; + + class declaration_by_enum_sort : public std::binary_function { + public: + bool operator()(const declaration* a, const declaration* b) { + return a->type() < b->type(); + } + }; + + public: + schema_definition(const std::string& name, const std::vector& declarations, const bool built_in = false) + : name_(name) + , declarations_(declarations) + , built_in_(built_in) + { + std::sort(declarations_.begin(), declarations_.end(), declaration_by_enum_sort()); + for (std::vector::const_iterator it = declarations_.begin(); it != declarations_.end(); ++it) { + if ((**it).as_type_declaration()) type_declarations_.push_back((**it).as_type_declaration()); + if ((**it).as_select_type()) select_types_.push_back((**it).as_select_type()); + if ((**it).as_enumeration_type()) enumeration_types_.push_back((**it).as_enumeration_type()); + if ((**it).as_entity()) entities_.push_back((**it).as_entity()); + } + } + + ~schema_definition() { + for (std::vector::const_iterator it = declarations_.begin(); it != declarations_.end(); ++it) { + delete *it; + } + } + + const declaration* declaration_by_name(const std::string& name) const { + std::vector::const_iterator it = std::lower_bound(declarations_.begin(), declarations_.end(), name, declaration_by_name_cmp()); + if (it == declarations_.end() || boost::to_lower_copy((**it).name()) != boost::to_lower_copy(name)) { + throw IfcParse::IfcException("Entity with '" + name + "' not found"); + } else { + return *it; + } + } + + const declaration* declaration_by_name(IfcSchema::Type::Enum name) const { + if (!built_in_) throw; + return declarations_[name]; + } + + const std::vector& declarations() const { return declarations_; } + const std::vector& type_declarations() const { return type_declarations_; } + const std::vector& select_types() const { return select_types_; } + const std::vector& enumeration_types() const { return enumeration_types_; } + const std::vector& entities() const { return entities_; } + + const std::string& name() const { return name_; } + }; + +} + +#endif diff --git a/src/ifcparse/IfcUtil.cpp b/src/ifcparse/IfcUtil.cpp index 5c2546c5e7..8480f6fcdd 100644 --- a/src/ifcparse/IfcUtil.cpp +++ b/src/ifcparse/IfcUtil.cpp @@ -92,12 +92,6 @@ IfcEntityList::ptr IfcEntityList::unique() { return return_value; } - -unsigned int IfcUtil::IfcBaseType::getArgumentCount() const { return 1; } -Argument* IfcUtil::IfcBaseType::getArgument(unsigned int i) const { return entity->getArgument(i); } -const char* IfcUtil::IfcBaseType::getArgumentName(unsigned int i) const { if (i == 0) { return "wrappedValue"; } else { throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); } } - - //Note: some of these methods are overloaded in derived classes Argument::operator int() const { throw IfcParse::IfcException("Argument is not an integer"); } Argument::operator bool() const { throw IfcParse::IfcException("Argument is not a boolean"); } @@ -196,4 +190,13 @@ std::vector IfcUtil::IfcBaseEntity::getInverseAttributeNames() cons Argument* IfcUtil::IfcBaseEntity::getArgumentByName(const std::string& name) const { unsigned int i = IfcSchema::Type::GetAttributeIndex(type(), name); return getArgument(i); -} +} + +IfcUtil::IfcBaseClass::~IfcBaseClass() { + delete data_; +} + +void IfcUtil::IfcBaseClass::data(IfcAbstractEntity* d) { + delete data_; + data_ = d; +}