def check_semantics(model, metamodel): parser = metamodel.parser # Check config model if not model.config: raise TextXSemanticError('Config is required') # If app label does not exist, put app name instead. if not model.config.app_label: model.config.app_label = model.config.app_name model.config.qname = model.config.namespace + '.' + camel_to_under(model.config.app_name) if model.config.platforms.android and not model.config.android_specs: raise TextXSemanticError('Android specs are required if the android platform is specified') android_specs = model.config.android_specs if not android_specs: android_specs.min_version = DEF_ANDROID_MIN_VER android_specs.target_version = DEF_ANDROID_TARGET_VER model.config.android_specs = android_specs else: if not android_specs.min_version: android_specs.min_version = DEF_ANDROID_MIN_VER if not android_specs.target_version: android_specs.target_version = DEF_ANDROID_TARGET_VER if android_specs.min_version > android_specs.target_version: raise TextXSemanticError('In Android Specs: Minimum version cannot be greater than the target version, {}' .format(print_position(parser, android_specs._position))) if model.config.start_screen: if not model.config.start_screen.operations.listall: raise TextXSemanticError("Start screen must be an entity that has the 'listall' operation, " "start screen entity = '{}', {}" .format(model.config.start_screen.name, print_position(parser, model.config._position))) if not model.entities: raise TextXSemanticError('At least one entity is required') if not [x for x in model.entities if x.operations.listall]: raise TextXSemanticError("At least one entity with the 'listAll' operation is required") #Checking duplicate entity names entity_names = [x.name for x in model.entities] if len(entity_names) != len(set(entity_names)): raise TextXSemanticError('Duplicate entity names detected!') for entity in model.entities: # If entity label does not exist, put entity name instead. if not entity.label: entity.label = entity.name attribute_names = [x.name for x in entity.attributes] if len(attribute_names) != len(set(attribute_names)): raise TextXSemanticError('Duplicate attribute names in entity "{}" detected, {}'.format(entity.name, print_position(parser, entity._position))) for attr in entity.attributes: # If attribute label does not exist, put attribute name instead. if not attr.label: attr.label = attr.name.title() if attr.view_from_container and not attr.reference_type: raise TextXSemanticError( "Cannot have 'viewFromContainer' on an non-reference type, in attribute '{}', entity '{}', {}" .format(attr.name, entity.name, print_position(parser, attr._position))) #Setting toString of the entity if attr.to_string: if not attr.primitive_type or attr.primitive_type == 'image': raise TextXSemanticError( "toString attribute must be a non-image primitive type, in attribute '{}', entity '{}', {}" .format(attr.name, entity.name, print_position(parser, attr._position))) if not hasattr(entity, 'to_string'): entity.to_string = attr else: raise TextXSemanticError( "Entity cannot have more than one toString attribute, in attribute '{}'" ", toString attribute '{}' entity '{}', {}" .format(attr.name, entity.to_string.name, entity.name, print_position(parser, attr._position))) if attr.searchable and attr.reference_type: if not hasattr(attr.reference_type, 'to_string'): if len([ref_attr for ref_attr in attr.reference_type.attributes if ref_attr.to_string]) == 0: raise TextXSemanticError( "The entity that is reference by a searchable attribute must have a 'toString' attribute, " "\nfrom searchable attribute '{}' in entity '{}', {}" .format(attr.name, entity.name, print_position(parser, attr._position))) # Unique_set check for unique_set in entity.unique_sets: if len(unique_set.attributes) == 1: raise TextXSemanticError("Unique set must contain at least two attributes, in entity {}, {}" .format(entity.name, print_position(parser, unique_set._position))) for attr in unique_set.attributes: if attr.name not in [x.name for x in entity.attributes]: raise TextXSemanticError("Unique set must only contain attributes from the parent entity {}. " "Attribute {} does not belong here, {}" .format(entity.name, attr.name, print_position(parser, unique_set._position))) return model
def check_semantics(model, metamodel): parser = metamodel.parser # Check config model if not model.config: raise TextXSemanticError('Config is required') # If app label does not exist, put app name instead. if not model.config.app_label: model.config.app_label = model.config.app_name model.config.qname = model.config.namespace + '.' + camel_to_under( model.config.app_name) if model.config.platforms.android and not model.config.android_specs: raise TextXSemanticError( 'Android specs are required if the android platform is specified') android_specs = model.config.android_specs if not android_specs: android_specs.min_version = DEF_ANDROID_MIN_VER android_specs.target_version = DEF_ANDROID_TARGET_VER model.config.android_specs = android_specs else: if not android_specs.min_version: android_specs.min_version = DEF_ANDROID_MIN_VER if not android_specs.target_version: android_specs.target_version = DEF_ANDROID_TARGET_VER if android_specs.min_version > android_specs.target_version: raise TextXSemanticError( 'In Android Specs: Minimum version cannot be greater than the target version, {}' .format(print_position(parser, android_specs._position))) if model.config.start_screen: if not model.config.start_screen.operations.listall: raise TextXSemanticError( "Start screen must be an entity that has the 'listall' operation, " "start screen entity = '{}', {}".format( model.config.start_screen.name, print_position(parser, model.config._position))) if not model.entities: raise TextXSemanticError('At least one entity is required') if not [x for x in model.entities if x.operations.listall]: raise TextXSemanticError( "At least one entity with the 'listAll' operation is required") #Checking duplicate entity names entity_names = [x.name for x in model.entities] if len(entity_names) != len(set(entity_names)): raise TextXSemanticError('Duplicate entity names detected!') for entity in model.entities: # If entity label does not exist, put entity name instead. if not entity.label: entity.label = entity.name attribute_names = [x.name for x in entity.attributes] if len(attribute_names) != len(set(attribute_names)): raise TextXSemanticError( 'Duplicate attribute names in entity "{}" detected, {}'.format( entity.name, print_position(parser, entity._position))) for attr in entity.attributes: # If attribute label does not exist, put attribute name instead. if not attr.label: attr.label = attr.name.title() if attr.view_from_container and not attr.reference_type: raise TextXSemanticError( "Cannot have 'viewFromContainer' on an non-reference type, in attribute '{}', entity '{}', {}" .format(attr.name, entity.name, print_position(parser, attr._position))) #Setting toString of the entity if attr.to_string: if not attr.primitive_type or attr.primitive_type == 'image': raise TextXSemanticError( "toString attribute must be a non-image primitive type, in attribute '{}', entity '{}', {}" .format(attr.name, entity.name, print_position(parser, attr._position))) if not hasattr(entity, 'to_string'): entity.to_string = attr else: raise TextXSemanticError( "Entity cannot have more than one toString attribute, in attribute '{}'" ", toString attribute '{}' entity '{}', {}".format( attr.name, entity.to_string.name, entity.name, print_position(parser, attr._position))) if attr.searchable and attr.reference_type: if not hasattr(attr.reference_type, 'to_string'): if len([ ref_attr for ref_attr in attr.reference_type.attributes if ref_attr.to_string ]) == 0: raise TextXSemanticError( "The entity that is reference by a searchable attribute must have a 'toString' attribute, " "\nfrom searchable attribute '{}' in entity '{}', {}" .format(attr.name, entity.name, print_position(parser, attr._position))) # Unique_set check for unique_set in entity.unique_sets: if len(unique_set.attributes) == 1: raise TextXSemanticError( "Unique set must contain at least two attributes, in entity {}, {}" .format(entity.name, print_position(parser, unique_set._position))) for attr in unique_set.attributes: if attr.name not in [x.name for x in entity.attributes]: raise TextXSemanticError( "Unique set must only contain attributes from the parent entity {}. " "Attribute {} does not belong here, {}".format( entity.name, attr.name, print_position(parser, unique_set._position))) return model
def gen_android(model, output_folder, overwrite_manifest=True, eclipse_gen=True): """ Generate the android application with the applang parser that contains the model. :param parser: The parser which was used to parse the applang language :param debug(bool): Debug mode :param output_folder(str): Folder to output the generated app :return: """ print print_log("~~~~Generating the ANDROID application~~~~") print config = model.config android_specs = config.android_specs print_log("App name: {}".format(config.app_name)) print_log("Namespace: {}".format(config.namespace)) print_log("Full Qname: {}".format(config.qname)) print_log("Min version: {}".format(android_specs.min_version)) print_log("Target version: {}".format(android_specs.target_version)) if android_specs.sdk_path is None: print_log("WARNING: The Android SDK Path is not set! " "This will cause problems if the libraries haven't been copied yet!") if android_specs.sdk_path is not None and not os.path.exists(android_specs.sdk_path): raise GeneratorException("The SDK in the specified path '{}' does not exist!" .format(android_specs.sdk_path)) #Checking if the output folder exists. If it doesn't, creates it. android_gen_folder = os.path.join(output_folder, "android", '') if not os.path.exists(android_gen_folder): print_log("Folder for storing generated android applications does not exist. It will be created: {}" .format(os.path.abspath(android_gen_folder))) os.makedirs(android_gen_folder) output_app_folder = os.path.join(android_gen_folder, config.namespace.replace('.', os.path.sep), config.app_name, '') if not os.path.exists(output_app_folder): print_log("Folder for storing the generated android application does not exist. It will be created: {}" .format(os.path.abspath(android_gen_folder))) os.makedirs(output_app_folder) print_log('App folder: {}'.format(os.path.abspath(android_gen_folder))) print #Checking if the V7 appcompat library exists. #If it doesn't, copies it from the Android SDK. output_appcompat_folder = os.path.join(android_gen_folder, 'appcompat', '') if os.path.exists(output_appcompat_folder): print_log("Appcompat library exists in {}. No need to copy it from the android SDK." .format(os.path.abspath(output_appcompat_folder))) else: print_log("Appcompat does not exist in the generated folder. Copying in from the android SDK to {}" .format(os.path.abspath(output_appcompat_folder))) copy_tree(src=android_specs.sdk_path + PATH_APPCOMPAT_V7, dst=output_appcompat_folder) #Framework copying frameworks_folder = os.path.join(package_directory, "frameworks", "") print print_log("Copying the app framework to the output folder: {}".format(os.path.abspath(output_app_folder))) copy_tree(src=os.path.join(frameworks_folder, "android"), dst=android_gen_folder) print_log("Finished copying the app framework.") print #File generation environment = Environment(loader=FileSystemLoader( os.path.join(package_directory, 'templates', 'android'))) environment.filters['cameltounder'] = camel_to_under environment.trim_blocks = True environment.lstrip_blocks = True environment.globals['xml_gen_comment'] = XML_GEN_COMMENT environment.globals['java_gen_comment'] = JAVA_GEN_COMMENT environment.globals['xml_gen_once_comment'] = XML_GEN_ONCE_COMMENT environment.globals['java_gen_once_comment'] = JAVA_GEN_ONCE_COMMENT if eclipse_gen or \ query_yes_no("Do you want to generate " "(and possibly overwrite) the eclipse files (.project and .classpath)?"): print_log("Generating the eclipse files") copy_file(src=os.path.join(frameworks_folder, 'eclipse_files', '.classpath'), dst=output_app_folder) generate_from_template(environment, "dot.project.tmpl", output_app_folder, ".project", config=config) print_log("Finished generating the eclipse files") # AndroidManifest.xml generation generate_overwrite_backup_from_template(environment, "AndroidManifest.tmpl", output_app_folder, "AndroidManifest.xml", overwrite_manifest, config=config, android_specs=android_specs, entities=model.entities, has_image=[attr for entity in model.entities for attr in entity.attributes if attr.primitive_type and attr.primitive_type == 'image']) # project.properties generation generate_from_template(environment, "project.properties.tmpl", output_app_folder, "project.properties", android_specs=android_specs, namespace_dot_count=config.namespace.count('.')) # res/values folder creation output_gen_values_folder = os.path.join(output_app_folder, 'res', 'values', '') if not os.path.exists(output_gen_values_folder): os.makedirs(output_gen_values_folder) # libs folder creation output_gen_libs_folder = os.path.join(output_app_folder, 'libs', '') if not os.path.exists(output_gen_libs_folder): os.makedirs(output_gen_libs_folder) # assets folder creation output_gen_assets_folder = os.path.join(output_app_folder, 'assets', '') if not os.path.exists(output_gen_assets_folder): os.makedirs(output_gen_assets_folder) # gen_string_entities.xml generation generate_from_template(environment, "gen_strings_entities.xml.tmpl", output_gen_values_folder, "gen_strings_entities.xml", app_label=config.app_label, entities=model.entities) # gen_arrays.xml generation generate_from_template(environment, "gen_arrays.xml.tmpl", output_gen_values_folder, "gen_arrays.xml", config=config, entities=[entity for entity in model.entities if entity.operations.listall]) output_src_folder = os.path.dirname(os.path.join(output_app_folder, 'src', config.qname.replace(".", os.path.sep), '')) if not os.path.exists(output_src_folder): os.makedirs(output_src_folder) # src gen folder creation output_src_gen_folder = os.path.join(output_app_folder, 'src-gen', config.qname.replace(".", os.path.sep), '') if not os.path.exists(output_src_gen_folder): os.makedirs(output_src_gen_folder) # databases folder creation output_src_gen_databases_folder = os.path.join(output_src_gen_folder, 'databases', '') if not os.path.exists(output_src_gen_databases_folder): os.makedirs(output_src_gen_databases_folder) # content_providers folder creation output_src_gen_content_providers_folder = os.path.join(output_src_gen_folder, 'content_providers', '') if not os.path.exists(output_src_gen_content_providers_folder): os.makedirs(output_src_gen_content_providers_folder) # adapters folder creation output_src_gen_adapters_folder = os.path.join(output_src_gen_folder, 'adapters', '') if not os.path.exists(output_src_gen_adapters_folder): os.makedirs(output_src_gen_adapters_folder) # fragments folder creation output_src_gen_fragments_folder = os.path.join(output_src_gen_folder, 'fragments', '') if not os.path.exists(output_src_gen_fragments_folder): os.makedirs(output_src_gen_fragments_folder) # res/layout folder creation output_gen_layout_folder = os.path.join(output_app_folder, 'res', 'layout', '') if not os.path.exists(output_gen_layout_folder): os.makedirs(output_gen_layout_folder) # DatabaseOpenHelper.java generate_from_template(environment, "DatabaseOpenHelper.java.tmpl", output_src_gen_databases_folder, "DatabaseOpenHelper.java", config=config, entities=model.entities) for entity in model.entities: # Used for import statements in the code. ref_entity_names = {attr.reference_type.name for attr in entity.attributes if attr.reference_type} # User for view_from_container view_container_attrs = [(entity_container, attr) for entity_container in model.entities for attr in entity_container.attributes if attr.reference_type and attr.reference_type.name == entity.name and attr.view_from_container] view_container_ref_entity_names = {x[0].name for x in view_container_attrs} # {{Entity}}Table.java generate_from_template(environment, "EntityTable.java.tmpl", output_src_gen_databases_folder, "{}Table.java".format(entity.name), config=config, entity=entity, TYPE_TEXT_IN_DB=TYPE_TEXT_IN_DB, TYPE_INT_IN_DB=TYPE_INT_IN_DB, TYPE_REAL_IN_DB=TYPE_REAL_IN_DB) #{{Entity}}ContentProvider.java generate_from_template(environment, "EntityContentProvider.java.tmpl", output_src_gen_content_providers_folder, "{}ContentProvider.java".format(entity.name), config=config, entity=entity, ref_entity_names=ref_entity_names) #{{Entity}}ListFragment.java generate_from_template(environment, "EntityListFragment.java.tmpl", output_src_gen_fragments_folder, "{}ListFragment.java".format(entity.name), config=config, entity=entity, ref_entity_names=ref_entity_names) #{{Entity}}DetailsFragment.java generate_from_template(environment, "EntityDetailsFragment.java.tmpl", output_src_gen_fragments_folder, "{}DetailsFragment.java".format(entity.name), config=config, entity=entity, ref_entity_names=ref_entity_names, view_container_attrs=view_container_attrs, view_container_ref_entity_names=view_container_ref_entity_names) #{{Entity}}Adapters.java generate_from_template(environment, "EntityAdapter.java.tmpl", output_src_gen_adapters_folder, "{}Adapter.java".format(entity.name), config=config, entity=entity, ref_entity_names=ref_entity_names) #{{Entity}}NewFragment.java generate_from_template(environment, "EntityNewFragment.java.tmpl", output_src_gen_fragments_folder, "{}NewFragment.java".format(entity.name), config=config, entity=entity, ref_entity_names=ref_entity_names) #{{Entity}}EditFragment.java generate_from_template(environment, "EntityEditFragment.java.tmpl", output_src_gen_fragments_folder, "{}EditFragment.java".format(entity.name), config=config, entity=entity, ref_entity_names=ref_entity_names) generate_from_template(environment, "gen_list_item_entity.xml.tmpl", output_gen_layout_folder, "gen_list_item_{}.xml".format(camel_to_under(entity.name)), entity=entity) generate_from_template(environment, "gen_fragment_entity_details.xml.tmpl", output_gen_layout_folder, "gen_fragment_{}_details.xml".format(camel_to_under(entity.name)), entity=entity, view_container_attrs=view_container_attrs) generate_from_template(environment, "gen_fragment_entity_new.xml.tmpl", output_gen_layout_folder, "gen_fragment_{}_new.xml".format(camel_to_under(entity.name)), entity=entity) generate_from_template(environment, "gen_fragment_entity_edit.xml.tmpl", output_gen_layout_folder, "gen_fragment_{}_edit.xml".format(camel_to_under(entity.name)), entity=entity) # Generating impl classes ONCE output_src_impl_databases_folder = os.path.join(output_src_folder, 'impl', 'databases', '') if not os.path.exists(output_src_impl_databases_folder): os.makedirs(output_src_impl_databases_folder) output_src_impl_fragments_folder = os.path.join(output_src_folder, 'impl', 'fragments', '') if not os.path.exists(output_src_impl_fragments_folder): os.makedirs(output_src_impl_fragments_folder) # DatabaseOpenHelperImpl.java generate generate_once_from_template(environment, "DatabaseOpenHelperImpl.java.tmpl", output_src_impl_databases_folder, "DatabaseOpenHelperImpl.java", config=config) for entity in model.entities: generate_once_from_template(environment, "EntityListFragmentImpl.java.tmpl", output_src_impl_fragments_folder, "{}ListFragmentImpl.java".format(entity.name), config=config, entity=entity) generate_once_from_template(environment, "EntityNewFragmentImpl.java.tmpl", output_src_impl_fragments_folder, "{}NewFragmentImpl.java".format(entity.name), config=config, entity=entity) generate_once_from_template(environment, "EntityEditFragmentImpl.java.tmpl", output_src_impl_fragments_folder, "{}EditFragmentImpl.java".format(entity.name), config=config, entity=entity) generate_once_from_template(environment, "EntityDetailsFragmentImpl.java.tmpl", output_src_impl_fragments_folder, "{}DetailsFragmentImpl.java".format(entity.name), config=config, entity=entity) print print_log("Finished generating the ANDROID application in {}".format(os.path.abspath(android_gen_folder))) print