def registerSourceDependency(cls, sourceDependency): # type: (SourceFileDependency) -> None """ Register a source file dependency. Register before the source and target if not done before. Also register the model dependency if needed. """ source = sourceDependency.source target = sourceDependency.target # Element registration cls.registerSource(source) cls.registerSource(target) from modelscripts.megamodels import Megamodel Megamodel.registerModel(source.model) Megamodel.registerModel(target.model) cls._allSourceFileDependencies.append(sourceDependency) # BySource if source not in cls._sourceFileDependenciesBySource: cls._sourceFileDependenciesBySource[source] = [] cls._sourceFileDependenciesBySource[source].append(sourceDependency) # ByTarget if target not in cls._sourceFileDependenciesByTarget: cls._sourceFileDependenciesByTarget[target] = [] cls._sourceFileDependenciesByTarget[target].append(sourceDependency)
def __init__(self, qname): super(MetaCheckerPackage, self).__init__(qname=qname) self.pyPackageName = 'modelscripts.metamodels.' + qname if DEBUG>=1: print('MM3: import metacheckerpackage %s' % self.pyPackageName) self.pyModule=importlib.import_module( self.pyPackageName) #type: types.ModuleType Megamodel.registerMetaCheckerPackage(self)
def __init__(self, sourceModel, targetModel, sourceElement=None): #type: (Model, Model, Optional['SourceElement']) -> None self.sourceModel=sourceModel self.targetModel=targetModel self.sourceElement=sourceElement from modelscripts.megamodels import Megamodel Megamodel.registerModelDependency(self)
def __init__(self, qname): super(MetaPackage, self).__init__(qname=qname) self.pyPackageName = 'modelscripts.metamodels.' + qname if DEBUG>=1: print('MM3: import metapackage %s' % self.pyPackageName) self.pyModule=importlib.import_module( self.pyPackageName) #type: types.ModuleType self.metaClassNamed=collections.OrderedDict() #type: List[MetaClass] Megamodel.registerMetaPackage(self)
def models(self): #type: () -> List['Model'] """ Return all models for the current metamodel. """ from modelscripts.megamodels import Megamodel return Megamodel.models(metamodel=self)
def metamodelDependency(self): #type: (ModelDependency) -> MetamodelDependency """ Return the metamodel dependency this model dependency conforms to. This could raise a ValueError if there are more than one or no metamodel dependency.This should not occur unless the metamodels are not built with care. """ # could raise a ValueError, but should not from modelscripts.megamodels import Megamodel return Megamodel.metamodelDependency( source=self.sourceModel.metamodel, target=self.targetModel.metamodel) # @property # def check(self): # """ # Check if the ModelDependency is valid. # Do nothing if it is else raise a ValueError. # This could be because there is no corresponding metadependency between metamodel. # This could also due because of too much depedency of the same type # with the same source model. # """ # # this could raise a ValueError # deps_same_type=self.sourceModel.outDependencies( # targetMetamodel=self.targetModel.metamodel) # if len(deps_same_type)>=2 and not self.metamodelDependency.multiple: # raise ValueError('A %s model can depend on at most one %s model.' # % (self.sourceModel.metamodel.label, # self.targetModel.metamodel.label))
def outMetamodelDependencies(self): """ Return all metamodel dependencies from the current metamodel. """ from modelscripts.megamodels import Megamodel return Megamodel.metamodelDependencies(source=self)
def sourceImport(self, id, optional=True): #type: (Text) -> Optional[SourceImport] """ Return "the one and only one" sourceImport for a given metamodel id. If there is no sourceImport then add an issue, unless optional is True in which case None is returned. If there is more than one sourceImport then an issue is added. """ from modelscripts.megamodels import Megamodel metamodel_label = Megamodel.theMetamodel(id=id).label if id not in self._importsByMetamodelId: if optional: return None else: Issue( origin=self.modelSource, level=Levels.Fatal, message= 'No %s model imported.' % metamodel_label ) r=self._importsByMetamodelId[id] if len(r)==1: return r[0] else: Issue( origin=self.modelSource, level=Levels.Fatal, message= 'More than on %s model imported.' % metamodel_label)
def outDependencies(self, targetMetamodel=None, metamodelDependency=None): #type: (Optional[Metamodel]) -> List[ModelDependency] """ Returns the dependencies starting from this dependency filtered either by targetMetamodel, or by metamodelDependency. """ # select all out dependencies from self from modelscripts.megamodels import Megamodel all_deps=Megamodel.modelDependencies(source=self) if targetMetamodel is None: deps=all_deps else: deps=[ dep for dep in all_deps if ( dep.targetModel.metamodel == targetMetamodel) ] if metamodelDependency is None: return deps else: return [ dep for dep in deps # could raise ValueError, but should not if (dep.metamodelDependency == metamodelDependency) ]
def test_display_megamodel(): print('Metamodels:') print(' Metamodels by id') for (id, mm) in Megamodel._metamodelById.items(): print(' %s -> %s' % (id, str(mm))) print(' Via metamodels()') for mm in Megamodel.metamodels(): print(' %s' % mm.id) print('Metamodel dependencies:') for mmd in Megamodel.metamodelDependencies(): print(' %s' % repr(mmd)) Megamodel.checkMetamodelLevel()
def __init__(self, sourceId, targetId, optional=False, multiple=True): #type: (Text, Text, bool, bool) -> None """ Create a metamodel dependency. Note that parameters are ids instead of metamodels. This is necessary to avoid python module dependency cycles. """ self.sourceId=sourceId #type: Text self.targetId=targetId #type: Text self.optional=optional #type: bool self.multiple=multiple #type: bool from modelscripts.megamodels import Megamodel Megamodel.registerMetamodelDependency(self)
def modelDependencies(self): # type: (MetamodelDependency) -> List(ModelDependency) """ Model dependencies based on this metamodel dependency. This could raise a ValueError. """ # could raise a ValueError from modelscripts.megamodels import Megamodel return Megamodel.modelDependencies( metamodelDependency=self)
def registerSource(cls, source): # type: (ModelSourceFile) -> None """ Register a source. Register the model as well. """ if source.path not in cls._sourceFileByPath: cls._allSourceFiles.append(source) # ByPath metamodel = source.metamodel cls._sourceFileByPath[source.path] = source # ByMetamodel if metamodel not in cls._sourceFilesByMetamodel: cls._sourceFilesByMetamodel[metamodel] = [] if source not in cls._sourceFilesByMetamodel[metamodel]: cls._sourceFilesByMetamodel[metamodel].append(source) # Register model if source.model is not None: from modelscripts.megamodels import Megamodel Megamodel.registerModel(source.model)
def processSourceFile(filename, manySourceFiles, args): try: source = Megamodel.loadFile(filename) except Exception as e: traceback.print_exc(e) cprint(str(e), 'red') return str(e) if manySourceFiles: cprint('#' * 30 + ' ' + filename + ' ' + '#' * 30, 'blue') printer_config = ModelPrinterConfig( # ContentPrinterConfig( # TODO: check if creating a ModelPrinterConfig is better # for models styled=not args.bw, verbose=args.verbose, quiet=args.quiet, title=source.basename, issuesMode=args.issues, contentMode=args.listing, summaryMode=args.summary, # styled=True, # width=120, # baseIndent=0, # displayLineNos=True, # lineNoPadding=' ', # verbose=0, # quiet=False, # # ------------------------ # title=None, # issuesMode='top', # # ------------------------ # contentMode='self', # self|source|model|no # summaryMode='top', # top | down | no ) Megamodel.displaySource(source=source, config=printer_config) if manySourceFiles: cprint('#' * 28 + ' END ' + filename + ' ' + '#' * 28 + '\n' * 2, 'blue') return None
def sourceMetamodel(self): #type: (MetamodelDependency) -> Metamodel """ Source metamodel ot ValueError if this metamodel is not registered yet (which should not happen). """ try: from modelscripts.megamodels import Megamodel return Megamodel.theMetamodel(id=self.sourceId) except: raise ValueError( 'No target "%s" metamodel registered from %s' % ( self.sourceId, self.targetId ))
def __init__(self, importingSourceFile, importedSourceFile): #type: (ModelSourceFile, ModelSourceFile) -> None """ Create the dependency and register it in the metamodel. """ self.importingSourceFile=importingSourceFile #type: ModelSourceFile """ Source file that contains the import """ self.importedSourceFile= importedSourceFile #type: ModelSourceFile """ Imported source file. The target of the import""" from modelscripts.megamodels import Megamodel Megamodel.registerSourceDependency(self) # register model dependency if not already there # (it should not be there) sm=self.importingSourceFile.model tm=self.importedSourceFile.model if Megamodel.modelDependency(sm, tm) is None: d=ModelDependency(sm, tm) Megamodel.registerModelDependency(d)
def __init__( self, id, label, extension, modelClass, modelKinds=('', ), sourceClass=None, modelPrinterClass=None, sourcePrinterClass=None, diagramPrinterClass=None, ): #type: (Text, Text, Text, Cls, OptCls, OptCls, OptCls, OptCls) -> None self.id = id self.label = label self.extension = extension self.modelKinds = modelKinds self._modelClass = modelClass self._sourceClass = sourceClass self._modelPrinterClass = modelPrinterClass self._sourcePrinterClass = sourcePrinterClass self._diagramPrinterClass = diagramPrinterClass from modelscripts.megamodels import Megamodel Megamodel.registerMetamodel(self)
def inDependencies(self, sourceMetamodel=None): #type: (Optional[Metamodel]) -> List[ModelDependency] """ Returns the dependencies towards from this dependency filtered either by targetMetamodel, or by metamodelDependency. """ from modelscripts.megamodels import Megamodel deps=Megamodel.modelDependencies(target=self) if sourceMetamodel is None: return deps return [ dep for dep in deps if (dep.sourceModel.metamodel == sourceMetamodel) ]
def __init__(self, importStmt): #type: (ImportStatement) -> None """ Create an SourceImport and actually perform the actuel import of target source file. If the target source file is not already registered in the megamodel then the target is read and a ModelSourceFile is therefore created. This possibly could generate some issues but these are stored in the target ModelSourceFile. The _issueBox of the importing ModelSourceFile is linked to the one of the target. This allows to source to "see" issues produced in the target. """ self.importStmt=importStmt #type: ImportStatement """ Link to the syntactic import statement """ self.importBox=None #type: Optional[ImportBox] """ Back reference to the containing import box """ # filled in ImportBox.addImport try: # already registered from modelscripts.megamodels import Megamodel importedSourceFile=Megamodel.source( importStmt.absoluteTargetFilename) except: # Not registered yet: # actually perform the import importedSourceFile=self._doImport(importStmt) super(SourceImport, self).__init__( self.importStmt.sourceFile, importedSourceFile ) self._doBindIssueBoxes() if self.importedSourceFile.issues.bigIssues: Issue( origin=self.importingSourceFile, level=Levels.Fatal, message= 'Serious issue(s) found when importing "%s"' % self.importedSourceFile.basename )
def checkDependencies(self, metamodelDependencies=None): #type: (List[MetamodelDependency])->None """ Check if this model has not problems with dependencies. Do nothing if this is not the case. Otherwise it is else raise a ValueError. This could be because there is no corresponding metamodel dependency for a model metamodel. This could also due because of too much outgoing dependency of the same typel. This could be because of missing dependency. """ #-- metamodels dependencies to be check against from modelscripts.megamodels import Megamodel if metamodelDependencies is None: mm_deps=Megamodel.metamodelDependencies( source=self.metamodel) else: mm_deps=metamodelDependencies #-- perform check for all metamodels dependencies for mm_dep in mm_deps: # all model dependencies of type mm_dep # starting from here m_deps=self.outDependencies( metamodelDependency=mm_dep) if len(m_deps)==0 and not mm_dep.optional: raise ValueError( 'Reference to a %s model' ' is model is missing' % mm_dep.targetMetamodel) elif len(m_deps)>=0 and not mm_dep.multiple: raise ValueError( 'Too many %s models associated' ' with this model' % mm_dep.targetMetamodel) else: pass
def incomingDependencies(self): #type: () -> List[SourceImport] return Megamodel.sourceDependencies(target=self)
sys.path.insert(0, modelscribes_home) # sys.path.append("/home/jmfavre/.config/gedit") # sys.path.append("/home/jmfavre/.local/share/gtksourceview-3.0/language-specs") #------------------------------------------------------ import modelscripts from modelscripts.megamodels import Megamodel def source(filename): return Megamodel.loadFile(filename) s = source M = Megamodel gl = Megamodel.theMetamodel(id='gl') cl = Megamodel.theMetamodel(id='cl') us = Megamodel.theMetamodel(id='us') ob = Megamodel.theMetamodel(id='ob') pe = Megamodel.theMetamodel(id='pe') sc = Megamodel.theMetamodel(id='sc') mg = Megamodel.theMetamodel(id='mg') print('ModelScripts Interpreter') print('========================') print("source('foo.cls') to load the file 'foo.cls'") print('quit() to quit this session') print('M to get the megamodel')
def source(filename): return Megamodel.loadFile(filename)
def incomingDependencies(self): from modelscripts.megamodels import Megamodel return Megamodel.metamodelDependencies(target=self)
def scriptsIterator(m2id, expectedIssues): metamodel = Megamodel.theMetamodel(id=m2id) res = checkFileIssues(metamodel.extension[1:], [metamodel.extension], expectedIssues)
def _matchModelImport(lineNo, modelSourceFile, justMatch=False): #Megamodel statements are always extracted from # RAW UNPROCESSED ORIGINAL source file. #type: (int, ModelSourceFile, bool) -> Optional[Union[bool,ImportStatement]] """ Check if the line is an import statement. If justMatch just indicates if the line is recognized. In that case it returns True otherwise None. If not justMatch build an ImportStatement (if possible) Return None if this is not the case. Otherwise return a ModelImportStatement if the import is valid. Otherwise raise a fatal error that go in the issue box. Import statements looks like this import usecase model x.cs import glossary model a/b/../c.gls """ re_stmt = (r'^ *(?P<import>import)' + r' +(?P<metamodelLabel>\w+)' + r' +model' + r' +from' + r' +\'(?P<target>[\w\./\-]+)\' *$') line = modelSourceFile.realSourceLines[lineNo - 1] m = re.match(re_stmt, line, re.MULTILINE) if m is None: return None else: if justMatch: return True else: source_metamodel = modelSourceFile.metamodel # get actual metamodel metamodel_label = m.group('metamodelLabel') try: # could raise ValueError from modelscripts.megamodels import Megamodel metamodel = Megamodel.theMetamodel( label=metamodel_label) #type: Metamodel except ValueError as e: LocalizedSourceIssue( sourceFile=modelSourceFile, line=lineNo, level=Levels.Fatal, # could be error with some work message=str(e)) # Check that metamodel dependency is allowed target_mms = source_metamodel.outMetamodels # noinspection PyUnboundLocalVariable if metamodel not in target_mms: LocalizedSourceIssue( sourceFile=modelSourceFile, line=lineNo, level=Levels.Fatal, # could be error with some work message=('A %s model cannot reference a %s model.' % (source_metamodel.label, metamodel.label))) # Check path literal_target_filename = m.group('target') abs_target_filename = os.path.abspath( os.path.join(modelSourceFile.directory, literal_target_filename)) file_extension = os.path.splitext(abs_target_filename)[1] if file_extension != metamodel.extension: LocalizedSourceIssue( sourceFile=modelSourceFile, line=lineNo, level=Levels.Fatal, # could be error with some work message=('The extension of the file must be "%s".' % (metamodel.extension))) if not os.path.isfile(abs_target_filename): LocalizedSourceIssue( sourceFile=modelSourceFile, line=lineNo, level=Levels.Fatal, # could be error with some work message=('File not found: %s' % literal_target_filename)) return ImportStatement( lineNo=lineNo, sourceFile=modelSourceFile, metamodel=metamodel, absoluteTargetFilename=abs_target_filename, literalTargetFileName=literal_target_filename)
def finishMegamodel(): from modelscripts.megamodels import (Megamodel, METAMODEL) Megamodel.model = Megamodel() Megamodel.model.name = 'megamodel' from modelscripts.metamodels.megamodels import _setMetaModel _setMetaModel(METAMODEL)
def __init__(self, parents=()): #type: (List[IssueBox]) -> None super(WithIssueModel, self).__init__(parents=parents) from modelscripts.megamodels import Megamodel Megamodel.registerIssueBox(self._issueBox)
def _matchModelDefinition( lineNo, modelSourceFile, justMatch=False, # prefixRegexp=r' *(--)? *@?', noSymbolChecking=False, recognizeUSEOCLNativeModelDefinition=False): # type: (int, ModelSourceFile, bool, bool, bool) -> Optional[Union[bool,DefinitionStatement]] """ Check if the line is a model definition statement. If justMatch just indicates if the line is recognized. In that case it returns True otherwise None. If not justMatch build an ImportStatement (if possible) Return None if this is not the case. Return a ModelDefinitionStatement if the stmt is valid. Otherwise raise a value error. Definition statements looks like this preliminary usecase model MyModel class model MyModel If 'recognizeUSEOCLNativeModelDefinition' then the line "model <NAME>" is accepted as the model definition in USE OCL files. This is a patch good enough for now for .use file. Later the .use file could be generated and the syntax improved. """ def _parseStandardSyntax(): re_stmt = (r'^ *((?P<modelKind>\w+) +)?' + r'(?P<metamodelLabel>\w+)' + r' +model' + r' +(?P<name>\w+) *$') line = modelSourceFile.realSourceLines[lineNo - 1] m = re.match(re_stmt, line, re.MULTILINE) if m is None: return None else: if m.group('modelKind') == 'import': # because overlapping regexp return None if justMatch: return True else: return { 'modelKind': m.group('modelKind'), 'metamodelLabel': m.group('metamodelLabel'), 'name': m.group('name') } def _parseUSEOCLSyntax(): re_stmt = ('^' + r' *model' + r' +(?P<name>\w+) *$') line = modelSourceFile.realSourceLines[lineNo - 1] m = re.match(re_stmt, line, re.MULTILINE) if m is None: return None if justMatch: return True else: return { 'modelKind': '', 'metamodelLabel': 'class', 'name': m.group('name') } def _parse(): r1 = _parseStandardSyntax() if r1 is not None or not recognizeUSEOCLNativeModelDefinition: return r1 else: # No match so search USEOCL return _parseUSEOCLSyntax() m = _parse() if m in [True, None]: return m else: source_metamodel = modelSourceFile.metamodel # get actual metamodel metamodel_label = m['metamodelLabel'] try: # could raise ValueError from modelscripts.megamodels import Megamodel metamodel = Megamodel.theMetamodel( label=metamodel_label) #type: Metamodel except ValueError as e: LocalizedSourceIssue( sourceFile=modelSourceFile, line=lineNo, level=Levels.Fatal, # could be error with some work message=str(e)) # check model_kind model_kind = ('' if m['modelKind'] is None else m['modelKind']) # noinspection PyUnboundLocalVariable if model_kind not in metamodel.modelKinds: LocalizedSourceIssue( sourceFile=modelSourceFile, line=lineNo, level=Levels.Fatal, # could be error with some work message=( '%s models can\'t be "%s".' ' Choose one of %s.' % (metamodel_label, model_kind, str(metamodel.modelKinds)))) # Check that the metamodel is the expected one # if metamodel != source_metamodel: # LocalizedSourceIssue( # sourceFile=modelSourceFile, # line=lineNo, # level=Levels.Fatal, # could be error with some work # message=( # 'A %s model cannot be defined in a "%s" file.' % ( # metamodel.label, # source_metamodel.extension, # ))) # Check name name = m['name'] if not noSymbolChecking and not (Symbol.is_CamlCase(name)): LocalizedSourceIssue( sourceFile=modelSourceFile, line=lineNo, level=Levels.Error, message=('Invalid model name "%s". It must be in CamlCases.' % (name))) if name.lower() != modelSourceFile.name.lower(): if not recognizeUSEOCLNativeModelDefinition: LocalizedSourceIssue( sourceFile=modelSourceFile, line=lineNo, level=Levels.Error, message=( 'Model must be named %s according to the name of the file' % ( # modelSourceFile.fileName, modelSourceFile.name))) return DefinitionStatement(lineNo=lineNo, sourceFile=modelSourceFile, modelKind=model_kind, metamodel=metamodel, name=name)
def __init__( self, fileName, realFileName=None, prequelFileName=None, preErrorMessages=(), #TODO: check the type readFileLater=False, fillImportBoxLater=False, parseFileLater=False, noSymbolChecking=False, allowedFeatures=(), recognizeUSEOCLNativeModelDefinition=False): #type: (Text, Optional[Text], Optional[Text], List[Any], bool, bool, bool, bool, List[Text], bool) -> None """ An empty model is created automatically and it is associated with this source file. This empty model is created according to the metamodel specified by the property 'metamodel'. An importBox is also created. In order to do this the content of the source file is parsed looking the declaration of the model name as well as import statements. All this information is stored in the importBox. If `fillImportBoxLater` is True then this importBox is left empty for the moment. The client have to call explicitly parseToFillImportBox() later. The parameter recognizeUSEOCLNativeModelDefinition is a patch to allow parsing regular .use file. Args: fileName: The logical name of the file. This is not necessarily the file parsed. realFileName: The real file to be read. If file reading has to be postponed, then the parameter should be set to None. The doRealFileRead() will set the filed realFileName. preErrorMessages: The errors in this list will be added. readFileLater: If False the file is read directly. Otherwise the method doReadFile() must be called! fillImportBoxLater: If False, the file read is parsed to find megamodel statements (e.g. import) and to fill the import box. If not parseToFillImportBox() must be called after reading the file, and this in order to get the imported model. allowedFeatures The list of features allowed. This depends on each parser. This allows to remove the use of some features during parsing. For instance to forbid the use of association classes. """ # if readFileLater or fillImportBoxLater or parseFileLater: # assert finalizeLater # Create an empty model # Not to be moved after super # This should be done in all case so that # the model attribute always exist even if there # are some error in reading the file self.model = self.emptyModel() # type: Model # Call the super class, read the file or not try: # This can raise an exception for instance if # there is a problem reading the file super(ModelSourceFile, self).__init__(fileName=fileName, realFileName=realFileName, prequelFileName=prequelFileName, preErrorMessages=preErrorMessages, doNotReadFiles=readFileLater, allowedFeatures=allowedFeatures) except FatalError: pass # an error as already been registered from modelscripts.megamodels import Megamodel Megamodel.registerSource(self) Megamodel.registerModel(self.model) # Backward link self.model.source = self # Link issue box self.model._issueBox.addParent(self._issueBox) # Source to ModelElement Mapping self._modelMapping = _ModelSourceMapping() # Create first an empty ImportBox. self.importBox = ImportBox(self) # Then fill it by reading megamodel statements, # unless specified. try: if not fillImportBoxLater: parseToFillImportBox(self, noSymbolChecking, recognizeUSEOCLNativeModelDefinition) if not parseFileLater: self.parseToFillModel() self.finalize() except FatalError: pass # nothing to do, the issue has been registered