def __init__(self, yamlfile, install_dir, package_name, io_handlers, verbose, dryrun): self.install_dir = install_dir self.package_name = package_name self.io_handlers = io_handlers self.verbose = verbose self.dryrun = dryrun self.yamlfile = yamlfile self.reader = PodioConfigReader(yamlfile) self.reader.read() self.include_subfolder = self.reader.options["includeSubfolder"] self.env = jinja2.Environment( loader=jinja2.FileSystemLoader(TEMPLATE_DIR), keep_trailing_newline=True, lstrip_blocks=True, trim_blocks=True) self.get_syntax = self.reader.options["getSyntax"] self.incfolder = self.package_name + "/" if self.reader.options[ "includeSubfolder"] else "" self.expose_pod_members = self.reader.options["exposePODMembers"] self.clang_format = []
def __init__(self, yamlfile, install_dir, package_name, verbose=True, getSyntax=False): self.yamlfile = yamlfile self.install_dir = install_dir self.package_name = package_name self.template_dir = os.path.join(thisdir, "../templates") self.verbose = verbose self.getSyntax = getSyntax self.buildin_types = ClassDefinitionValidator.buildin_types self.created_classes = [] self.requested_classes = [] self.reader = PodioConfigReader(yamlfile) self.warnings = []
def __init__(self,yamlfile, install_dir, package_name, verbose = True): self.yamlfile = yamlfile self.install_dir = install_dir self.package_name =package_name self.template_dir = os.path.join(thisdir,"../templates") self.verbose=verbose self.buildin_types = ["int","float","double","unsigned int","unsigned","short","bool","longlong","ulonglong"] self.created_classes = [] self.requested_classes = [] self.reader = PodioConfigReader(yamlfile) self.warnings = []
class ClassGenerator(object): def __init__(self, yamlfile, install_dir, package_name, io_handlers, verbose, dryrun): self.install_dir = install_dir self.package_name = package_name self.io_handlers = io_handlers self.verbose = verbose self.dryrun = dryrun self.yamlfile = yamlfile self.reader = PodioConfigReader(yamlfile) self.reader.read() self.include_subfolder = self.reader.options["includeSubfolder"] self.env = jinja2.Environment( loader=jinja2.FileSystemLoader(TEMPLATE_DIR), keep_trailing_newline=True, lstrip_blocks=True, trim_blocks=True) self.get_syntax = self.reader.options["getSyntax"] self.incfolder = self.package_name + "/" if self.reader.options[ "includeSubfolder"] else "" self.expose_pod_members = self.reader.options["exposePODMembers"] self.clang_format = [] def process(self): for name, component in self.reader.components.items(): self._process_component(name, component) for name, datatype in self.reader.datatypes.items(): self._process_datatype(name, datatype) if 'ROOT' in self.io_handlers: self._create_selection_xml() self.print_report() def print_report(self): if not self.verbose: return with open(os.path.join(THIS_DIR, "figure.txt"), 'rb') as pkl: figure = pickle.load(pkl) nclasses = 5 * len(self.reader.datatypes) + len(self.reader.components) text = REPORT_TEXT.format(yamlfile=self.yamlfile, nclasses=nclasses, installdir=self.install_dir) print() for figline, summaryline in zip_longest(figure, text.splitlines(), fillvalue=''): print(figline + summaryline) print(" 'Homage to the Square' - Josef Albers") print() def _eval_template(self, template, data): """Fill the specified template""" return self.env.get_template(template).render(data) def _write_file(self, name, content): """Write the content to file. Dispatch to the correct directory depending on whether it is a header or a .cc file.""" if name.endswith("h"): fullname = os.path.join(self.install_dir, self.package_name, name) else: fullname = os.path.join(self.install_dir, "src", name) if not self.dryrun: if self.clang_format: cfproc = subprocess.Popen(self.clang_format, stdin=subprocess.PIPE, stdout=subprocess.PIPE) content = cfproc.communicate( input=content.encode())[0].decode() try: with open(fullname, 'r') as f: existing_content = f.read() changed = existing_content != content except EnvironmentError as e: # If we deprecate python2 support, FileNotFoundError becomes available # and this can be using it. For now we keep it compatible with both # versions if e.errno != errno.ENOENT: raise changed = True if changed: with open(fullname, 'w') as f: f.write(content) @staticmethod def _get_filenames_templates(template_base, name): """Get the list of output filenames and corresponding template names""" # depending on which category is passed different naming conventions apply # for the generated files. Additionally not all categories need source files. # Listing the special cases here fn_base = { 'Data': 'Data', 'Obj': 'Obj', 'ConstObject': 'Const', 'PrintInfo': 'PrintInfo', 'Object': '', 'Component': '', 'SIOBlock': 'SIOBlock', }.get(template_base, template_base) endings = { 'Data': ('h', ), 'Component': ('h', ), 'PrintInfo': ('h', ) }.get(template_base, ('h', 'cc')) fn_templates = [] for ending in endings: template_name = '{fn}.{end}.jinja2'.format(fn=template_base, end=ending) filename = '{name}{fn}.{end}'.format(fn=fn_base, name=name, end=ending) fn_templates.append((filename, template_name)) return fn_templates def _fill_templates(self, template_base, data): """Fill the template and write the results to file""" # Update the passed data with some global things that are the same for all # files data['package_name'] = self.package_name data['use_get_syntax'] = self.get_syntax data['incfolder'] = self.incfolder for filename, template in self._get_filenames_templates( template_base, data['class'].bare_type): self._write_file(filename, self._eval_template(template, data)) def _process_component(self, name, component): """Process one component""" includes = set() if any(m.is_array for m in component['Members']): includes.add('#include <array>') for member in component['Members']: if member.full_type in self.reader.components or member.array_type in self.reader.components: includes.add(self._build_include(member.bare_type)) includes.update( component.get("ExtraCode", {}).get("includes", "").split('\n')) component['includes'] = self._sort_includes(includes) component['class'] = DataType(name) self._fill_templates('Component', component) def _process_datatype(self, name, definition): """Process one datatype""" datatype = self._preprocess_datatype(name, definition) self._fill_templates('Data', datatype) self._fill_templates('Object', datatype) self._fill_templates('ConstObject', datatype) self._fill_templates('Obj', datatype) self._fill_templates('Collection', datatype) if 'SIO' in self.io_handlers: self._fill_templates('SIOBlock', datatype) def _preprocess_for_obj(self, datatype): """Do the preprocessing that is necessary for the Obj classes""" fwd_declarations = {} includes, includes_cc = set(), set() for relation in datatype['OneToOneRelations']: if not relation.is_builtin: relation.relation_type = relation.as_qualified_const() if relation.full_type != datatype['class'].full_type: if relation.namespace not in fwd_declarations: fwd_declarations[relation.namespace] = [] fwd_declarations[relation.namespace].append('Const' + relation.bare_type) includes_cc.add( self._build_include(relation.bare_type + 'Const')) if datatype['VectorMembers'] or datatype['OneToManyRelations']: includes.add('#include <vector>') includes.add('#include "podio/RelationRange.h"') for relation in datatype['VectorMembers'] + datatype[ 'OneToManyRelations']: if not relation.is_builtin: if relation.full_type not in self.reader.components: relation.relation_type = relation.as_qualified_const() if relation.full_type == datatype['class'].full_type: includes_cc.add( self._build_include(datatype['class'].bare_type)) else: includes.add(self._build_include(relation.bare_type)) datatype['forward_declarations_obj'] = fwd_declarations datatype['includes_obj'] = self._sort_includes(includes) datatype['includes_cc_obj'] = self._sort_includes(includes_cc) def _preprocess_for_class(self, datatype): """Do the preprocessing that is necessary for the classes and Const classes""" includes = set(datatype['includes_data']) fwd_declarations = {} includes_cc = set() for member in datatype["Members"]: if self.expose_pod_members and not member.is_builtin and not member.is_array: member.sub_members = self.reader.components[ member.full_type]['Members'] for relation in datatype['OneToOneRelations']: if self._needs_include(relation): if relation.namespace not in fwd_declarations: fwd_declarations[relation.namespace] = [] fwd_declarations[relation.namespace].append(relation.bare_type) fwd_declarations[relation.namespace].append('Const' + relation.bare_type) includes_cc.add(self._build_include(relation.bare_type)) if datatype['VectorMembers'] or datatype['OneToManyRelations']: includes.add('#include <vector>') for relation in datatype['OneToManyRelations']: if self._needs_include(relation): includes.add(self._build_include(relation.bare_type)) elif relation.is_array: includes.add('#include <array>') if not relation.is_builtin_array: includes.add(self._build_include(relation.array_bare_type)) for vectormember in datatype['VectorMembers']: if vectormember.full_type in self.reader.components: includes.add(self._build_include(vectormember.bare_type)) includes.update( datatype.get('ExtraCode', {}).get('includes', '').split('\n')) includes.update( datatype.get('ConstExtraCode', {}).get('includes', '').split('\n')) # When we have a relation to the same type we have the header that we are # just generating in the includes. This would lead to a circular include, so # remove "ourselves" again from the necessary includes try: includes.remove(self._build_include(datatype['class'].bare_type)) except KeyError: pass datatype['includes'] = self._sort_includes(includes) datatype['includes_cc'] = self._sort_includes(includes_cc) datatype['forward_declarations'] = fwd_declarations def _preprocess_for_collection(self, datatype): """Do the necessary preprocessing for the collection""" includes_cc = set() for relation in datatype['OneToManyRelations'] + datatype[ 'OneToOneRelations']: includes_cc.add( self._build_include(relation.bare_type + 'Collection')) if datatype['VectorMembers']: includes_cc.add('#include <numeric>') datatype['includes_coll_cc'] = self._sort_includes(includes_cc) # the ostream operator needs a bit of help from the python side in the form # of some pre processing but also in the form of formatting, both are done # here. # TODO: also handle array members properly. These are currently simply # ignored header_contents = [] for member in datatype['Members']: header = {'name': member.name} if member.full_type in self.reader.components: comps = [ c.name for c in self.reader.components[member.full_type] ['Members'] ] header['components'] = comps header_contents.append(header) def ostream_collection_header(member_header, col_width=12): """Custom filter for the jinja2 templates to handle the ostream header that is printed for the collections. Need this custom filter because it is easier to implement the content dependent width in python than in jinja2. """ if not isinstance(member_header, Mapping): # Assume that we have a string and format it according to the width return '{{:>{width}}}'.format( width=col_width).format(member_header) components = member_header.get('components', None) name = member_header['name'] if components is None: return '{{:>{width}}}'.format(width=col_width).format(name) n_comps = len(components) comp_str = '[ {}]'.format(', '.join(components)) return '{{:>{width}}}'.format( width=col_width * n_comps).format(name + ' ' + comp_str) datatype['ostream_collection_settings'] = { 'header_contents': header_contents } # Register the custom filter for it to become available in the templates self.env.filters[ 'ostream_collection_header'] = ostream_collection_header def _preprocess_datatype(self, name, definition): """Preprocess the datatype (building includes, etc.)""" # Make a copy here and add the preprocessing steps to that such that the # original definition can be left untouched data = deepcopy(definition) data['class'] = DataType(name) data['includes_data'] = self._get_member_includes( definition["Members"]) data['is_pod'] = self._is_pod_type(definition["Members"]) self._preprocess_for_class(data) self._preprocess_for_obj(data) self._preprocess_for_collection(data) return data def _get_member_includes(self, members): """Process all members and gather the necessary includes""" includes = set() for member in members: if member.is_array: includes.add("#include <array>") if not member.is_builtin_array: includes.add(self._build_include(member.array_bare_type)) for stl_type in ClassDefinitionValidator.allowed_stl_types: if member.full_type == 'std::' + stl_type: includes.add("#include <{}>".format(stl_type)) if self._needs_include(member): includes.add(self._build_include(member.bare_type)) return self._sort_includes(includes) @staticmethod def _is_pod_type(members): """Check if the members of the class define a POD type""" for stl_type in ClassDefinitionValidator.allowed_stl_types: full_stl_type = 'std::' + stl_type if any(m.full_type.startswith(full_stl_type) for m in members): return False return True def _needs_include(self, member): """Check whether the member needs an include from within the datamodel""" return member.full_type in self.reader.components or member.full_type in self.reader.datatypes def _create_selection_xml(self): """Create the selection xml that is necessary for ROOT I/O""" data = { 'components': [DataType(c) for c in self.reader.components.keys()], 'datatypes': [DataType(d) for d in self.reader.datatypes.keys()] } self._write_file('selection.xml', self._eval_template('selection.xml.jinja2', data)) def _build_include(self, classname): """Return the include statement.""" if self.include_subfolder: classname = os.path.join(self.package_name, classname) return '#include "%s.h"' % classname def _sort_includes(self, includes): """Sort the includes in order to try to have the std includes at the bottom""" package_includes = sorted(i for i in includes if self.package_name in i) podio_includes = sorted(i for i in includes if 'podio' in i) stl_includes = sorted(i for i in includes if '<' in i and '>' in i) # TODO: check whether there are includes that fulfill more than one of the # above conditions? return package_includes + podio_includes + stl_includes
class ClassGenerator(object): def __init__(self,yamlfile, install_dir, package_name, verbose = True): self.yamlfile = yamlfile self.install_dir = install_dir self.package_name =package_name self.template_dir = os.path.join(thisdir,"../templates") self.verbose=verbose self.buildin_types = ["int","float","double","unsigned int","unsigned","short","bool","longlong","ulonglong"] self.created_classes = [] self.requested_classes = [] self.reader = PodioConfigReader(yamlfile) self.warnings = [] def load_data_definitions(self): self.datatypes = self.reader.read() def process(self): stream = open(self.yamlfile, "r") content = yaml.load(stream) if content.has_key("components"): self.components = content["components"] self.process_components(content["components"]) self.load_data_definitions() self.process_datatypes(self.datatypes) self.create_selection_xml() self.print_report() def process_components(self,content): for name in content.iterkeys(): self.requested_classes.append(name) for name, components in content.iteritems(): self.create_component(name, components) def process_datatypes(self,content): for name in content.iterkeys(): self.requested_classes.append(name) self.requested_classes.append("%sData" %name) for name, components in content.iteritems(): self.create_data(name, components) self.create_class(name, components) self.create_collection(name, components) self.create_obj(name, components) def print_report(self): if self.verbose: pkl = open(os.path.join(thisdir,"figure.txt")) figure = pickle.load(pkl) text = _text_ % (self.yamlfile, len(self.created_classes), self.install_dir ) for i, line in enumerate(figure): print print line+text.splitlines()[i], print " 'Homage to the Square' - Josef Albers" print def get_template(self,filename): templatefile = os.path.join(self.template_dir,filename) return open(templatefile,"r").read() def create_selection_xml(self): content = "" for klass in self.created_classes: #if not klass.endswith("Collection") or klass.endswith("Data"): content += ' <class name="std::vector<%s>" />\n' %klass content += """ <class name="%s"> <field name="m_registry" transient="true"/> <field name="m_container" transient="true"/> </class>\n""" %klass templatefile = os.path.join(self.template_dir,"selection.xml.template") template = open(templatefile,"r").read() content = string.Template(template).substitute({"classes" : content}) self.write_file("selection.xml",content) def create_data(self, classname, definition): # check whether all member types are known # and prepare include directives namespace_open = "" namespace_close = "" if "::" in classname: namespace, rawclassname = classname.split("::") namespace_open = "namespace %s {" % namespace namespace_close = "} // namespace %s" % namespace else: rawclassname = classname includes = "" description = definition["Description"] author = definition["Author"] members = definition["Members"] for member in members: klass = member["type"] name = member["type"] if klass in self.buildin_types: pass elif klass in self.requested_classes: # includes += '#include "%s/%s.h"\n' %(self.package_name,klass) if "::" in klass: namespace, klassname = klass.split("::") includes += '#include "%s.h"\n' % klassname else: includes += '#include "%s.h"\n' %(klass) elif "std::array" in klass: includes += "#include <array>\n" self.created_classes.append(klass) elif "vector" in klass: includes += "#include <vector>\n" self.warnings.append("%s defines a vector member %s, that spoils the PODness" %(classname, klass)) elif "[" in klass: raise Exception("'%s' defines an array type. Array types are not supported yet." %(classname, klass)) else: raise Exception("'%s' defines a member of a type '%s' that is not (yet) declared!" %(classname, klass)) membersCode = "" for member in members: name = member["name"] klass = member["type"] description = member["description"] membersCode+= " %s %s; ///%s \n" %(klass, name, description) # now handle the vector-members vectormembers = definition["VectorMembers"] for vectormember in vectormembers: name = vectormember["name"] membersCode+= " unsigned int %s_begin; \n" %(name) membersCode+= " unsigned int %s_end; \n" %(name) # now handle the one-to-many relations refvectors = definition["OneToManyRelations"] for refvector in refvectors: name = refvector["name"] membersCode+= " unsigned int %s_begin; \n" %(name) membersCode+= " unsigned int %s_end; \n" %(name) substitutions = {"includes" : includes, "members" : membersCode, "name" : rawclassname, "description" : description, "author" : author, "package_name" : self.package_name, "namespace_open" : namespace_open, "namespace_close" : namespace_close } self.fill_templates("Data",substitutions) self.created_classes.append(classname+"Data") def create_class(self, classname, definition): namespace_open = "" namespace_close = "" if "::" in classname: namespace, rawclassname = classname.split("::") namespace_open = "namespace %s {" % namespace namespace_close = "} // namespace %s" % namespace else: rawclassname = classname includes = "" includes_cc = "" forward_declarations = "" forward_declarations_namespace = {"":[]} getter_implementations = "" setter_implementations = "" getter_declarations = "" setter_declarations = "" constructor_signature = "" constructor_body = "" ConstGetter_implementations = "" # check whether all member types are known # and prepare include directives includes += '#include "%s.h"\n' %(rawclassname+"Data") #includes += '#include "%s/%s.h"\n' %(self.package_name,classname+"Data") description = definition["Description"] author = definition["Author"] members = definition["Members"] for member in members: klass = member["type"] if klass in self.buildin_types: pass elif klass in self.requested_classes: #includes += '#include "%s/%s.h"\n' %(self.package_name,klass) if "::" in klass: namespace, klassname = klass.split("::") includes += '#include "%s.h"\n' %klassname else: includes += '#include "%s.h"\n' %klass elif "std::array" in klass: includes += "#include <array>\n" elif "vector" in klass: includes += "#include <vector>\n" else: raise Exception("'%s' defines a member of a type '%s' that is not declared!" %(classname, klass)) # check on-to-one relations and prepare include directives oneToOneRelations = definition["OneToOneRelations"] for member in oneToOneRelations: klass = member["type"] if klass in self.requested_classes: mnamespace = "" klassname = klass if "::" in klass: mnamespace, klassname = klass.split("::") if mnamespace not in forward_declarations_namespace.keys(): forward_declarations_namespace[mnamespace] = [] forward_declarations_namespace[mnamespace] += ["class %s;\n" %(klassname)] forward_declarations_namespace[mnamespace] += ["class Const%s;\n" %(klassname)] includes_cc += '#include "%s.h"\n' %(klassname) for nsp in forward_declarations_namespace.iterkeys(): if nsp != "": forward_declarations += "namespace %s {\n" % nsp forward_declarations += "".join(forward_declarations_namespace[nsp]) if nsp != "": forward_declarations += "}\n" # check one-to-many relations for consistency # and prepare include directives refvectors = definition["OneToManyRelations"] if len(refvectors) != 0: includes += "#include <vector>\n" for item in refvectors: klass = item["type"] if klass in self.requested_classes: if "::" in klass: mnamespace, klassname = klass.split("::") includes += '#include "%s.h"\n' %klassname else: includes += '#include "%s.h"\n' %klass elif "std::array" in klass: includes += "#include <array>\n" else: raise Exception("'%s' declares a non-allowed many-relation to '%s'!" %(classname, klass)) # handle standard members for member in members: name = member["name"] klass = member["type"] description = member["description"] getter_declarations+= " const %s& %s() const;\n" %(klass, name) getter_implementations+= " const %s& %s::%s() const { return m_obj->data.%s; }\n" %(klass,rawclassname,name, name) # getter_declarations+= " %s& %s() { return m_obj->data.%s; };\n" %(klass, name, name) if klass in self.buildin_types: setter_declarations += " void %s(%s value);\n" %(name, klass) setter_implementations += "void %s::%s(%s value){ m_obj->data.%s = value;}\n" %(rawclassname, name, klass, name) else: setter_declarations += " %s& %s();\n" %(klass, name) # getting non-const reference is conceptually a setter setter_implementations += " %s& %s::%s() { return m_obj->data.%s; }\n" %(klass, rawclassname,name, name) setter_declarations += " void %s(class %s value);\n" %(name, klass) setter_implementations += "void %s::%s(class %s value){ m_obj->data.%s = value;}\n" %(rawclassname, name, klass, name) # set up signature constructor_signature += "%s %s," %(klass, name) # constructor constructor_body += " m_obj->data.%s = %s;" %(name, name) ConstGetter_implementations += " const %s& Const%s::%s() const { return m_obj->data.%s; }\n" %(klass, rawclassname, name, name) # one-to-one relations for member in oneToOneRelations: name = member["name"] klass = member["type"] mnamespace = "" klassname = klass if "::" in klass: mnamespace, klassname = klass.split("::") setter_declarations += " void %s(%s::Const%s value);\n" %(name, mnamespace, klassname) setter_implementations += "void %s::%s(%s::Const%s value) { if (m_obj->m_%s != nullptr) delete m_obj->m_%s; m_obj->m_%s = new Const%s(value); }\n" %(rawclassname, name, mnamespace, klassname, name, name, name, klassname) getter_declarations += " const %s::Const%s %s() const;\n" %(mnamespace, klassname, name) getter_implementations += " const %s::Const%s %s::%s() const { if (m_obj->m_%s == nullptr) {\n return %s::Const%s(nullptr);}\n return %s::Const%s(*(m_obj->m_%s));}\n" \ %(mnamespace, klassname, rawclassname, name, name, mnamespace, klassname, mnamespace, klassname, name) ConstGetter_implementations += " const %s::Const%s Const%s::%s() const { if (m_obj->m_%s == nullptr) {\n return %s::Const%s(nullptr);}\n return %s::Const%s(*(m_obj->m_%s));}\n" \ %(mnamespace, klassname, rawclassname, name, name, mnamespace, klassname, mnamespace, klassname, name) else: setter_declarations += " void %s(Const%s value);\n" %(name, klassname) setter_implementations += "void %s::%s(Const%s value) { if (m_obj->m_%s != nullptr) delete m_obj->m_%s; m_obj->m_%s = new Const%s(value); }\n" %(rawclassname,name, klassname, name, name, name,klassname) getter_declarations += " const Const%s %s() const;\n" %(klassname, name) getter_implementations += " const Const%s %s::%s() const { if (m_obj->m_%s == nullptr) {\n return Const%s(nullptr);}\n return Const%s(*(m_obj->m_%s));}\n" \ %(klassname, rawclassname, name, name, klassname, klassname, name) ConstGetter_implementations += " const Const%s Const%s::%s() const { if (m_obj->m_%s == nullptr) {\n return Const%s(nullptr);}\n return Const%s(*(m_obj->m_%s));}\n" \ %(klassname, rawclassname, name, name, klassname, klassname, name) # handle vector members vectormembers = definition["VectorMembers"] if len(vectormembers) != 0: includes += "#include <vector>\n" for item in vectormembers: klass = item["type"] if klass not in self.buildin_types and klass not in self.components: raise Exception("'%s' declares a non-allowed vector member of type '%s'!" %(classname, klass)) if klass in self.components: if "::" in klass: namespace, klassname = klass.split("::") includes += '#include "%s.h"\n' %klassname else: includes += '#include "%s.h"\n' %klass # handle constructor from values constructor_signature = constructor_signature.rstrip(",") if constructor_signature == "": constructor_implementation = "" constructor_declaration = "" ConstConstructor_declaration = "" ConstConstructor_implementation = "" else: substitutions = { "name" : rawclassname, "constructor" : constructor_body, "signature" : constructor_signature } constructor_implementation = self.evaluate_template("Object.constructor.cc.template",substitutions) constructor_declaration = " %s(%s);\n" %(rawclassname, constructor_signature) ConstConstructor_implementation = self.evaluate_template("ConstObject.constructor.cc.template",substitutions) ConstConstructor_declaration = "Const%s(%s);\n" %(rawclassname, constructor_signature) # handle one-to-many relations references_members = "" references_declarations = "" references = "" ConstReferences_declarations = "" ConstReferences = "" references_template = self.get_template("RefVector.cc.template") references_declarations_template = self.get_template("RefVector.h.template") ConstReferences_declarations_template = self.get_template("ConstRefVector.h.template") ConstReferences_template = self.get_template("ConstRefVector.cc.template") for refvector in refvectors+definition["VectorMembers"]: relationtype = refvector["type"] if relationtype not in self.buildin_types and relationtype not in self.components: relationtype = "Const"+relationtype substitutions = {"relation" : refvector["name"], "relationtype" : relationtype, "classname" : rawclassname, "package_name" : self.package_name } references_declarations += string.Template(references_declarations_template).substitute(substitutions) references += string.Template(references_template).substitute(substitutions) references_members += "std::vector<%s>* m_%s; //! transient \n" %(refvector["type"], refvector["name"]) ConstReferences_declarations += string.Template(ConstReferences_declarations_template).substitute(substitutions) ConstReferences += string.Template(ConstReferences_template).substitute(substitutions) substitutions = {"includes" : includes, "includes_cc" : includes_cc, "forward_declarations" : forward_declarations, "getters" : getter_implementations, "getter_declarations": getter_declarations, "setters" : setter_implementations, "setter_declarations": setter_declarations, "constructor_declaration" : constructor_declaration, "constructor_implementation" : constructor_implementation, "name" : rawclassname, "description" : description, "author" : author, "relations" : references, "relation_declarations" : references_declarations, "relation_members" : references_members, "" "package_name" : self.package_name, "namespace_open" : namespace_open, "namespace_close" : namespace_close } self.fill_templates("Object",substitutions) self.created_classes.append(classname) substitutions["constructor_declaration"] = ConstConstructor_declaration substitutions["constructor_implementation"] = ConstConstructor_implementation substitutions["relation_declarations"] = ConstReferences_declarations substitutions["relations"] = ConstReferences substitutions["getters"] = ConstGetter_implementations self.fill_templates("ConstObject", substitutions) if "::" in classname: self.created_classes.append("%s::Const%s" %(namespace, rawclassname)) else: self.created_classes.append("Const%s" %classname) def create_collection(self, classname, definition): namespace_open = "" namespace_close = "" if "::" in classname: namespace, rawclassname = classname.split("::") namespace_open = "namespace %s {" % namespace namespace_close = "} // namespace %s" % namespace else: rawclassname = classname members = definition["Members"] constructorbody = "" prepareforwritinghead = "" prepareforwritingbody = "" vectorized_access_decl, vectorized_access_impl = self.prepare_vectorized_access(rawclassname,members) setreferences = "" prepareafterread = "" includes = "" initializers = "" relations = "" create_relations = "" clear_relations = "" push_back_relations = "" prepareafterread_refmembers = "" prepareforwriting_refmembers = "" refmembers = definition["OneToOneRelations"] refvectors = definition["OneToManyRelations"] nOfRefVectors = len(refvectors) nOfRefMembers = len(refmembers) if nOfRefVectors + nOfRefMembers > 0: # member initialization constructorbody += " m_refCollections = new podio::CollRefCollection();\n" clear_relations += " for (auto& pointer : (*m_refCollections)) {pointer->clear(); }\n" for counter, item in enumerate(refvectors): name = item["name"] klass = item["type"] substitutions = { "counter" : counter, "class" : klass, "name" : name } mnamespace = "" klassname = klass if "::" in klass: mnamespace, klassname = klass.split("::") # includes includes += '#include "%sCollection.h" \n' %(klassname) if mnamespace != "": # members relations += " std::vector<::%s::Const%s>* m_rel_%s; //relation buffer for r/w\n" %(mnamespace, klassname, name) relations += " std::vector<std::vector<::%s::Const%s>*> m_rel_%s_tmp;\n " %(mnamespace, klassname, name) # constructor calls initializers += ",m_rel_%s(new std::vector<::%s::Const%s>())" %(name, mnamespace, klassname) else: # members relations += " std::vector<Const%s>* m_rel_%s; //relation buffer for r/w\n" %(klass, name) relations += " std::vector<std::vector<Const%s>*> m_rel_%s_tmp;\n " %(klass, name) # constructor calls initializers += ",m_rel_%s(new std::vector<Const%s>())" %(name, klass) constructorbody += " m_refCollections->push_back(new std::vector<podio::ObjectID>());\n" # relation handling in ::create create_relations += " m_rel_%s_tmp.push_back(obj->m_%s);\n" %(name,name) # relation handling in ::clear clear_relations += " // clear relations to %s. Make sure to unlink() the reference data as they may be gone already\n" %(name) clear_relations += " for (auto& pointer : m_rel_%s_tmp) {for(auto& item : (*pointer)) {item.unlink();}; delete pointer;}\n" %(name) clear_relations += " m_rel_%s_tmp.clear();\n" %(name) clear_relations += " for (auto& item : (*m_rel_%s)) {item.unlink(); }\n" %(name) clear_relations += " m_rel_%s->clear();\n" %(name) # relation handling in push_back push_back_relations += " m_rel_%s_tmp.push_back(obj->m_%s);\n" %(name,name) # relation handling in ::prepareForWrite prepareforwritingbody += self.evaluate_template("CollectionPrepareForWriting.cc.template",substitutions) # relation handling in ::settingReferences setreferences += self.evaluate_template("CollectionSetReferences.cc.template",substitutions) prepareafterread += " obj->m_%s = m_rel_%s;" %(name, name) for counter, item in enumerate(refmembers): name = item["name"] klass = item["type"] mnamespace = "" klassname = klass if "::" in klass: mnamespace, klassname = klass.split("::") substitutions = { "counter" : counter+nOfRefVectors, "class" : klass, "rawclass" : klassname, "name" : name } # includes includes += '#include "%sCollection.h" \n' %(klassname) if mnamespace != "": # constructor calls initializers += ",m_rel_%s(new std::vector<::%s::Const%s>())" %(name, mnamespace, klassname) # members relations += " std::vector<::%s::Const%s>* m_rel_%s; //relation buffer for r/w\n" %(mnamespace, klassname, name) else: # constructor calls initializers += ",m_rel_%s(new std::vector<Const%s>())" %(name, klass) # members relations += " std::vector<Const%s>* m_rel_%s; //relation buffer for r/w\n" %(klass, name) constructorbody += " m_refCollections->push_back(new std::vector<podio::ObjectID>());\n" # relation handling in ::clear clear_relations += " for (auto& item : (*m_rel_%s)) {item.unlink(); }\n" %(name) clear_relations += " m_rel_%s->clear();\n" %(name) # relation handling in ::prepareForWrite prepareforwriting_refmembers += " for (auto& obj : m_entries) {\nif (obj->m_%s != nullptr){\n(*m_refCollections)[%s]->emplace_back(obj->m_%s->getObjectID());} else {(*m_refCollections)[%s]->push_back({-2,-2}); } }\n" %(name,counter+nOfRefVectors,name,counter+nOfRefVectors) # relation handling in ::settingReferences prepareafterread_refmembers += self.evaluate_template("CollectionSetSingleReference.cc.template",substitutions) substitutions = { "name" : rawclassname, "constructorbody" : constructorbody, "prepareforwritinghead" : prepareforwritinghead, "prepareforwritingbody" : prepareforwritingbody, "prepareforwriting_refmembers" : prepareforwriting_refmembers, "setreferences" : setreferences, "prepareafterread" : prepareafterread, "prepareafterread_refmembers" : prepareafterread_refmembers, "includes" : includes, "initializers" : initializers, "relations" : relations, "create_relations" : create_relations, "clear_relations" : clear_relations, "push_back_relations" : push_back_relations, "package_name" : self.package_name, "vectorized_access_declaration" : vectorized_access_decl, "vectorized_access_implementation" : vectorized_access_impl, "namespace_open" : namespace_open, "namespace_close" : namespace_close } self.fill_templates("Collection",substitutions) self.created_classes.append("%sCollection"%classname) def create_component(self, classname, components): """ Create a component class to be used within the data types Components can only contain simple data types and no user defined ones """ namespace_open = "" namespace_close = "" if "::" in classname: namespace, rawclassname = classname.split("::") namespace_open = "namespace %s {" % namespace namespace_close = "} // namespace %s" % namespace else: rawclassname = classname for klass in components.itervalues(): if klass in self.buildin_types or self.components.has_key(klass): pass else: raise Exception("'%s' defines a member of a type '%s' which is not allowed in a component!" %(classname, klass)) includes = "" members = "" for name, klass in components.iteritems(): klassname = klass mnamespace = "" if "::" in klass: mnamespace, klassname = klass.split("::") if mnamespace == "": members+= " %s %s;\n" %(klassname, name) else: members += " ::%s::%s %s;\n" %(mnamespace, klassname, name) if self.components.has_key(klass): includes+= '#include "%s.h"\n' %(klassname) substitutions = { "includes" : includes, "members" : members, "name" : rawclassname, "package_name" : self.package_name, "namespace_open" : namespace_open, "namespace_close" : namespace_close } self.fill_templates("Component",substitutions) self.created_classes.append(classname) def create_obj(self, classname, definition): """ Create an obj class containing all information relevant for a given object. """ namespace_open = "" namespace_close = "" if "::" in classname: namespace, rawclassname = classname.split("::") namespace_open = "namespace %s {" % namespace namespace_close = "} // namespace %s" % namespace else: rawclassname = classname relations = "" includes = "" includes_cc = "" forward_declarations = "" forward_declarations_namespace = {"":[]} initialize_relations = "" deepcopy_relations = "" delete_relations = "" refvectors = definition["OneToManyRelations"] singleRelations = definition["OneToOneRelations"] # do includes and forward declarations for # oneToOneRelations and do proper cleanups for item in singleRelations: name = item["name"] klass = item["type"] klassname = klass mnamespace = "" if "::" in klass: mnamespace, klassname = klass.split("::") if mnamespace not in forward_declarations_namespace.keys(): forward_declarations_namespace[mnamespace] = [] if mnamespace != "": relations+= " ::%s::Const%s* m_%s;\n" %(mnamespace, klassname, name) else: relations+= " Const%s* m_%s;\n" %(klassname, name) if klass not in self.buildin_types: if klass != classname: forward_declarations_namespace[mnamespace] += ['class Const%s;\n' %(klassname)] includes_cc += '#include "%sConst.h"\n' %(klassname) initialize_relations += ",m_%s(nullptr)\n" %(name) delete_relations+="delete m_%s;\n" %name for nsp in forward_declarations_namespace.iterkeys(): if nsp != "": forward_declarations += "namespace %s {" % nsp forward_declarations += "".join(forward_declarations_namespace[nsp]) if nsp != "": forward_declarations += "}\n" if len(refvectors+definition["VectorMembers"]) !=0: includes += "#include <vector>\n" for item in refvectors+definition["VectorMembers"]: name = item["name"] klass = item["type"] if klass not in self.buildin_types: if klass not in self.components: if "::" in klass: mnamespace, klassname = klass.split("::") klassWithQualifier = "::"+mnamespace+"::Const"+klassname else: klassWithQualifier = "Const"+klass else: klassWithQualifier = klass relations += " std::vector<%s>* m_%s;\n" %(klassWithQualifier, name) initialize_relations += ",m_%s(new std::vector<%s>())" %(name,klassWithQualifier) deepcopy_relations += ",m_%s(new std::vector<%s>(*(other.m_%s)))" %(name,klassWithQualifier,name) if klass == classname: includes_cc += '#include "%s.h"\n' %(rawclassname) else: if "::" in klass: mnamespace, klassname = klass.split("::") includes += '#include "%s.h"\n' %klassname else: includes += '#include "%s.h"\n' %klass else: relations += " std::vector<%s>* m_%s;\n" %(klass, name) initialize_relations += ",m_%s(new std::vector<%s>())" %(name,klass) deepcopy_relations += ",m_%s(new std::vector<%s>(*(other.m_%s)))" %(name,klass,name) delete_relations += "delete m_%s;\n" %(name) substitutions = { "name" : rawclassname, "includes" : includes, "includes_cc" : includes_cc, "forward_declarations" : forward_declarations, "relations" : relations, "initialize_relations" : initialize_relations, "deepcopy_relations" : deepcopy_relations, "delete_relations" : delete_relations, "namespace_open" : namespace_open, "namespace_close" : namespace_close } self.fill_templates("Obj",substitutions) self.created_classes.append(classname+"Obj") def prepare_vectorized_access(self, classname,members ): implementation = "" declaration = "" for member in members: name = member["name"] klass = member["type"] substitutions = { "classname" : classname, "member" : name, "type" : klass } if klass not in self.buildin_types: substitutions["type"] = "class %s" % klass implementation += self.evaluate_template("CollectionReturnArray.cc.template", substitutions) declaration += " template<size_t arraysize> \n const std::array<%s,arraysize> %s() const;\n" %(klass, name) return declaration, implementation def write_file(self, name,content): #dispatch headers to header dir, the rest to /src # fullname = os.path.join(self.install_dir,self.package_name,name) if name.endswith("h"): fullname = os.path.join(self.install_dir,self.package_name,name) else: fullname = os.path.join(self.install_dir,"src",name) open(fullname, "w").write(content) def evaluate_template(self, filename, substitutions): """ reads in a given template, evaluates it and returns result """ templatefile = os.path.join(self.template_dir,filename) template = open(templatefile,"r").read() return string.Template(template).substitute(substitutions) def fill_templates(self, category, substitutions): # "Data" denotes the real class; # only headers and the FN should not contain Data if category == "Data": FN = "Data" endings = ("h") elif category == "Obj": FN = "Obj" endings = ("h","cc") elif category == "Component": FN = "" endings = ("h") elif category == "Object": FN = "" endings = ("h","cc") elif category == "ConstObject": FN = "Const" endings = ("h","cc") else: FN = category endings = ("h","cc") for ending in endings: templatefile = "%s.%s.template" %(category,ending) templatefile = os.path.join(self.template_dir,templatefile) template = open(templatefile,"r").read() content = string.Template(template).substitute(substitutions) filename = "%s%s.%s" %(substitutions["name"],FN,ending) self.write_file(filename, content)
class ClassGenerator(object): def __init__(self, yamlfile, install_dir, package_name, verbose=True, getSyntax=False): self.yamlfile = yamlfile self.install_dir = install_dir self.package_name = package_name self.template_dir = os.path.join(thisdir, "../templates") self.verbose = verbose self.getSyntax = getSyntax self.buildin_types = ClassDefinitionValidator.buildin_types self.created_classes = [] self.requested_classes = [] self.reader = PodioConfigReader(yamlfile) self.warnings = [] def process(self): self.reader.read() self.process_components(self.reader.components) self.process_datatypes(self.reader.datatypes) self.create_selection_xml() self.print_report() def process_components(self, content): self.requested_classes += content.keys() for name, components in content.iteritems(): self.create_component(name, components["Members"]) def process_datatypes(self, content): for name in content.iterkeys(): self.requested_classes.append(name) self.requested_classes.append("%sData" % name) for name, components in content.iteritems(): self.create_data(name, components) self.create_class(name, components) self.create_collection(name, components) self.create_obj(name, components) def print_report(self): if self.verbose: pkl = open(os.path.join(thisdir, "figure.txt")) figure = pickle.load(pkl) text = _text_ % (self.yamlfile, len(self.created_classes), self.install_dir ) for i, line in enumerate(figure): print print line + text.splitlines()[i], print " 'Homage to the Square' - Josef Albers" print def get_template(self, filename): templatefile = os.path.join(self.template_dir, filename) return open(templatefile, "r").read() def create_selection_xml(self): content = "" for klass in self.created_classes: # if not klass.endswith("Collection") or klass.endswith("Data"): content += ' <class name="std::vector<%s>" />\n' % klass content += """ <class name="%s"> <field name="m_registry" transient="true"/> <field name="m_container" transient="true"/> </class>\n""" % klass templatefile = os.path.join(self.template_dir, "selection.xml.template") template = open(templatefile, "r").read() content = string.Template(template).substitute({"classes": content}) self.write_file("selection.xml", content) def process_datatype(self, classname, definition, is_data=False): datatype_dict = {} datatype_dict["description"] = definition["Description"] datatype_dict["author"] = definition["Author"] datatype_dict["includes"] = [] datatype_dict["members"] = [] members = definition["Members"] for member in members: klass = member["type"] name = member["name"] description = member["description"] datatype_dict["members"].append(" %s %s; ///<%s" % (klass, name, description)) if "std::string" == klass: datatype_dict["includes"].append("#include <string>") self.warnings.append("%s defines a string member %s, that spoils the PODness" % (classname, klass)) elif klass in self.buildin_types: pass elif klass in self.requested_classes: if "::" in klass: namespace, klassname = klass.split("::") datatype_dict["includes"].append('#include "%s.h"' % klassname) else: datatype_dict["includes"].append('#include "%s.h"' % klass) elif "std::array" in klass: datatype_dict["includes"].append("#include <array>") self.created_classes.append(klass) elif "vector" in klass: datatype_dict["includes"].append("#include <vector>") if is_data: # avoid having warnings twice self.warnings.append("%s defines a vector member %s, that spoils the PODness" % (classname, klass)) elif "[" in klass and is_data: # FIXME: is this only true ofr PODs? raise Exception("'%s' defines an array type. Array types are not supported yet." % (classname, klass)) else: raise Exception("'%s' defines a member of a type '%s' that is not (yet) declared!" % (classname, klass)) return datatype_dict def demangle_classname(self, classname): namespace_open = "" namespace_close = "" namespace = "" rawclassname = "" if "::" in classname: cnameparts = classname.split("::") if len(cnameparts) > 2: raise Exception("'%s' defines a type with nested namespaces. Not supported, yet." % classname) namespace, rawclassname = cnameparts namespace_open = "namespace %s {" % namespace namespace_close = "} // namespace %s" % namespace else: rawclassname = classname return namespace, rawclassname, namespace_open, namespace_close def create_data(self, classname, definition): # check whether all member types are known # and prepare include directives namespace, rawclassname, namespace_open, namespace_close = self.demangle_classname(classname) data = self.process_datatype(classname, definition) # now handle the vector-members vectormembers = definition["VectorMembers"] for vectormember in vectormembers: name = vectormember["name"] data["members"].append(" unsigned int %s_begin;" % name) data["members"].append(" unsigned int %s_end;" %(name)) # now handle the one-to-many relations refvectors = definition["OneToManyRelations"] for refvector in refvectors: name = refvector["name"] data["members"].append(" unsigned int %s_begin;" %(name)) data["members"].append(" unsigned int %s_end;" %(name)) substitutions = {"includes" : "\n".join(data["includes"]), "members" : "\n".join(data["members"]), "name" : rawclassname, "description" : data["description"], "author" : data["author"], "package_name" : self.package_name, "namespace_open" : namespace_open, "namespace_close" : namespace_close } self.fill_templates("Data", substitutions) self.created_classes.append(classname+"Data") def create_class(self, classname, definition): namespace, rawclassname, namespace_open, namespace_close = self.demangle_classname(classname) includes_cc = "" forward_declarations = "" forward_declarations_namespace = {"":[]} getter_implementations = "" setter_implementations = "" getter_declarations = "" setter_declarations = "" constructor_signature = "" constructor_body = "" ConstGetter_implementations = "" # check whether all member types are known # and prepare include directives datatype = self.process_datatype(classname, definition, False) datatype["includes"].append('#include "%s.h"' % (rawclassname+"Data")) # check on-to-one relations and prepare include directives oneToOneRelations = definition["OneToOneRelations"] for member in oneToOneRelations: klass = member["type"] if klass in self.requested_classes: mnamespace = "" klassname = klass if "::" in klass: mnamespace, klassname = klass.split("::") if mnamespace not in forward_declarations_namespace.keys(): forward_declarations_namespace[mnamespace] = [] forward_declarations_namespace[mnamespace] += ["class %s;\n" %(klassname)] forward_declarations_namespace[mnamespace] += ["class Const%s;\n" %(klassname)] includes_cc += '#include "%s.h"\n' %(klassname) for nsp in forward_declarations_namespace.iterkeys(): if nsp != "": forward_declarations += "namespace %s {\n" % nsp forward_declarations += "".join(forward_declarations_namespace[nsp]) if nsp != "": forward_declarations += "}\n" # check one-to-many relations for consistency # and prepare include directives refvectors = definition["OneToManyRelations"] if len(refvectors) != 0: datatype["includes"].append("#include <vector>") for item in refvectors: klass = item["type"] if klass in self.requested_classes: if "::" in klass: mnamespace, klassname = klass.split("::") datatype["includes"].append('#include "%s.h"' %klassname) else: datatype["includes"].append('#include "%s.h"' %klass) elif "std::array" in klass: datatype["includes"].append("#include <array>") else: raise Exception("'%s' declares a non-allowed many-relation to '%s'!" %(classname, klass)) # handle standard members for member in definition["Members"]: name = member["name"] klass = member["type"] gname,sname = name,name if( self.getSyntax ): gname = "get" + name[:1].upper() + name[1:] sname = "set" + name[:1].upper() + name[1:] getter_declarations += declarations["member_getter"].format(type=klass, name=name,fname=gname) getter_implementations += implementations["member_getter"].format(type=klass, classname=rawclassname, name=name, fname=gname) if klass in self.buildin_types: setter_declarations += declarations["member_builtin_setter"].format(type=klass, name=name, fname=sname) setter_implementations += implementations["member_builtin_setter"].format(type=klass, classname=rawclassname, name=name, fname=sname) else: setter_declarations += declarations["member_class_refsetter"].format(type=klass, name=name) setter_implementations += implementations["member_class_refsetter"].format(type=klass, classname=rawclassname, name=name, fname=sname) setter_declarations += declarations["member_class_setter"].format(type=klass, name=name, fname=sname) setter_implementations += implementations["member_class_setter"].format(type=klass, classname=rawclassname, name=name, fname=sname) # Getter for the Const variety of this datatype ConstGetter_implementations += implementations["const_member_getter"].format(type=klass, classname=rawclassname, name=name, fname=gname) # set up signature constructor_signature += "%s %s," %(klass, name) # constructor constructor_body += " m_obj->data.%s = %s;" %(name, name) # one-to-one relations for member in oneToOneRelations: name = member["name"] klass = member["type"] mnamespace = "" klassname = klass mnamespace, klassname, _, __ = self.demangle_classname(klass) setter_declarations += declarations["one_rel_setter"].format(name=name, namespace=mnamespace, type=klassname) setter_implementations += implementations["one_rel_setter"].format(name=name, namespace=mnamespace, type=klassname, classname=rawclassname) getter_declarations += declarations["one_rel_getter"].format(name=name, namespace=mnamespace, type=klassname) getter_implementations += implementations["one_rel_getter"].format(name=name, namespace=mnamespace, type=klassname, classname=rawclassname) ConstGetter_implementations += implementations["const_one_rel_getter"].format(name=name, namespace=mnamespace, type=klassname, classname=rawclassname) # handle vector members vectormembers = definition["VectorMembers"] if len(vectormembers) != 0: datatype["includes"].append("#include <vector>") for item in vectormembers: klass = item["type"] if klass not in self.buildin_types and klass not in self.reader.components: raise Exception("'%s' declares a non-allowed vector member of type '%s'!" %(classname, klass)) if klass in self.reader.components: if "::" in klass: namespace, klassname = klass.split("::") datatype["includes"].append('#include "%s.h"' %klassname) else: datatype["includes"].append('#include "%s.h"' %klass) # handle constructor from values constructor_signature = constructor_signature.rstrip(",") if constructor_signature == "": constructor_implementation = "" constructor_declaration = "" ConstConstructor_declaration = "" ConstConstructor_implementation = "" else: substitutions = { "name" : rawclassname, "constructor" : constructor_body, "signature" : constructor_signature } constructor_implementation = self.evaluate_template("Object.constructor.cc.template",substitutions) constructor_declaration = " %s(%s);\n" %(rawclassname, constructor_signature) ConstConstructor_implementation = self.evaluate_template("ConstObject.constructor.cc.template",substitutions) ConstConstructor_declaration = "Const%s(%s);\n" %(rawclassname, constructor_signature) # handle one-to-many relations references_members = "" references_declarations = "" references = "" ConstReferences_declarations = "" ConstReferences = "" references_template = self.get_template("RefVector.cc.template") references_declarations_template = self.get_template("RefVector.h.template") ConstReferences_declarations_template = self.get_template("ConstRefVector.h.template") ConstReferences_template = self.get_template("ConstRefVector.cc.template") for refvector in refvectors+definition["VectorMembers"]: relationtype = refvector["type"] if relationtype not in self.buildin_types and relationtype not in self.reader.components: relationtype = "Const"+relationtype relationName = refvector["name"] get_relation = relationName add_relation = "add"+relationName if( self.getSyntax ): get_relation = "get" + relationName[:1].upper() + relationName[1:] add_relation = "add" + relationName[:1].upper() + relationName[1:len(relationName)-1] # drop the 's' at the end !?? substitutions = {"relation" : relationName, "get_relation" : get_relation, "add_relation" : add_relation, "relationtype" : relationtype, "classname" : rawclassname, "package_name" : self.package_name } references_declarations += string.Template(references_declarations_template).substitute(substitutions) references += string.Template(references_template).substitute(substitutions) references_members += "std::vector<%s>* m_%s; ///< transient \n" %(refvector["type"], refvector["name"]) ConstReferences_declarations += string.Template(ConstReferences_declarations_template).substitute(substitutions) ConstReferences += string.Template(ConstReferences_template).substitute(substitutions) # handle user provided extra code extracode_declarations = "" extracode = "" constextracode_declarations = "" constextracode = "" if definition.has_key("ExtraCode"): extra = definition["ExtraCode"] if( extra.has_key("declaration")): extracode_declarations = extra["declaration"].replace("{name}",rawclassname) if( extra.has_key("implementation")): extracode = extra["implementation"].replace("{name}",rawclassname) if( extra.has_key("const_declaration")): constextracode_declarations = extra["const_declaration"].replace("{name}","Const"+rawclassname) extracode_declarations += "\n" extracode_declarations += extra["const_declaration"] if( extra.has_key("const_implementation")): constextracode = extra["const_implementation"].replace("{name}","Const"+rawclassname) extracode += "\n" extracode += extra["const_implementation"].replace("{name}",rawclassname) # TODO: add loading of code from external files if( extra.has_key("includes")): datatype["includes"].append( extra["includes"] ) print " ***** adding includes : " , extra["includes"] , "to" , datatype["includes"] substitutions = {"includes" : "\n".join(datatype["includes"]), "includes_cc" : includes_cc, "forward_declarations" : forward_declarations, "getters" : getter_implementations, "getter_declarations": getter_declarations, "setters" : setter_implementations, "setter_declarations": setter_declarations, "constructor_declaration" : constructor_declaration, "constructor_implementation" : constructor_implementation, "extracode" : extracode, "extracode_declarations" : extracode_declarations, "name" : rawclassname, "description" : datatype["description"], "author" : datatype["author"], "relations" : references, "relation_declarations" : references_declarations, "relation_members" : references_members, "package_name" : self.package_name, "namespace_open" : namespace_open, "namespace_close" : namespace_close } self.fill_templates("Object",substitutions) self.created_classes.append(classname) substitutions["constructor_declaration"] = ConstConstructor_declaration substitutions["constructor_implementation"] = ConstConstructor_implementation substitutions["relation_declarations"] = ConstReferences_declarations substitutions["relations"] = ConstReferences substitutions["getters"] = ConstGetter_implementations substitutions["constextracode"] = constextracode substitutions["constextracode_declarations"] = constextracode_declarations self.fill_templates("ConstObject", substitutions) if "::" in classname: self.created_classes.append("%s::Const%s" %(namespace, rawclassname)) else: self.created_classes.append("Const%s" %classname) def create_collection(self, classname, definition): namespace, rawclassname, namespace_open, namespace_close = self.demangle_classname(classname) members = definition["Members"] constructorbody = "" destructorbody = "" prepareforwritinghead = "" prepareforwritingbody = "" vectorized_access_decl, vectorized_access_impl = self.prepare_vectorized_access(rawclassname,members) setreferences = "" prepareafterread = "" includes = "" initializers = "" relations = "" create_relations = "" clear_relations = "" push_back_relations = "" prepareafterread_refmembers = "" prepareforwriting_refmembers = "" refmembers = definition["OneToOneRelations"] refvectors = definition["OneToManyRelations"] nOfRefVectors = len(refvectors) nOfRefMembers = len(refmembers) if nOfRefVectors + nOfRefMembers > 0: # member initialization #constructorbody += "\tm_refCollections = new podio::CollRefCollection();\n" destructorbody += "\tfor (auto& pointer : m_refCollections) { if (pointer != nullptr) delete pointer; }\n" clear_relations += "\tfor (auto& pointer : m_refCollections) { pointer->clear(); }\n" for counter, item in enumerate(refvectors): name = item["name"] klass = item["type"] substitutions = { "counter" : counter, "class" : klass, "name" : name } mnamespace, klassname, _, __ = self.demangle_classname(klass) # includes includes += '#include "%sCollection.h" \n' %(klassname) # FIXME check if it compiles with :: for both... then delete this. relations += declarations["relation"].format(namespace=mnamespace, type=klassname, name=name) relations += declarations["relation_collection"].format(namespace=mnamespace, type=klassname, name=name) initializers += implementations["ctor_list_relation"].format(namespace=mnamespace, type=klassname, name=name) constructorbody += "\tm_refCollections.push_back(new std::vector<podio::ObjectID>());\n" # relation handling in ::create create_relations += "\tm_rel_{name}_tmp.push_back(obj->m_{name});\n".format(name=name) # relation handling in ::clear clear_relations += implementations["clear_relations_vec"].format(name=name) clear_relations += implementations["clear_relations"].format(name=name) # relation handling in dtor: destructorbody += implementations["destroy_relations"].format(name=name) # relation handling in push_back push_back_relations += "\tm_rel_{name}_tmp.push_back(obj->m_{name});\n".format(name=name) # relation handling in ::prepareForWrite prepareforwritinghead += "\tint {name}_index =0;\n".format(name=name) prepareforwritingbody += self.evaluate_template("CollectionPrepareForWriting.cc.template",substitutions) # relation handling in ::settingReferences setreferences += self.evaluate_template("CollectionSetReferences.cc.template",substitutions) prepareafterread += "\t\tobj->m_%s = m_rel_%s;" %(name, name) for counter, item in enumerate(refmembers): name = item["name"] klass = item["type"] mnamespace = "" klassname = klass if "::" in klass: mnamespace, klassname = klass.split("::") substitutions = { "counter" : counter+nOfRefVectors, "class" : klass, "rawclass" : klassname, "name" : name } # includes includes += '#include "%sCollection.h"\n' %(klassname) # constructor call initializers += implementations["ctor_list_relation"].format(namespace=mnamespace, type=klassname, name=name) # member relations += declarations["relation"].format(namespace=mnamespace, type=klassname, name=name) constructorbody += "\tm_refCollections.push_back(new std::vector<podio::ObjectID>());\n" # relation handling in ::clear clear_relations += implementations["clear_relations"].format(name=name) # relation handling in dtor: destructorbody += implementations["destroy_relations"].format(name=name) # relation handling in ::prepareForWrite prepareforwriting_refmembers += implementations["prep_writing_relations"].format(name=name, i=(counter+nOfRefVectors)) # relation handling in ::settingReferences prepareafterread_refmembers += self.evaluate_template("CollectionSetSingleReference.cc.template",substitutions) substitutions = { "name" : rawclassname, "constructorbody" : constructorbody, "destructorbody" : destructorbody, "prepareforwritinghead" : prepareforwritinghead, "prepareforwritingbody" : prepareforwritingbody, "prepareforwriting_refmembers" : prepareforwriting_refmembers, "setreferences" : setreferences, "prepareafterread" : prepareafterread, "prepareafterread_refmembers" : prepareafterread_refmembers, "includes" : includes, "initializers" : initializers, "relations" : relations, "create_relations" : create_relations, "clear_relations" : clear_relations, "push_back_relations" : push_back_relations, "package_name" : self.package_name, "vectorized_access_declaration" : vectorized_access_decl, "vectorized_access_implementation" : vectorized_access_impl, "namespace_open" : namespace_open, "namespace_close" : namespace_close } self.fill_templates("Collection",substitutions) self.created_classes.append("%sCollection"%classname) def create_component(self, classname, components): """ Create a component class to be used within the data types Components can only contain simple data types and no user defined ones """ namespace, rawclassname, namespace_open, namespace_close = self.demangle_classname(classname) includes = "" members = "" extracode_declarations = "" #fg: sort the dictionary, so at least we get a predictable order (alphabetical) of the members keys = sorted( components.keys() ) for name in keys: klass = components[ name ] # for name, klass in components.iteritems(): if( name != "ExtraCode"): klassname = klass mnamespace = "" if "::" in klass: mnamespace, klassname = klass.split("::") if mnamespace == "": members+= " %s %s;\n" %(klassname, name) else: members += " ::%s::%s %s;\n" %(mnamespace, klassname, name) if self.reader.components.has_key(klass): includes+= '#include "%s.h"\n' %(klassname) else: # handle user provided extra code if klass.has_key("declaration"): extracode_declarations = klass["declaration"] substitutions = { "includes" : includes, "members" : members, "extracode_declarations" : extracode_declarations, "name" : rawclassname, "package_name" : self.package_name, "namespace_open" : namespace_open, "namespace_close" : namespace_close } self.fill_templates("Component",substitutions) self.created_classes.append(classname) def create_obj(self, classname, definition): """ Create an obj class containing all information relevant for a given object. """ namespace, rawclassname, namespace_open, namespace_close = self.demangle_classname(classname) relations = "" includes = "" includes_cc = "" forward_declarations = "" forward_declarations_namespace = {"":[]} initialize_relations = "" deepcopy_relations = "" delete_relations = "" delete_singlerelations = "" refvectors = definition["OneToManyRelations"] singleRelations = definition["OneToOneRelations"] # do includes and forward declarations for # oneToOneRelations and do proper cleanups for item in singleRelations: name = item["name"] klass = item["type"] klassname = klass mnamespace = "" if "::" in klass: mnamespace, klassname = klass.split("::") if mnamespace not in forward_declarations_namespace.keys(): forward_declarations_namespace[mnamespace] = [] if mnamespace != "": relations+= " ::%s::Const%s* m_%s;\n" %(mnamespace, klassname, name) else: relations+= " Const%s* m_%s;\n" %(klassname, name) if klass not in self.buildin_types: if klass != classname: forward_declarations_namespace[mnamespace] += ['class Const%s;\n' %(klassname)] includes_cc += '#include "%sConst.h"\n' %(klassname) initialize_relations += ",m_%s(nullptr)\n" %(name) delete_singlerelations+="\t\tif (m_%s != nullptr) delete m_%s;\n" % (name, name) for nsp in forward_declarations_namespace.iterkeys(): if nsp != "": forward_declarations += "namespace %s {" % nsp forward_declarations += "".join(forward_declarations_namespace[nsp]) if nsp != "": forward_declarations += "}\n" if len(refvectors+definition["VectorMembers"]) !=0: includes += "#include <vector>\n" for item in refvectors+definition["VectorMembers"]: name = item["name"] klass = item["type"] if klass not in self.buildin_types: if klass not in self.reader.components: if "::" in klass: mnamespace, klassname = klass.split("::") klassWithQualifier = "::"+mnamespace+"::Const"+klassname else: klassWithQualifier = "Const"+klass else: klassWithQualifier = klass relations += "\tstd::vector<%s>* m_%s;\n" %(klassWithQualifier, name) initialize_relations += ", m_%s(new std::vector<%s>())" %(name,klassWithQualifier) deepcopy_relations += ", m_%s(new std::vector<%s>(*(other.m_%s)))" %(name,klassWithQualifier,name) if klass == classname: includes_cc += '#include "%s.h"\n' %(rawclassname) else: if "::" in klass: mnamespace, klassname = klass.split("::") includes += '#include "%s.h"\n' %klassname else: includes += '#include "%s.h"\n' %klass else: relations += "\tstd::vector<%s>* m_%s;\n" %(klass, name) initialize_relations += ", m_%s(new std::vector<%s>())" %(name,klass) deepcopy_relations += ", m_%s(new std::vector<%s>(*(other.m_%s)))" %(name,klass,name) delete_relations += "\t\tdelete m_%s;\n" %(name) substitutions = { "name" : rawclassname, "includes" : includes, "includes_cc" : includes_cc, "forward_declarations" : forward_declarations, "relations" : relations, "initialize_relations" : initialize_relations, "deepcopy_relations" : deepcopy_relations, "delete_relations" : delete_relations, "delete_singlerelations" : delete_singlerelations, "namespace_open" : namespace_open, "namespace_close" : namespace_close } self.fill_templates("Obj",substitutions) self.created_classes.append(classname+"Obj") def prepare_vectorized_access(self, classname,members ): implementation = "" declaration = "" for member in members: name = member["name"] klass = member["type"] substitutions = { "classname" : classname, "member" : name, "type" : klass } if klass not in self.buildin_types: substitutions["type"] = "class %s" % klass implementation += self.evaluate_template("CollectionReturnArray.cc.template", substitutions) declaration += "\ttemplate<size_t arraysize>\n\tconst std::array<%s,arraysize> %s() const;\n" %(klass, name) return declaration, implementation def write_file(self, name,content): #dispatch headers to header dir, the rest to /src # fullname = os.path.join(self.install_dir,self.package_name,name) if name.endswith("h"): fullname = os.path.join(self.install_dir,self.package_name,name) else: fullname = os.path.join(self.install_dir,"src",name) open(fullname, "w").write(content) def evaluate_template(self, filename, substitutions): """ reads in a given template, evaluates it and returns result """ templatefile = os.path.join(self.template_dir,filename) template = open(templatefile,"r").read() return string.Template(template).substitute(substitutions) def fill_templates(self, category, substitutions): # "Data" denotes the real class; # only headers and the FN should not contain Data if category == "Data": FN = "Data" endings = ("h") elif category == "Obj": FN = "Obj" endings = ("h","cc") elif category == "Component": FN = "" endings = ("h") elif category == "Object": FN = "" endings = ("h","cc") elif category == "ConstObject": FN = "Const" endings = ("h","cc") else: FN = category endings = ("h","cc") for ending in endings: templatefile = "%s.%s.template" %(category,ending) templatefile = os.path.join(self.template_dir,templatefile) template = open(templatefile,"r").read() content = string.Template(template).substitute(substitutions).expandtabs(2) filename = "%s%s.%s" %(substitutions["name"],FN,ending) self.write_file(filename, content)
class ClassGenerator(object): def __init__(self, yamlfile, install_dir, package_name, verbose=True, dryrun=False): self.yamlfile = yamlfile self.install_dir = install_dir self.package_name = package_name self.template_dir = os.path.join(thisdir, "../templates") self.verbose = verbose self.buildin_types = ClassDefinitionValidator.buildin_types self.created_classes = [] self.requested_classes = [] self.reader = PodioConfigReader(yamlfile) self.warnings = [] self.component_members = {} self.dryrun = dryrun def process(self): self.reader.read() self.getSyntax = self.reader.options["getSyntax"] self.expose_pod_members = self.reader.options["exposePODMembers"] self.process_components(self.reader.components) self.process_datatypes(self.reader.datatypes) self.create_selection_xml() self.print_report() def process_components(self, content): self.requested_classes += content.keys() for name, components in content.iteritems(): self.create_component(name, components["Members"]) def process_datatypes(self, content): for name in content.iterkeys(): self.requested_classes.append(name) self.requested_classes.append("%sData" % name) for name, components in content.iteritems(): self.create_data(name, components) self.create_class(name, components) self.create_collection(name, components) self.create_obj(name, components) # self.create_PrintInfo(name, components) def print_report(self): if self.verbose: pkl = open(os.path.join(thisdir, "figure.txt")) figure = pickle.load(pkl) text = _text_ % (self.yamlfile, len(self.created_classes), self.install_dir ) cntr = 0 print for figline, summaryline in zip(figure, text.splitlines()): cntr += 1 print figline + summaryline for i in xrange(cntr, len(figure)): print figure[i] print " 'Homage to the Square' - Josef Albers" print def get_template(self, filename): templatefile = os.path.join(self.template_dir, filename) return open(templatefile, "r").read() def create_selection_xml(self): content = "" for klass in self.created_classes: # if not klass.endswith("Collection") or klass.endswith("Data"): content += ' <class name="std::vector<%s>" />\n' % klass content += """ <class name="%s"> <field name="m_registry" transient="true"/> <field name="m_container" transient="true"/> </class>\n""" % klass templatefile = os.path.join(self.template_dir, "selection.xml.template") template = open(templatefile, "r").read() content = string.Template(template).substitute({"classes": content}) self.write_file("selection.xml", content) def process_datatype(self, classname, definition, is_data=False): datatype_dict = {} datatype_dict["description"] = definition["Description"] datatype_dict["author"] = definition["Author"] datatype_dict["includes"] = [] datatype_dict["members"] = [] members = definition["Members"] for member in members: klass = member["type"] name = member["name"] description = member["description"] datatype_dict["members"].append(" %s %s; ///<%s" % (klass, name, description)) if "std::string" == klass: datatype_dict["includes"].append("#include <string>") self.warnings.append("%s defines a string member %s, that spoils the PODness" % (classname, klass)) elif klass in self.buildin_types: pass elif klass in self.requested_classes: if "::" in klass: namespace, klassname = klass.split("::") datatype_dict["includes"].append('#include "%s.h"' % klassname) else: datatype_dict["includes"].append('#include "%s.h"' % klass) elif "std::array" in klass: datatype_dict["includes"].append("#include <array>") self.created_classes.append(klass) elif "vector" in klass: datatype_dict["includes"].append("#include <vector>") if is_data: # avoid having warnings twice self.warnings.append("%s defines a vector member %s, that spoils the PODness" % (classname, klass)) elif "[" in klass and is_data: # FIXME: is this only true ofr PODs? raise Exception("'%s' defines an array type. Array types are not supported yet." % (classname, klass)) else: raise Exception("'%s' defines a member of a type '%s' that is not (yet) declared!" % (classname, klass)) return datatype_dict def demangle_classname(self, classname): namespace_open = "" namespace_close = "" namespace = "" rawclassname = "" if "::" in classname: cnameparts = classname.split("::") if len(cnameparts) > 2: raise Exception("'%s' defines a type with nested namespaces. Not supported, yet." % classname) namespace, rawclassname = cnameparts namespace_open = "namespace %s {" % namespace namespace_close = "} // namespace %s" % namespace else: rawclassname = classname return namespace, rawclassname, namespace_open, namespace_close def create_data(self, classname, definition): # check whether all member types are known # and prepare include directives namespace, rawclassname, namespace_open, namespace_close = self.demangle_classname(classname) data = self.process_datatype(classname, definition) # now handle the vector-members vectormembers = definition["VectorMembers"] for vectormember in vectormembers: name = vectormember["name"] data["members"].append(" unsigned int %s_begin;" % name) data["members"].append(" unsigned int %s_end;" %(name)) # now handle the one-to-many relations refvectors = definition["OneToManyRelations"] for refvector in refvectors: name = refvector["name"] data["members"].append(" unsigned int %s_begin;" %(name)) data["members"].append(" unsigned int %s_end;" %(name)) substitutions = {"includes" : "\n".join(data["includes"]), "members" : "\n".join(data["members"]), "name" : rawclassname, "description" : data["description"], "author" : data["author"], "package_name" : self.package_name, "namespace_open" : namespace_open, "namespace_close" : namespace_close } self.fill_templates("Data", substitutions) self.created_classes.append(classname+"Data") def create_class(self, classname, definition): namespace, rawclassname, namespace_open, namespace_close = self.demangle_classname(classname) includes_cc = "" forward_declarations = "" forward_declarations_namespace = {"":[]} getter_implementations = "" setter_implementations = "" getter_declarations = "" setter_declarations = "" constructor_signature = "" constructor_body = "" ConstGetter_implementations = "" # check whether all member types are known # and prepare include directives datatype = self.process_datatype(classname, definition, False) datatype["includes"].append('#include "%s.h"' % (rawclassname+"Data")) # check on-to-one relations and prepare include directives oneToOneRelations = definition["OneToOneRelations"] for member in oneToOneRelations: klass = member["type"] if klass in self.requested_classes: mnamespace = "" klassname = klass if "::" in klass: mnamespace, klassname = klass.split("::") if mnamespace not in forward_declarations_namespace.keys(): forward_declarations_namespace[mnamespace] = [] forward_declarations_namespace[mnamespace] += ["class %s;\n" %(klassname)] forward_declarations_namespace[mnamespace] += ["class Const%s;\n" %(klassname)] includes_cc += '#include "%s.h"\n' %(klassname) for nsp in forward_declarations_namespace.iterkeys(): if nsp != "": forward_declarations += "namespace %s {\n" % nsp forward_declarations += "".join(forward_declarations_namespace[nsp]) if nsp != "": forward_declarations += "}\n" # check one-to-many relations for consistency # and prepare include directives refvectors = definition["OneToManyRelations"] if len(refvectors) != 0: datatype["includes"].append("#include <vector>") for item in refvectors: klass = item["type"] if klass in self.requested_classes: if "::" in klass: mnamespace, klassname = klass.split("::") datatype["includes"].append('#include "%s.h"' %klassname) else: datatype["includes"].append('#include "%s.h"' %klass) elif "std::array" in klass: datatype["includes"].append("#include <array>") else: raise Exception("'%s' declares a non-allowed many-relation to '%s'!" %(classname, klass)) # handle standard members all_members = {} for member in definition["Members"]: name = member["name"] klass = member["type"] desc = member["description"] gname,sname = name,name if( self.getSyntax ): gname = "get" + name[:1].upper() + name[1:] sname = "set" + name[:1].upper() + name[1:] if name in all_members.keys(): raise Exception("'%s' clashes with another member name in class '%s', previously defined in %s" % (name, classname, all_members[name])) all_members[name] = classname getter_declarations += declarations["member_getter"].format(type=klass, name=name,fname=gname, description=desc) getter_implementations += implementations["member_getter"].format(type=klass, classname=rawclassname, name=name, fname=gname) if klass in self.buildin_types: setter_declarations += declarations["member_builtin_setter"].format(type=klass, name=name, fname=sname, description=desc) setter_implementations += implementations["member_builtin_setter"].format(type=klass, classname=rawclassname, name=name, fname=sname) else: setter_declarations += declarations["member_class_refsetter"].format(type=klass, name=name, description=desc) setter_implementations += implementations["member_class_refsetter"].format(type=klass, classname=rawclassname, name=name, fname=sname) setter_declarations += declarations["member_class_setter"].format(type=klass, name=name, fname=sname, description=desc) setter_implementations += implementations["member_class_setter"].format(type=klass, classname=rawclassname, name=name, fname=sname) if self.expose_pod_members: sub_members = self.component_members[klass] for sub_member in sub_members: comp_member_class, comp_member_name = sub_member if comp_member_name in all_members.keys(): raise Exception("'%s' clashes with another member name in class '%s' (defined in the component '%s' and '%s')" % (comp_member_name, classname, name, all_members[comp_member_name])) all_members[comp_member_name] = " member '" + name + "'" # use mystructMember with camel case as name to avoid clashes comp_gname, comp_sname = comp_member_name, comp_member_name if self.getSyntax: comp_gname = "get" + comp_member_name[:1].upper() + comp_member_name[1:] comp_sname = "set" + comp_member_name[:1].upper() + comp_member_name[1:] getter_declarations += declarations["pod_member_getter"].format(type=comp_member_class, name=comp_member_name, fname=comp_gname, compname=name, description=desc) getter_implementations += implementations["pod_member_getter"].format(type=comp_member_class, classname=rawclassname, name=comp_member_name, fname=comp_gname, compname=name, description=desc) if comp_member_class in self.buildin_types: setter_declarations += declarations["pod_member_builtin_setter"].format(type=comp_member_class, name=comp_member_name, fname=comp_sname, compname=name, description=desc) setter_implementations += implementations["pod_member_builtin_setter"].format(type=comp_member_class, classname=rawclassname, name=comp_member_name, fname=comp_sname, compname=name, description=desc) else: setter_declarations += declarations["pod_member_class_refsetter"].format(type=comp_member_class, name=comp_member_name, compname=name, description=desc) setter_implementations += implementations["pod_member_class_refsetter"].format(type=comp_member_class, classname=rawclassname, name=comp_member_name, fname=comp_sname, compname=name, description=desc) setter_declarations += declarations["pod_member_class_setter"].format(type=comp_member_class, name=comp_member_name, fname=comp_sname, compname=name, description=desc) setter_implementations += implementations["pod_member_class_setter"].format(type=comp_member_class, classname=rawclassname, fname=comp_sname, name=comp_member_name, compname=name, description=desc) ConstGetter_implementations += implementations["const_pod_member_getter"].format(type=comp_member_class, classname=rawclassname, name=comp_member_name, fname=comp_gname, compname=name, description=desc) # Getter for the Const variety of this datatype ConstGetter_implementations += implementations["const_member_getter"].format(type=klass, classname=rawclassname, name=name, fname=gname, description=desc) # set up signature constructor_signature += "%s %s," %(klass, name) # constructor constructor_body += " m_obj->data.%s = %s;" %(name, name) # one-to-one relations for member in oneToOneRelations: name = member["name"] klass = member["type"] desc = member["description"] mnamespace = "" klassname = klass mnamespace, klassname, _, __ = self.demangle_classname(klass) setter_declarations += declarations["one_rel_setter"].format(name=name, namespace=mnamespace, type=klassname, description=desc) setter_implementations += implementations["one_rel_setter"].format(name=name, namespace=mnamespace, type=klassname, classname=rawclassname) getter_declarations += declarations["one_rel_getter"].format(name=name, namespace=mnamespace, type=klassname, description=desc) getter_implementations += implementations["one_rel_getter"].format(name=name, namespace=mnamespace, type=klassname, classname=rawclassname) ConstGetter_implementations += implementations["const_one_rel_getter"].format(name=name, namespace=mnamespace, type=klassname, classname=rawclassname, description=desc) # handle vector members vectormembers = definition["VectorMembers"] if len(vectormembers) != 0: datatype["includes"].append("#include <vector>") for item in vectormembers: klass = item["type"] if klass not in self.buildin_types and klass not in self.reader.components: raise Exception("'%s' declares a non-allowed vector member of type '%s'!" %(classname, klass)) if klass in self.reader.components: if "::" in klass: namespace, klassname = klass.split("::") datatype["includes"].append('#include "%s.h"' %klassname) else: datatype["includes"].append('#include "%s.h"' %klass) # handle constructor from values constructor_signature = constructor_signature.rstrip(",") if constructor_signature == "": constructor_implementation = "" constructor_declaration = "" ConstConstructor_declaration = "" ConstConstructor_implementation = "" else: substitutions = { "name" : rawclassname, "constructor" : constructor_body, "signature" : constructor_signature } constructor_implementation = self.evaluate_template("Object.constructor.cc.template",substitutions) constructor_declaration = " %s(%s);\n" %(rawclassname, constructor_signature) ConstConstructor_implementation = self.evaluate_template("ConstObject.constructor.cc.template",substitutions) ConstConstructor_declaration = "Const%s(%s);\n" %(rawclassname, constructor_signature) # handle one-to-many relations references_members = "" references_declarations = "" references = "" ConstReferences_declarations = "" ConstReferences = "" references_template = self.get_template("RefVector.cc.template") references_declarations_template = self.get_template("RefVector.h.template") ConstReferences_declarations_template = self.get_template("ConstRefVector.h.template") ConstReferences_template = self.get_template("ConstRefVector.cc.template") for refvector in refvectors+definition["VectorMembers"]: relnamespace, reltype, _, __ = self.demangle_classname(refvector["type"]) relationtype = refvector["type"] if relationtype not in self.buildin_types and relationtype not in self.reader.components: relationtype = relnamespace+"::Const"+reltype relationName = refvector["name"] get_relation = relationName add_relation = "add"+relationName if( self.getSyntax ): get_relation = "get" + relationName[:1].upper() + relationName[1:] add_relation = "add" + relationName[:1].upper() + relationName[1:len(relationName)-1] # drop the 's' at the end !?? substitutions = {"relation" : relationName, "get_relation" : get_relation, "add_relation" : add_relation, "relationtype" : relationtype, "classname" : rawclassname, "package_name" : self.package_name } references_declarations += string.Template(references_declarations_template).substitute(substitutions) references += string.Template(references_template).substitute(substitutions) references_members += "std::vector<%s>* m_%s; ///< transient \n" %(refvector["type"], refvector["name"]) ConstReferences_declarations += string.Template(ConstReferences_declarations_template).substitute(substitutions) ConstReferences += string.Template(ConstReferences_template).substitute(substitutions) # handle user provided extra code extracode_declarations = "" extracode = "" constextracode_declarations = "" constextracode = "" if definition.has_key("ExtraCode"): extra = definition["ExtraCode"] if( extra.has_key("declaration")): extracode_declarations = extra["declaration"].replace("{name}",rawclassname) if( extra.has_key("implementation")): extracode = extra["implementation"].replace("{name}",rawclassname) if( extra.has_key("const_declaration")): constextracode_declarations = extra["const_declaration"].replace("{name}","Const"+rawclassname) extracode_declarations += "\n" extracode_declarations += extra["const_declaration"] if( extra.has_key("const_implementation")): constextracode = extra["const_implementation"].replace("{name}","Const"+rawclassname) extracode += "\n" extracode += extra["const_implementation"].replace("{name}",rawclassname) # TODO: add loading of code from external files if( extra.has_key("includes")): datatype["includes"].append( extra["includes"] ) print " ***** adding includes : " , extra["includes"] , "to" , datatype["includes"] substitutions = {"includes" : "\n".join(datatype["includes"]), "includes_cc" : includes_cc, "forward_declarations" : forward_declarations, "getters" : getter_implementations, "getter_declarations": getter_declarations, "setters" : setter_implementations, "setter_declarations": setter_declarations, "constructor_declaration" : constructor_declaration, "constructor_implementation" : constructor_implementation, "extracode" : extracode, "extracode_declarations" : extracode_declarations, "name" : rawclassname, "description" : datatype["description"], "author" : datatype["author"], "relations" : references, "relation_declarations" : references_declarations, "relation_members" : references_members, "package_name" : self.package_name, "namespace_open" : namespace_open, "namespace_close" : namespace_close, } self.fill_templates("Object",substitutions) self.created_classes.append(classname) substitutions["constructor_declaration"] = ConstConstructor_declaration substitutions["constructor_implementation"] = ConstConstructor_implementation substitutions["relation_declarations"] = ConstReferences_declarations substitutions["relations"] = ConstReferences substitutions["getters"] = ConstGetter_implementations substitutions["constextracode"] = constextracode substitutions["constextracode_declarations"] = constextracode_declarations self.fill_templates("ConstObject", substitutions) if "::" in classname: self.created_classes.append("%s::Const%s" %(namespace, rawclassname)) else: self.created_classes.append("Const%s" %classname) def create_collection(self, classname, definition): namespace, rawclassname, namespace_open, namespace_close = self.demangle_classname(classname) members = definition["Members"] constructorbody = "" destructorbody = "" prepareforwritinghead = "" prepareforwritingbody = "" vectorized_access_decl, vectorized_access_impl = self.prepare_vectorized_access(rawclassname,members) setreferences = "" prepareafterread = "" includes = "" initializers = "" relations = "" create_relations = "" clear_relations = "" push_back_relations = "" prepareafterread_refmembers = "" prepareforwriting_refmembers = "" refmembers = definition["OneToOneRelations"] refvectors = definition["OneToManyRelations"] nOfRefVectors = len(refvectors) nOfRefMembers = len(refmembers) if nOfRefVectors + nOfRefMembers > 0: # member initialization #constructorbody += "\tm_refCollections = new podio::CollRefCollection();\n" destructorbody += "\tfor (auto& pointer : m_refCollections) { if (pointer != nullptr) delete pointer; }\n" clear_relations += "\tfor (auto& pointer : m_refCollections) { pointer->clear(); }\n" for counter, item in enumerate(refvectors): name = item["name"] klass = item["type"] substitutions = { "counter" : counter, "class" : klass, "name" : name } mnamespace, klassname, _, __ = self.demangle_classname(klass) # includes includes += '#include "%sCollection.h" \n' %(klassname) # FIXME check if it compiles with :: for both... then delete this. relations += declarations["relation"].format(namespace=mnamespace, type=klassname, name=name) relations += declarations["relation_collection"].format(namespace=mnamespace, type=klassname, name=name) initializers += implementations["ctor_list_relation"].format(namespace=mnamespace, type=klassname, name=name) constructorbody += "\tm_refCollections.push_back(new std::vector<podio::ObjectID>());\n" # relation handling in ::create create_relations += "\tm_rel_{name}_tmp.push_back(obj->m_{name});\n".format(name=name) # relation handling in ::clear clear_relations += implementations["clear_relations_vec"].format(name=name) clear_relations += implementations["clear_relations"].format(name=name) # relation handling in dtor: destructorbody += implementations["destroy_relations"].format(name=name) # relation handling in push_back push_back_relations += "\tm_rel_{name}_tmp.push_back(obj->m_{name});\n".format(name=name) # relation handling in ::prepareForWrite prepareforwritinghead += "\tint {name}_index =0;\n".format(name=name) prepareforwritingbody += self.evaluate_template("CollectionPrepareForWriting.cc.template",substitutions) # relation handling in ::settingReferences setreferences += self.evaluate_template("CollectionSetReferences.cc.template",substitutions) prepareafterread += "\t\tobj->m_%s = m_rel_%s;" %(name, name) for counter, item in enumerate(refmembers): name = item["name"] klass = item["type"] mnamespace = "" klassname = klass if "::" in klass: mnamespace, klassname = klass.split("::") substitutions = { "counter" : counter+nOfRefVectors, "class" : klass, "rawclass" : klassname, "name" : name } # includes includes += '#include "%sCollection.h"\n' %(klassname) # constructor call initializers += implementations["ctor_list_relation"].format(namespace=mnamespace, type=klassname, name=name) # member relations += declarations["relation"].format(namespace=mnamespace, type=klassname, name=name) constructorbody += "\tm_refCollections.push_back(new std::vector<podio::ObjectID>());\n" # relation handling in ::clear clear_relations += implementations["clear_relations"].format(name=name) # relation handling in dtor: destructorbody += implementations["destroy_relations"].format(name=name) # relation handling in ::prepareForWrite prepareforwriting_refmembers += implementations["prep_writing_relations"].format(name=name, i=(counter+nOfRefVectors)) # relation handling in ::settingReferences prepareafterread_refmembers += self.evaluate_template("CollectionSetSingleReference.cc.template",substitutions) substitutions = { "name" : rawclassname, "constructorbody" : constructorbody, "destructorbody" : destructorbody, "prepareforwritinghead" : prepareforwritinghead, "prepareforwritingbody" : prepareforwritingbody, "prepareforwriting_refmembers" : prepareforwriting_refmembers, "setreferences" : setreferences, "prepareafterread" : prepareafterread, "prepareafterread_refmembers" : prepareafterread_refmembers, "includes" : includes, "initializers" : initializers, "relations" : relations, "create_relations" : create_relations, "clear_relations" : clear_relations, "push_back_relations" : push_back_relations, "package_name" : self.package_name, "vectorized_access_declaration" : vectorized_access_decl, "vectorized_access_implementation" : vectorized_access_impl, "namespace_open" : namespace_open, "namespace_close" : namespace_close } self.fill_templates("Collection",substitutions) self.created_classes.append("%sCollection"%classname) def create_PrintInfo(self, classname, definition): namespace, rawclassname, namespace_open, namespace_close = self.demangle_classname(classname) toFormattingStrings = {"char" : "3" , "unsigned char" : "3", "long":"11", "longlong":"22", "bool":"1", "int":""} formats = {"char" : 3 , "unsigned char" : 3, "long": 11, "longlong": 22 , "bool": 1,} outputSingleObject = 'o << "' + classname + ':" << std::endl; \n o << ' WidthIntegers = "" widthOthers = "" findMaximum = "" setFormats = "" formattedOutput = "" tableHeader = "" for member in definition["Members"]: name = member["name"] lengthName = len(name) klass = member["type"] if(klass in toFormattingStrings and not klass == "int"): if(formats[klass] > lengthName): widthOthers = widthOthers + " int"+ " " + name + "Width = " + toFormattingStrings[klass] + " ; \n" else: widthOthers = widthOthers + " int" + " " + name + "Width = " + str(lengthName) + " ; \n" elif klass == "int": findMaximum += "\n int " + name + "Max ; \n" findMaximum += " " + name + "Width = 1 ; \n " findMaximum += " for(int i = 0 ; i < value.size(); i++){ \n" findMaximum += " if( value[i].get" + name[:1].upper() + name[1:] + "() > 0 ){ \n" findMaximum += " if(" + name + "Max < value[i].get" + name[:1].upper() + name[1:] + "()){ \n" findMaximum += " " + name + "Max = value[i].get" + name[:1].upper() + name[1:] + "();" findMaximum += "\n } \n" findMaximum += "\n } \n" findMaximum += " else if( -" + name + "Max / 10 > value[i].get" + name[:1].upper() + name[1:] + "()){ \n" findMaximum += " " + name + "Max = - value[i].get" + name[:1].upper() + name[1:] + "() * 10; " findMaximum += "\n } \n" findMaximum += " } \n" setFormats += "\n while(" + name + "Max != 0){ \n" setFormats += " " + name + "Width++; \n " + name + "Max = " + name + "Max / 10; \n } \n" setFormats += " if(" + name + "Width < " + str(lengthName) + "){ " + name + "Width = " + str(lengthName) + ";} \n" WidthIntegers = WidthIntegers + " " + klass + " " + name + "Width = 1 ; \n" elif(klass == "double" or klass == "float"): if(lengthName > 12): widthOthers = widthOthers + " int" + " " + name + "Width = " + str(lengthName) + " ; \n" else: widthOthers = widthOthers + " int" + " " + name + "Width = 12 ; \n" elif(klass == "DoubleThree" or klass == "FloatThree"): if(lengthName > 38): widthOthers = widthOthers + " int" + " " + name + "Width = " + str(lengthName) + " ; \n" else: widthOthers += " int" + " " + name + "Width = 38 ; \n" elif(klass == "IntTwo"): if(lengthName > 24): widthOthers = widthOthers + " int" + " " + name + "Width = " + str(lengthName) + " ; \n" else: widthOthers += " int" + " " + name + "Width = 24 ; \n" else: widthOthers = widthOthers + " int" + " " + name + "Width = 38 ; \n" if(klass != "StingVec"): tableHeader += 'std::setw(' + classname + "PrintInfo::instance()." + name + 'Width) << "' + name + '" << "|" << ' if(klass == "DoubleThree" or klass == "FloatThree" or klass == "StringVec"): formattedOutput += " value[i].get" + name[:1].upper() + name[1:] + '() << " "' + " << " else: formattedOutput += " std::setw(" + classname + "PrintInfo::instance()." + name + "Width)" formattedOutput += " << value[i].get" + name[:1].upper() + name[1:] + '() << " "' + " << " outputSingleObject += '"' + klass + '" << " " << "' + name + '" << " "' + " << value.get" + name[:1].upper() + name[1:] + '() << " "' + " << " substitutions = { "name" : rawclassname, "widthIntegers" : WidthIntegers, "widthOthers" : widthOthers, "findMaximum" : findMaximum, "setFormats" : setFormats, "formattedOutput" : formattedOutput, "tableHeader" : tableHeader, "outputSingleObject" : outputSingleObject } # TODO: add loading of code from external files self.fill_templates("PrintInfo",substitutions) self.created_classes.append(classname) def create_component(self, classname, components): """ Create a component class to be used within the data types Components can only contain simple data types and no user defined ones """ namespace, rawclassname, namespace_open, namespace_close = self.demangle_classname(classname) includes = "" members = "" extracode_declarations = "" ostreamComponents = "" printed = [""] self.component_members[classname] = [] #fg: sort the dictionary, so at least we get a predictable order (alphabetical) of the members keys = sorted( components.keys() ) for name in keys: klass = components[ name ] # for name, klass in components.iteritems(): if( name != "ExtraCode"): klassname = klass mnamespace = "" if "::" in klass: mnamespace, klassname = klass.split("::") if mnamespace == "": if((classname == "DoubleThree" or classname == "FloatThree") and not classname in printed): ostreamComponents += " inline std::ostream& operator<<( std::ostream& o,const " + classname + "& value ){ \n" ostreamComponents += " for(unsigned int i = 0; i < 3; i++){ \n" ostreamComponents += ' o << value[i] << " " ; } \n' ostreamComponents += " return o ; \n" ostreamComponents += " } \n \n" printed += [classname] elif(classname == "IntTwo" and not classname in printed): ostreamComponents += " inline std::ostream& operator<<( std::ostream& o,const " + classname + "& value ){ \n" ostreamComponents += " for(unsigned int i = 0; i < 2; i++){ \n" ostreamComponents += ' o << value[i] << " " ; } \n' ostreamComponents += " return o ; \n" ostreamComponents += " } \n \n" printed += [classname] elif(classname == "StringVec" and not classname in printed): ostreamComponents += " inline std::ostream& operator<<( std::ostream& o,const " + classname + "& value ){ \n" ostreamComponents += " for(unsigned int i = 0; i < value.size(); i++){" ostreamComponents += ' o << value[i] << " " ; } \n' ostreamComponents += " return o ; \n" ostreamComponents += " } \n \n" printed += [classname] members+= " %s %s;\n" %(klassname, name) self.component_members[classname].append([klassname, name]) else: members += " ::%s::%s %s;\n" %(mnamespace, klassname, name) self.component_members[classname].append(["::%s::%s" % (mnamespace, klassname), name]) if self.reader.components.has_key(klass): includes+= '#include "%s.h"\n' %(klassname) else: # handle user provided extra code if klass.has_key("declaration"): extracode_declarations = klass["declaration"] substitutions = { "ostreamComponents" : ostreamComponents, "includes" : includes, "members" : members, "extracode_declarations" : extracode_declarations, "name" : rawclassname, "package_name" : self.package_name, "namespace_open" : namespace_open, "namespace_close" : namespace_close } self.fill_templates("Component",substitutions) self.created_classes.append(classname) def create_obj(self, classname, definition): """ Create an obj class containing all information relevant for a given object. """ namespace, rawclassname, namespace_open, namespace_close = self.demangle_classname(classname) relations = "" includes = "" includes_cc = "" forward_declarations = "" forward_declarations_namespace = {"":[]} initialize_relations = "" set_relations = "" deepcopy_relations = "" delete_relations = "" delete_singlerelations = "" refvectors = definition["OneToManyRelations"] singleRelations = definition["OneToOneRelations"] # do includes and forward declarations for # oneToOneRelations and do proper cleanups for item in singleRelations: name = item["name"] klass = item["type"] klassname = klass mnamespace = "" if klass not in self.buildin_types: if "::" in klass: mnamespace, klassname = klass.split("::") klassWithQualifier = "::"+mnamespace+"::Const"+klassname if mnamespace not in forward_declarations_namespace.keys(): forward_declarations_namespace[mnamespace] = [] else: klassWithQualifier = "Const"+klass else: klassWithQualifier = klass if mnamespace != "": relations+= " ::%s::Const%s* m_%s;\n" %(mnamespace, klassname, name) else: relations+= " Const%s* m_%s;\n" %(klassname, name) if klass not in self.buildin_types: if klass != classname: forward_declarations_namespace[mnamespace] += ['class Const%s;\n' %(klassname)] includes_cc += '#include "%sConst.h"\n' %(klassname) initialize_relations += ", m_%s(nullptr)\n" %(name) # for deep copy initialise as nullptr and set in copy ctor body if copied object has non-trivial relation deepcopy_relations += ", m_%s(nullptr)" % (name) set_relations += implementations["set_relations"].format(name=name, klass=klassWithQualifier) delete_singlerelations+="\t\tif (m_%s != nullptr) delete m_%s;\n" % (name, name) for nsp in forward_declarations_namespace.iterkeys(): if nsp != "": forward_declarations += "namespace %s {" % nsp forward_declarations += "".join(forward_declarations_namespace[nsp]) if nsp != "": forward_declarations += "}\n" if len(refvectors+definition["VectorMembers"]) !=0: includes += "#include <vector>\n" for item in refvectors+definition["VectorMembers"]: name = item["name"] klass = item["type"] if klass not in self.buildin_types: if klass not in self.reader.components: if "::" in klass: mnamespace, klassname = klass.split("::") klassWithQualifier = "::"+mnamespace+"::Const"+klassname else: klassWithQualifier = "Const"+klass else: klassWithQualifier = klass relations += "\tstd::vector<%s>* m_%s;\n" %(klassWithQualifier, name) initialize_relations += ", m_%s(new std::vector<%s>())" %(name,klassWithQualifier) deepcopy_relations += ", m_%s(new std::vector<%s>(*(other.m_%s)))" %(name,klassWithQualifier,name) if klass == classname: includes_cc += '#include "%s.h"\n' %(rawclassname) else: if "::" in klass: mnamespace, klassname = klass.split("::") includes += '#include "%s.h"\n' %klassname else: includes += '#include "%s.h"\n' %klass else: relations += "\tstd::vector<%s>* m_%s;\n" %(klass, name) initialize_relations += ", m_%s(new std::vector<%s>())" %(name,klass) deepcopy_relations += ", m_%s(new std::vector<%s>(*(other.m_%s)))" %(name,klass,name) delete_relations += "\t\tdelete m_%s;\n" %(name) substitutions = { "name" : rawclassname, "includes" : includes, "includes_cc" : includes_cc, "forward_declarations" : forward_declarations, "relations" : relations, "initialize_relations" : initialize_relations, "deepcopy_relations" : deepcopy_relations, "delete_relations" : delete_relations, "delete_singlerelations" : delete_singlerelations, "namespace_open" : namespace_open, "namespace_close" : namespace_close, "set_deepcopy_relations": set_relations } self.fill_templates("Obj",substitutions) self.created_classes.append(classname+"Obj") def prepare_vectorized_access(self, classname,members ): implementation = "" declaration = "" for member in members: name = member["name"] klass = member["type"] substitutions = { "classname" : classname, "member" : name, "type" : klass } if klass not in self.buildin_types: substitutions["type"] = "class %s" % klass implementation += self.evaluate_template("CollectionReturnArray.cc.template", substitutions) declaration += "\ttemplate<size_t arraysize>\n\tconst std::array<%s,arraysize> %s() const;\n" %(klass, name) return declaration, implementation def write_file(self, name,content): #dispatch headers to header dir, the rest to /src # fullname = os.path.join(self.install_dir,self.package_name,name) if name.endswith("h"): fullname = os.path.join(self.install_dir,self.package_name,name) else: fullname = os.path.join(self.install_dir,"src",name) if not self.dryrun: open(fullname, "w").write(content) def evaluate_template(self, filename, substitutions): """ reads in a given template, evaluates it and returns result """ templatefile = os.path.join(self.template_dir,filename) template = open(templatefile,"r").read() return string.Template(template).substitute(substitutions) def fill_templates(self, category, substitutions): # "Data" denotes the real class; # only headers and the FN should not contain Data if category == "Data": FN = "Data" endings = ("h") elif category == "Obj": FN = "Obj" endings = ("h","cc") elif category == "Component": FN = "" endings = ("h") elif category == "Object": FN = "" endings = ("h","cc") elif category == "ConstObject": FN = "Const" endings = ("h","cc") elif category == "PrintInfo": FN = "PrintInfo" endings = ("h") else: FN = category endings = ("h","cc") for ending in endings: templatefile = "%s.%s.template" %(category,ending) templatefile = os.path.join(self.template_dir,templatefile) template = open(templatefile,"r").read() content = string.Template(template).substitute(substitutions).expandtabs(2) filename = "%s%s.%s" %(substitutions["name"],FN,ending) self.write_file(filename, content)