2015-08-13 14:28:50 +02:00
###############################################################################
# #
# 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 <http://www.gnu.org/licenses/>. #
# #
###############################################################################
import operator
import nodes
2017-12-11 13:43:06 +01:00
import codegen
2015-08-13 14:28:50 +02:00
import templates
2019-02-15 13:09:54 +01:00
from collections import defaultdict
2017-12-11 13:43:06 +01:00
class SchemaClass ( codegen . Base ) :
2015-08-13 14:28:50 +02:00
def __init__ ( self , mapping ) :
class UnmetDependenciesException ( Exception ) : pass
2015-09-08 20:40:54 +02:00
schema_name = mapping . schema . name
2017-12-13 13:16:49 +01:00
self . schema_name = schema_name_title = schema_name . capitalize ( )
2015-09-08 20:40:54 +02:00
declared_types = [ ]
2015-08-13 14:28:50 +02:00
def get_declared_type ( type , emitted_names = None ) :
2020-08-29 10:23:59 +02:00
if isinstance ( type , nodes . SimpleType ) :
type = type . type
if isinstance ( type , nodes . NamedType ) :
type = str ( type )
2015-08-13 14:28:50 +02:00
if isinstance ( type , nodes . AggregationType ) :
aggr_type = type . aggregate_type
make_bound = lambda b : - 1 if b == ' ? ' else int ( b )
2017-12-11 13:43:06 +01:00
bound1 , bound2 = map ( make_bound , ( type . bounds . lower , type . bounds . upper ) )
2018-01-29 15:03:12 +01:00
decl_type = get_declared_type ( type . type , emitted_names )
2015-08-13 14:28:50 +02:00
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) "
2017-12-11 14:20:30 +01:00
elif isinstance ( type , nodes . StringType ) :
return " new simple_type(simple_type::string_type) "
2015-08-13 14:28:50 +02:00
elif isinstance ( type , str ) :
if mapping . schema . is_type ( type ) or mapping . schema . is_entity ( type ) :
2018-01-29 15:03:12 +01:00
if emitted_names is None or type . lower ( ) in emitted_names :
2017-12-13 18:05:10 +01:00
return " new named_type( %s _ %s _type) " % ( schema_name , type )
2015-08-13 14:28:50 +02:00
else :
raise UnmetDependenciesException ( type )
else :
2017-12-11 13:43:06 +01:00
return " new simple_type(simple_type:: %s _type) " % type
2020-08-29 10:23:59 +02:00
else :
raise ValueError ( " No mapping for ' %s ' " % type )
2017-12-11 13:43:06 +01:00
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
2015-08-13 14:28:50 +02:00
2017-12-11 14:20:30 +01:00
else :
raise Exception ( " No declared type for < %r > " % type )
2015-09-08 20:40:54 +02:00
statements = [ ' ' ,
' #include " ../ifcparse/IfcSchema.h " ' ,
2017-12-13 13:16:49 +01:00
' #include " ../ifcparse/ %(schema_name_title)s .h " ' % locals ( ) ,
2015-09-08 20:40:54 +02:00
' ' ,
2017-12-13 13:16:49 +01:00
' using namespace IfcParse; ' ,
2015-09-08 20:40:54 +02:00
' ' ]
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 ( ) :
2017-12-13 18:05:10 +01:00
statements . append ( ' %(cpp_type)s * %(schema_name)s _ %(name)s _type = 0; ' % locals ( ) )
2017-12-13 13:16:49 +01:00
declarations_by_index = [ ]
statements . append ( " {factory_placeholder} " )
2017-12-11 14:20:30 +01:00
statements . append ( """
2017-12-29 12:21:20 +01:00
#if defined(__clang__)
2018-09-21 14:55:10 +02:00
__attribute__((optnone))
2017-12-29 12:21:20 +01:00
#elif defined(__GNUC__) || defined(__GNUG__)
#pragma GCC push_options
#pragma GCC optimize ( " O0 " )
#elif defined(_MSC_VER)
2017-12-11 14:20:30 +01:00
#pragma optimize( " " , off)
#endif
""" )
2017-12-13 18:05:10 +01:00
statements . append ( ' IfcParse::schema_definition* %(schema_name)s _populate_schema() { ' % locals ( ) )
2015-08-13 14:28:50 +02:00
2018-01-29 15:03:12 +01:00
emitted = set ( )
len_to_emit = len ( mapping . schema )
def write_simpletype ( schema_name , name , type ) :
try :
declared_type = get_declared_type ( type , emitted )
except UnmetDependenciesException :
2019-01-20 14:27:09 +01:00
# @todo?
# print("Unmet", repr(name))
2018-01-29 15:03:12 +01:00
return False
2015-08-13 14:28:50 +02:00
2018-01-29 15:03:12 +01:00
statements . append ( ' %(schema_name)s _ %(name)s _type = new type_declaration( " %(name)s " , %% (index_in_schema_ %(name)s )d, %(declared_type)s ); ' % locals ( ) )
def write_enumeration ( schema_name , name , enum ) :
2015-08-13 14:28:50 +02:00
statements . append ( ' { ' )
statements . append ( ' std::vector<std::string> items; items.reserve( %d ); ' % len ( enum . values ) )
statements . extend ( map ( lambda v : ' items.push_back( " %s " ); ' % v , sorted ( enum . values ) ) )
2017-12-13 18:05:10 +01:00
statements . append ( ' %(schema_name)s _ %(name)s _type = new enumeration_type( " %(name)s " , %% (index_in_schema_ %(name)s )d, items); ' % locals ( ) )
2015-08-13 14:28:50 +02:00
statements . append ( ' } ' )
2015-09-08 20:40:54 +02:00
2018-01-29 15:03:12 +01:00
def write_entity ( schema_name , name , type ) :
if len ( type . supertypes ) == 0 or set ( map ( lambda s : s . lower ( ) , type . supertypes ) ) < emitted :
supertype = ' 0 ' if len ( type . supertypes ) == 0 else ' %s _ %s _type ' % ( schema_name , type . supertypes [ 0 ] )
2019-02-20 15:35:19 +01:00
is_abstract = " true " if type . abstract else " false "
statements . append ( ' %(schema_name)s _ %(name)s _type = new entity( " %(name)s " , %(is_abstract)s , %% (index_in_schema_ %(name)s )d, %(supertype)s ); ' % locals ( ) )
2018-01-29 15:03:12 +01:00
else : return False
def write_select ( schema_name , name , type ) :
2020-08-29 10:23:59 +02:00
if set ( map ( lambda s : str ( s ) . lower ( ) , type . values ) ) < emitted :
2018-01-29 15:03:12 +01:00
statements . append ( ' { ' )
statements . append ( ' std::vector<const declaration*> items; items.reserve( %d ); ' % len ( type . values ) )
2020-08-29 10:23:59 +02:00
statements . extend ( map ( lambda v : ' items.push_back( %s _ %s _type); ' % ( schema_name , v ) , sorted ( map ( str , type . values ) ) ) )
2018-01-29 15:03:12 +01:00
statements . append ( ' %(schema_name)s _ %(name)s _type = new select_type( " %(name)s " , %% (index_in_schema_ %(name)s )d, items); ' % locals ( ) )
statements . append ( ' } ' )
else : return False
def write ( name ) :
if mapping . schema . is_simpletype ( name ) :
fn = write_simpletype
elif mapping . schema . is_enumeration ( name ) :
fn = write_enumeration
elif mapping . schema . is_entity ( name ) :
fn = write_entity
elif mapping . schema . is_select ( name ) :
fn = write_select
decl = mapping . schema [ name ]
if isinstance ( decl , nodes . TypeDeclaration ) :
2020-08-29 10:23:59 +02:00
decl = decl . type
2018-01-29 15:03:12 +01:00
return fn ( schema_name , name , decl ) is not False
2015-08-13 14:28:50 +02:00
2018-01-29 15:03:12 +01:00
while len ( emitted ) < len_to_emit :
for name in mapping . schema :
if name . lower ( ) in emitted : continue
if write ( name ) :
emitted . add ( name . lower ( ) )
2017-12-13 13:16:49 +01:00
declarations_by_index . append ( name )
2017-12-13 18:05:10 +01:00
declared_types . append ( ' %(schema_name)s _ %(name)s _type ' % locals ( ) )
2018-01-29 15:03:12 +01:00
2015-09-08 20:40:54 +02:00
num_declarations = len ( declared_types )
2015-08-13 14:28:50 +02:00
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 ( ' { ' )
2019-02-15 13:09:54 +01:00
statements . append ( ' std::vector<const attribute*> attributes; attributes.reserve( %d ); ' % len ( type . attributes ) )
2015-08-13 14:28:50 +02:00
for attr in type . attributes :
attr_name , optional = attr . name , str ( attr . optional ) . lower ( )
decl_type = get_declared_type ( attr . type )
2019-02-15 13:09:54 +01:00
statements . append ( ' attributes.push_back(new attribute( " %(attr_name)s " , %(decl_type)s , %(optional)s )); ' % locals ( ) )
2015-08-13 14:28:50 +02:00
statements . append ( ' std::vector<bool> derived; derived.reserve( %d ); ' % len ( attribute_names ) )
statements . append ( ' ' + " " . join ( map ( lambda b : ' derived.push_back( %s ); ' % str ( b in derived ) . lower ( ) , attribute_names ) ) )
2017-12-13 18:05:10 +01:00
statements . append ( ' %(schema_name)s _ %(name)s _type->set_attributes(attributes, derived); ' % locals ( ) )
2015-08-13 14:28:50 +02:00
statements . append ( ' } ' )
2015-09-08 20:40:54 +02:00
2017-12-11 13:43:06 +01:00
for name , type in mapping . schema . entities . items ( ) :
if type . inverse :
statements . append ( ' { ' )
2020-08-29 10:23:59 +02:00
statements . append ( ' std::vector<const inverse_attribute*> attributes; attributes.reserve( %d ); ' % len ( type . inverse ) )
for attr in type . inverse :
2017-12-11 13:43:06 +01:00
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 )
2019-02-15 13:09:54 +01:00
statements . append ( ' attributes.push_back(new inverse_attribute( " %(attr_name)s " , inverse_attribute:: %(aggr_type)s _type, %(bound1)d , %(bound2)d , %(schema_name)s _ %(entity_ref)s _type, %(schema_name)s _ %(attribute_entity)s _type->attributes()[ %(attribute_entity_index)d ])); ' % locals ( ) )
2017-12-13 18:05:10 +01:00
statements . append ( ' %(schema_name)s _ %(name)s _type->set_inverse_attributes(attributes); ' % locals ( ) )
2017-12-11 13:43:06 +01:00
statements . append ( ' } ' )
2019-02-15 13:09:54 +01:00
subtypes = defaultdict ( list )
for name , type in mapping . schema . entities . items ( ) :
for ty in type . supertypes :
subtypes [ ty ] . append ( name )
for name , tys in subtypes . items ( ) :
statements . append ( ' { ' )
statements . append ( ' std::vector<const entity*> defs; defs.reserve( %d ); ' % len ( tys ) )
statements . append ( ( ' ' + " " . join ( map ( lambda t : ( " defs.push_back( %% (schema_name)s_ %s _type); " % t ) , tys ) ) ) % locals ( ) )
statements . append ( ' %(schema_name)s _ %(name)s _type->set_subtypes(defs); ' % locals ( ) )
statements . append ( ' } ' )
2017-12-11 13:43:06 +01:00
2015-09-08 20:40:54 +02:00
statements . append ( ' ' )
statements . append ( ' std::vector<const declaration*> declarations; declarations.reserve( %(num_declarations)d ); ' % locals ( ) )
for type_name in declared_types :
statements . append ( ' declarations.push_back( %(type_name)s ); ' % locals ( ) )
2017-12-13 13:16:49 +01:00
statements . append ( ' return new schema_definition( " %(schema_name)s " , declarations, new %(schema_name)s _instance_factory()); ' % locals ( ) )
2015-09-08 20:40:54 +02:00
statements . extend ( ( ' } ' , ' ' ) )
2017-12-11 14:20:30 +01:00
statements . append ( """
2017-12-29 12:21:20 +01:00
#if defined(__clang__)
#elif defined(__GNUC__) || defined(__GNUG__)
#pragma GCC pop_options
#elif defined(_MSC_VER)
2017-12-11 14:20:30 +01:00
#pragma optimize( " " , on)
#endif
""" )
2017-12-11 13:43:06 +01:00
2017-12-18 15:25:09 +01:00
statements . extend ( ( ' const schema_definition& %s ::get_schema() { ' % schema_name_title ,
2015-09-08 20:40:54 +02:00
' ' ,
2017-12-13 18:05:10 +01:00
' static const schema_definition* s = %(schema_name)s _populate_schema(); ' % locals ( ) ,
2015-09-08 20:40:54 +02:00
' return *s; ' ,
2017-12-18 15:25:09 +01:00
' } ' , ' ' , ' ' ) )
2015-09-08 20:40:54 +02:00
2017-12-21 14:14:04 +01:00
declarations_by_index . sort ( key = str . lower )
2017-12-13 13:16:49 +01:00
declarations_by_index_map = dict ( ( " index_in_schema_ %s " % j , i ) for i , j in enumerate ( declarations_by_index ) )
def bind ( s ) :
if " % " in s : return s % declarations_by_index_map
else : return s
can_be_instantiated_set = set ( list ( mapping . schema . entities . keys ( ) ) + list ( mapping . schema . simpletypes . keys ( ) ) )
def can_be_instantiated ( idx_name ) :
name = idx_name [ 1 ]
return name in can_be_instantiated_set
instance_mapping = """ switch(data->type()->index_in_schema()) {
%s
default: throw IfcParse::IfcException(data->type()->name() + " cannot be instantiated " );
}
2017-12-13 18:05:10 +01:00
""" % " \n " . join ( map ( lambda tup : ( " case %% d: return new :: %s :: %% s(data); " % schema_name_title ) % tup , filter ( can_be_instantiated , enumerate ( declarations_by_index ) ) ) )
2017-12-13 13:16:49 +01:00
statements [ statements . index ( " {factory_placeholder} " ) ] = """
class %(schema_name)s _instance_factory : public IfcParse::instance_factory {
virtual IfcUtil::IfcBaseClass* operator()(IfcEntityInstanceData* data) const {
%(instance_mapping)s
}
};
""" % locals ( )
self . str = " \n " . join ( map ( bind , statements ) )
2017-12-11 13:43:06 +01:00
self . file_name = ' %s -schema.cpp ' % self . schema_name
2015-08-13 14:28:50 +02:00
def __repr__ ( self ) :
return self . str
2020-05-15 14:28:56 +02:00
Generator = SchemaClass