# __ File: ATOM3.py __________________________________________________________________________________________________ # Implements : class ATOM3 # Author : Juan de Lara # Description : This is the ATOM3 kernel. # Modified : 20 July 2002 # Changes : # - 14 May 2002 : Several changes to the GUI. Added operations toolbar and graphics toolbar. Rearranged the GUI's layout. (EPP) # - 23 Oct 2001 : Fixed bug, when closing a Meta-Model via the User Interface. # - 23 Oct 2001 : Fixed Bug, when trying to generate code from a regular model, it # gave an exception. # - 25 Oct 2001 : Fixed bug, when trying to re-load a model, modified in the same session, # it loaded the old version. Now recompilation is enforced (method open). Same # thing done in executeTrans, # - 25 Oct 2001 : Fixed bug, related to the possibility of scrolling, must convert all graphical events to canvas # coordinates, by means of .canvasx() and .canvasy() # - 26 Oct 2001 : Fixed bug, added an import of StatusBar to the generation of meta-models # - 14 Nov 2001 : Corrected code generation (for Meta-Models) for entities, method postCondition, it writed the # "or " in the conditions without a space before. The bug was in method writeACtionConstraint (copied from previous version of ATOM3) # - 14 Nov 2001 : When no meta-model is left (we've closed all), sets ASGroot to None (method removeMetaModel) # In openMetaModel, before merging, we have to check if ASGroot is None. (copied from old version of ATOM3) # - 14 Nov 2001 : in the editclass method, removed the invocation of canvasx and canvasy (this conversion is done in editEntity) (copied from old version of ATOM3). # - 14 Nov 2001 : added condition "if obj.semanticObject.keyword_:" in self.editclass, may be the # generated entity does not have a keyword. (copied from old version of ATOM3) # - 14 Nov 2001 : in writeCreation: added code to generate the keyword. (copied from old version of ATOM3). # - 14 Nov 2001 : in genButtons, added code to generate rows of 4 buttons. (copied from old version of ATOM3). # - 14 Nov 2001 : Modified the code generated for postconditions, added the parameter param[0] to all constraints, # This is useful as in pre and post conditions on connect this parameter has a string indicating if the entity # is the source or destination of a connection operation. Modified methods: writeActionConstraint and writeConstraintCode. Copied from old version of ATOM3 # - 14 Nov 2001 : DISCONNECT Event was not being raised. Created method getConnectedEntities, deleteRealEntity and deleteConnection and modified # method: deleteEntity. Copied from old version of ATOM3. # - 19 Dec 2001 : Added the following line in the genASGNodeCode: f.write(' ASGNode.GGcheckSetNone(self)\n'). This code is included in # the invalid() function in nodes and may mark the widgets as None before checking if they are valids. # - 9 July 2002: slightly modified the file selection in genPostscript(), to reflect the fact that we usually want to produce Encapsulated # Postscript (for inclusion into reports, articles, ...) # - 10 July 2002: Replaced all calls to canvas' find_closest with AToM3's find_visibe_closest, which only considers # visible objects. # - 15 July 2002: Added possibility to save the actual graphical representation of objects # - 16 July 2002: Changed the code generation function (genASGNodeCode), now the functions toString(), show() etc # are on the ASGNode class and the operations are performed by iterating on the generatedAttributes dictionary # and the realOrder list. # - 20 July 2002: Added a button to copy the LHS from the RHS in a Graph Grammar's rule. For this purpose, the functions # addCopyFromLHSButton and copyFromLHS have been added. # - 22 July 2002: Added a binding Alt-X to exit, and rearrangement of menus # - 22 July 2002: Modified methods reDrawGGLabels and editclass to show in attributes in RHS and # when these attributes are copied or calculated by a piece of Python code. # ____________________________________________________________________________________________________________________ import sys # make sure we can access some necessary directories (contain the source code!) for spath in ['ATOM3Types', 'TypeModels', 'GraphGrammar', 'UserInterface', 'ERModels', 'ButtonsModels']: if not spath in sys.path: sys.path.append(spath) from Tkinter import * #from graphLink import graphLink from graphEntity import graphEntity from PostscriptOptions import PostscriptOptions from ASGNode import * from ASG import * from ATOM3TypeDialog import * from ATOM3List import * from ATOM3TypeInfo import * from Options import * from GGrule import * from GraphGrammarEdit import * from GraphRewritingSys import * from StatusBar import * from Console import * from DebugConsole import * from GrammarExecution import * from TypeCodeGen import * from Buttons import * from createButtons import * import tkFileDialog import os.path import os import string class ATOM3(Frame): # Constants that define the operations' mode IDLEMODE = "IDLEMODE" EDITstate = "EDITstate" CONNECTstate = "CONNECTstate" EXPANDModel = "EXPANDModel" INSERTModel = "INSERTModel" DELETEstate = "DELETEstate" SELECTgraph = "SELECTgraph" SMOOTHconnection = "SMOOTHconnection" DELETEpoint = "DELETEpoint" INSERTpoint = "INSERTpoint" CHANGEconnector = "CHANGEconnector" fillAttributes = ['line', 'text'] # Attributes for whom we use 'fill' def __init__(self, parent, GUIModelName=None, isMainKernel = 0, genGraphics = 1, ASGroot = None, editGGLabels = 0): Frame.__init__(self, parent) # call the Frame constructor parent.geometry("+%d+%d"%(0,0)) self.parent = parent self.toolBar = Frame(self.parent) # create a Toolbar (implemented as a Frame) self.opBar = Frame(self.parent) self.isOpBarPresent = 0 self.grphBar = Frame(self.parent) self.isGrphBarPresent = 0 self.editGGLabel = editGGLabels # store if we are editing a Graph Grammar Rule self.ASGroot = ASGroot self.userActionsMap = {} # mapping between user actions and functions self.types = {} # dictionary to store the types and handling functions self.newTypes = [] # list with the newly added types self.IsMainKernel = isMainKernel # Wether I'm the main Kernel Window or Not. self.option=None self.codeGenDir = "work" self.coupledGG = None # info (object of type GraphGrammarExecution) with the graph-grammars to be executed on the model self.console = None self.entitiesInMetaModel = {} # dictionary with the entities defined by each meta-model. self.GUIModelName = None # name of the GUI Model currently in use self.metaModelName = None # name of the meta-model self.modes = {} # dictionary in which the keys are the buttons, and the contents are the modes if self.IsMainKernel: exec "from MyOptions import *\n" in self.__dict__, self.__dict__ if not GUIModelName: # no metamodel specified, see default self.GUIModelName = self.option.InitialMetaModel.getValue() else: # override the options self.GUIModelName = GUIModelName self.loadOptions() self.console = Console(self) # Create a console to show some messages self.debugConsole = DebugConsole(self) self.console.appendText("Initializing AToM3 with GUI: "+self.GUIModelName) # put the message in the console else: exec "from MyOptions import *\n" in self.__dict__, self.__dict__ self.loadOptions() self.GUIModelName = GUIModelName # meta model name currently in use self.openMetaModels = ATOM3List([0,0,1,0], ATOM3String) # list with the names of the open metamodels self.metaModelFileNames = [] # list with the file names of the open metamodels self.ConnectivityMap = {} # A dictionary to store how to connect entities. self.CardinalityTable= {} # A table to store the cardinalities... self.buttonList = [] # A list of buttons. if self.GUIModelName: # if a metamodel must be loaded... if not self.ASGroot: self.openMetaModel(self.GUIModelName, 0, 1) # create a new ASGroot if we do not have one else: self.openMetaModel(self.GUIModelName, 0, 0) # do not create a new ASGroot if we have one. self.toolBar.pack(side=LEFT, fill=Y, expand = 0) # Make toolbar visible self.addButtons2OpBar() self.addButtons2GrphBar() self.opBar.pack(side=TOP, fill=X, expand=0) self.grphBar.pack(side=TOP, fill=X, expand=0) # Canvas panel definition begins here canvasPanel= Frame(self.parent, name = "canvaspanel")#, width=800, height=500) self.UMLmodel = Canvas(canvasPanel, name = "modelcanvas", borderwidth=5, scrollregion=(0,0,2500,2500), relief=RIDGE, bg = 'white', width=800, height=500) # Create the modelling zone, width=700, height=500, # scrollbars for drawarea bottom_scrollbar = Frame(canvasPanel) self.UMLmodel.scrollX = Scrollbar(bottom_scrollbar, orient=HORIZONTAL) self.UMLmodel.scrollY = Scrollbar(canvasPanel, orient=VERTICAL) # link canvas, scrollbars, and events self.UMLmodel['xscrollcommand'] = self.UMLmodel.scrollX.set self.UMLmodel['yscrollcommand'] = self.UMLmodel.scrollY.set self.UMLmodel.scrollX['command'] = self.UMLmodel.xview self.UMLmodel.scrollY['command'] = self.UMLmodel.yview self.UMLmodel.square = Canvas(bottom_scrollbar, bg='lightgrey', width=20, height=20) self.UMLmodel.scrollX.pack(side=LEFT, fill = X, expand = 1) self.UMLmodel.square.pack(side=RIGHT) bottom_scrollbar.pack(side=BOTTOM, fill = X, expand = 0) self.UMLmodel.pack(side=LEFT, fill = BOTH, expand=1) self.UMLmodel.scrollY.pack(side=LEFT, fill = Y, expand = 0) self.statusbar = StatusBar(parent) canvasPanel.pack(side=TOP, fill=BOTH, expand=1) self.statusbar.pack(side = TOP, fill = X, expand = 0) self.UMLmodel.bind("", self.buttonPressed ) # bind mouseClicked self.UMLmodel.bind("", self.mouseMove ) # bind mouseMove self.UMLmodel.bind("", self.drop ) # bind release button self.UMLmodel.bind("", self.rbuttonPressed ) # bind right-mouseClicked # Canvas panel definition ends here self.parent.protocol("WM_DELETE_WINDOW", self.exitFromATOM3) self.createMenu() # Creates a topLevel menu from ATOM3TypeInfo import ATOM3TypeInfo self.typeList = ATOM3List([1,1,1,0], ATOM3TypeInfo, self ) if self.metaModelName: # if a metamodel must be loaded... try: self.fillTypesInformation(self) # fill the types list self.fillDictionaryWithTypes() # Convert list into a dictionary except AttributeError: print "File "+self.metaModelName+" is not a valid meta-model... Aborting" self.quit() sys.exit(1) self.mode = self.IDLEMODE self.initDragX = 0 # initial x value for drag operations self.initDragY = 0 # initial y value for drag operations self.isMousePressed = 0 # if the left mouse is pressed self.hasMoved = 0 # if we are in the middle of a drag-drop operation self.fromClass = None # initial class in a connection operation self.toClass = None # second class in a connection operation #self.globalConstraintsDict = {} # global constraints dictionary self.sem_objFrom = None # For connecting objects self.sem_objTo = None # For connecting objects self.EditingGraphGrammar = None # Graph Grammar being edited currently self.theKeyword = None self.inter_connect_points = [] # list of intermediate connecting points if self.ASGroot and not isMainKernel: # open the necessary metamodels (look over the merged ASGs)! mergeds = []+self.ASGroot.mergedASG for asg_merged in mergeds: self.openMetaModel(asg_merged.metaModelName, 0, 0) self.ASGroot.writeContents(self, genGraphics) # draw contents of ASGroot if any def configureUserActions(self): """ Fills the common actions for all formalisms... """ self.userActionsMap[self.IDLEMODE] = self.drag self.userActionsMap[self.CONNECTstate] = self.connectClass self.userActionsMap[self.INSERTModel] = self.createNew_Model self.userActionsMap[self.EXPANDModel] = self.expandModel self.userActionsMap[self.EDITstate] = self.editEntity self.userActionsMap[self.DELETEstate] = self.deleteEntity self.userActionsMap[self.SELECTgraph] = self.selectGraph self.userActionsMap[self.SMOOTHconnection] = self.doSmooth self.userActionsMap[self.DELETEpoint] = self.doDeletePoint self.userActionsMap[self.INSERTpoint] = self.doInsertPointInConnection self.userActionsMap[self.CHANGEconnector] = self.doChangeConnector def loadOptions(self): "Loads the options into class attributes" # add to search path the default directory ! if not self.option.codeGenDir.toString() in sys.path: sys.path.append(self.option.codeGenDir.toString()) # add to search path the indicated directories ! paths = self.option.PathDirectories.getValue() for direct in paths: if not direct in sys.path: sys.path.append(direct.toString()) self.codeGenDir = self.option.codeGenDir.toString() self.genGraphics = 1-self.option.GenerateGraphics.getValue()[1] # def loadFormalism(self, fileName): #""" # Imports the contents of 'fileName' #""" #print "in ATOM3::loadFormalism" #dir, file = os.path.split(fileName) # separate directory name and file name #className = string.split (file, ".") # compose class name #exec "from "+className[0]+" import *\n" in self.__dict__, self.__dict__ #print "done!" def fillDictionaryWithTypes(self): "Given an ATOM3List of ATOM3TypeInfo, extracts each object and fills a dictionary" objList = self.typeList.getValue() # retrieve the list of ATOM3TypeList's for obj in objList: name, strClassName, strParams, mayEditModel = obj.getValue() # (Name, className, (param1, param2,...)) realParams = [] # Form the list of parameters for param in strParams: # for each ATOM3String parameter stringParam = param.toString() rp = eval(stringParam) # de-Stringize realParams.append(rp) # append to the list # first import the library exec "from "+strClassName+" import *\n" self.types[name] = ( eval(strClassName), tuple(realParams) ) # for the dictionary entry def editMode(self): "enters in EDIT mode" self.mode = self.EDITstate def connMode(self): "enters in connection mode" self.mode = self.CONNECTstate def expandModel(self, unUsed, wx, wy): "Opens a new ATOM3 instance to edit the model components" x = self.UMLmodel.canvasx (wx) # convert to canvas coordinate system y = self.UMLmodel.canvasy (wy) ct = self.find_visible_closest(x, y, self.UMLmodel) tags = self.UMLmodel.gettags(ct) if tags[1][:4] == "ASG_": tags = self.UMLmodel.gettags(ct) if tags: obj = VisualObj.Tag2ObjMap[tags[0]] ma = ATOM3TypeDialog(self, obj.semanticObject, ATOM3TypeDialog.OPEN ) # edit types... self.mode=self.IDLEMODE def editEntity(self, unUsed, wx, wy): "check the type of entity that we have to edit and do it" x = self.UMLmodel.canvasx (wx) # convert to canvas coordinate system y = self.UMLmodel.canvasy (wy) ct = self.find_visible_closest(x, y, self.UMLmodel) tags = self.UMLmodel.gettags(ct) if tags and tags[0] != 'current': # i.e. we don't have a connection self.editclass( x, y) self.mode=self.IDLEMODE def createMenu(self): # creates a top level menu "creates a toplevel menu" self.mmtoolMenu = Menu( self ) # ....................................................................................... # create a File pulldown menu, and add it to the menu bar # ....................................................................................... filemenu = Menu(self.mmtoolMenu, tearoff=0) filemenu.add_command(label="Open model", command= self.open, underline=0) filemenu.add_command(label="Clear model", command= self.clearModel, underline=0) filemenu.add_command(label="Save model", underline = 0, command= lambda x=self: x.save(0, x.statusbar.getState(StatusBar.MODEL)[1][0])) filemenu.add_command(label="Save as...", underline = 5, command= lambda x=self: x.save(0)) filemenu.add_command(label="Export model", underline = 0, command= lambda x=self: x.save(1)) filemenu.add_command(label="Generate PostScript from model", command= self.genPostscript, underline = 9) filemenu.add_separator() filemenu.add_command(label="Open meta-model", command= self.openMetaModel, underline = 5) filemenu.add_command(label="Close meta-model", command= self.closeMetaModel, underline = 1) if self.IsMainKernel: # only generate this if we are a main window... filemenu.add_separator() filemenu.add_command(label="Show console", command= self.showConsole, underline = 1) filemenu.add_command(label="Options", command= self.options, underline = 0) filemenu.add_command(label="Exit", command=self.exitFromATOM3, underline = 1, accelerator="Alt-X") self.parent.bind('', self.exitFromATOM3) self.parent.bind('', self.exitFromATOM3) self.mmtoolMenu.add_cascade(label="File", menu=filemenu, underline=0) # ....................................................................................... # create a Model pulldown menu, and add it to the menu bar, as the entities to be created # varies from model to model, this is done in the loaded meta-model # ....................................................................................... self.modelMenu = Menu(self.mmtoolMenu, tearoff=0) self.createModelMenu(self, self.modelMenu) # implemented in the meta-model self.modelMenu.add_separator() self.modelMenu.add_command(label="Edit entity", command=self.editMode, underline = 0 ) self.modelMenu.add_command(label="Connect", command=self.connMode, underline = 0 ) self.modelMenu.add_command(label="Delete", command=self.deleteMode, underline = 0 ) self.modelMenu.add_separator() self.modelMenu.add_command(label="Insert model", command=self.newModeModel, underline = 0 ) self.modelMenu.add_command(label="Expand model", command=self.expandModeModel, underline = 1 ) self.modelMenu.add_separator() self.modelMenu.add_command(label="Edit model attributes", command=self.modelAttributes, underline = 11 ) self.modelMenu.add_command(label="Edit types", command=self.editTypes, underline = 5 ) if self.IsMainKernel: self.modelMenu.add_separator() self.modelMenu.add_command(label="Generate code", command=self.genCode, underline = 0 ) self.mmtoolMenu.add_cascade(label="Model", menu=self.modelMenu, underline = 0) # ....................................................................................... # create the Transformation Menu... (only if we are in the main window) # ....................................................................................... if self.IsMainKernel: transMenu = Menu(self.mmtoolMenu, tearoff = 0) transMenu.add_command(label="Load transformation", command = self.loadTrans, underline = 0 ) transMenu.add_command(label="Save transformation", command = self.saveTrans, underline = 0 ) transMenu.add_command(label="Edit transformation", command = self.editTrans, underline = 0 ) transMenu.add_command(label="Create transformation", command = self.createTrans, underline = 0 ) transMenu.add_separator() transMenu.add_command(label="Generate code for transformation", command = self.genCode4Trans, underline = 0 ) transMenu.add_command(label="Execute transformation", command = self.executeTrans, underline = 1 ) self.mmtoolMenu.add_cascade(label="Transformation", menu=transMenu, underline = 0) # ....................................................................................... # create the Graphics Menu... # ....................................................................................... graphMenu = Menu(self.mmtoolMenu, tearoff = 0) graphMenu.add_command(label="Smooth connection", command = self.smoothConnection ,underline = 0 ) graphMenu.add_command(label="Insert point in connection", command = self.insertPointInConnection,underline = 0 ) graphMenu.add_command(label="Delete point from connection", command = self.deletePointFromConnection,underline = 0 ) graphMenu.add_command(label="Change connection from connector", command = self.changeConnector,underline = 0 ) self.mmtoolMenu.add_cascade(label="Graphics", menu=graphMenu, underline=0) self.parent.config(menu=self.mmtoolMenu) def changeConnector(self): """ enters in the CHANGEconnector mode """ self.mode = self.CHANGEconnector self.connX, self.connY, self.connHandler, self.obj_connection = None, None, None, None def find_closest_connector(self, x, y): """ Finds the closest connctor to the coordinate (x, y). Returns the handle of this connector or -1 if there is not any. """ import math connectors = self.UMLmodel.find_withtag("connector") # get tuple with all the connectors maxDistance, minconnector = 10000, -1 for connector in connectors: # iterate to look for the closest coords = self.UMLmodel.coords(connector) # get an n-tuple with the coordinates of connector distance = math.sqrt((x-coords[0])*(x-coords[0])+(y-coords[1])*(y-coords[1])) # get the distance to 2 first coordinates (connectors are just a point) if distance < maxDistance: maxDistance = distance minconnector = connector return minconnector def doChangeConnector(self, UnUsed, wx, wy): """ changes a connector point """ if (self.connX, self.connY, self.connHandler, self.obj_connection) == (None, None, None, None) : # it is the first time self.connX, self.connY = self.UMLmodel.canvasx(wx), self.UMLmodel.canvasy(wy) # convert to canvas coordinate system self.connHandler = self.find_visible_closest(self.connX, self.connY, self.UMLmodel) # find the closest items if type(self.connHandler) == TupleType: self.connHandler = self.connHandler[0] tags = self.UMLmodel.gettags(self.connHandler) self.obj_connection = VisualObj.Tag2ObjMap[tags[0]] # check that it is a connection... if (string.find(tags[0], "1stSeg") > -1 or string.find(tags[0], "2ndSeg") > -1) and self.UMLmodel.type(self.connHandler) == "line": pass else: self.connX, self.connY, self.connHandler, self.obj_connection = None, None, None, None else: x, y = self.UMLmodel.canvasx(wx), self.UMLmodel.canvasy(wy) # convert to canvas coordinate system # now get the nearest connector connector = self.find_closest_connector(x, y) if type(connector) == TupleType: connector = connector[0] self.obj_connection.changeConnector (self.connHandler, connector) self.mode = self.IDLEMODE def deletePointFromConnection(self): """ enters in the DELETEpoint mode """ self.mode = self.DELETEpoint def doDeletePoint (self, UnUsed, wx, wy): "deletes the nearest point of a segment link" x = self.UMLmodel.canvasx(wx) # converto to canvas coordinate system y = self.UMLmodel.canvasy(wy) ct = self.find_visible_closest(x, y, self.UMLmodel) # find the closest items tags = self.UMLmodel.gettags(ct) # get the tags if tags and tags[0] != 'current': obj = VisualObj.Tag2ObjMap[tags[0]] obj.deletePointFromConnection( tags[0], ct[0], x, y ) # select it self.mode = self.IDLEMODE def insertPointInConnection(self): """ ENters in the INSERTpoint mode """ self.mode = self.INSERTpoint def doInsertPointInConnection(self, UnUsed, wx, wy): """ inserts a point in a connection """ x = self.UMLmodel.canvasx(wx) # converto to canvas coordinate system y = self.UMLmodel.canvasy(wy) ct = self.find_visible_closest(x, y, self.UMLmodel) # find the closest items print ct tags = self.UMLmodel.gettags(ct) # get the tags if tags and tags[0] != 'current': obj = VisualObj.Tag2ObjMap[tags[0]] if graphLink in obj.__class__.__bases__: # JL, 9 July obj.insertIntermediatePoint( tags[0], ct[0], x, y ) # select it self.mode = self.IDLEMODE def smoothConnection(self): """ Enters in smooth mode """ self.mode = self.SMOOTHconnection def doSmooth(self, unused, wx, wy): """ Smooths or de-smooths a connection """ x, y = self.UMLmodel.canvasx (wx), self.UMLmodel.canvasy (wy) # convert to canvas coordinate system ct = self.find_visible_closest(x, y, self.UMLmodel) tags = self.UMLmodel.gettags(ct) if tags and tags[0] != 'current': obj = VisualObj.Tag2ObjMap[tags[0]] obj.smooth(ct[0]) self.mode = self.IDLEMODE def showConsole(self): """ Shows the console, if it is hidden """ self.console.showWindow() def closeUnusedMetaModels(self): """ Closes the meta-models which do not have entities in the current model. """ MetaModels2Leave = [] # List of meta-models that we will not erase # Search for each entity of each meta-model, to see if there is any instance, in that case, we cannot erase the mm. for mm in self.entitiesInMetaModel.keys(): # for each opened metamodel delete = 1 for entity in self.entitiesInMetaModel[mm]: # for each entity of that meta-model if self.ASGroot.listNodes[entity] != []: # if we do not have any... MetaModels2Leave.append(mm) # then we should not erase that metamodel break trueNames = [] # now get the name of the GUI Model that corresponds to this meta-model for mm in MetaModels2Leave: for mminfo in self.buttonList: # look in buttonList, becasue we have tuples (, , , ...) if type(mminfo) == TupleType: # Some other elements of this list are not tuples. mmFile = mminfo[2] # get the filename dir, fileName = os.path.split(mmFile) mmName = fileName[:len(fileName)-6] # erase the trailing "_MM.py" if mmName == mm: trueNames.append(ATOM3String(mminfo[1])) break numMM = len(self.openMetaModels.getValue()) # now remove the meta-models, calling self.removeMetaModels self.openMetaModels.setValue(trueNames) self.removeMetaModels(numMM) self.putMetaModelName() def closeMetaModel(self): "Presents a window that shows the open metamodels, allow to check some of them to be deleted" numMetaModels = len(self.openMetaModels.getValue()) cm = ATOM3TypeDialog(self, self.openMetaModels) # Select the meta-model to delete if cm.result_ok: # The user pressed Ok self.removeMetaModels(numMetaModels) self.putMetaModelName() def putMetaModelName(self): """ Updates the name of the current meta model and presents it in the Window´s title bar """ mmodels = self.openMetaModels.getValue() name = "" if len(mmodels) > 0: counter = 0 for mm in mmodels: if counter == 0: name = name + mm.toString() else: name = name + " + " + mm.toString() counter = counter + 1 if name == "": self.parent.title("AToM3 v0.2.1") else: self.parent.title("AToM3 v0.2.1 using: "+name) def removeMetaModels(self, numMetaModels): """ Closes one or more of the loaded metamodels (leaves only the present in self.openMetaModels). - numMetaModels: is the number of meta-models currently loaded """ omm = self.openMetaModels.getValue() # obtain the list of remaining meta-models somm = [] # list with the meta-models' names for openmm in omm: # for each ATOM3String... somm.append(openmm.toString()) # append its value to somm index, erased = 0, 0 while ( index < numMetaModels-erased ): # The elements of buttonList are tuples ( , , , , ...) mm = self.buttonList[index][1] # get the GUI name trueMM = self.buttonList[index][2] # get the name of the file where the meta-model is stored dir, fileName = os.path.split(trueMM) mmName = fileName[:len(fileName)-6] # that must be the name of the meta-model stored in entitiesInMetaModel if not mm in somm: # It is not in the list, so we have erased it frame2delete = self.buttonList[index][0] frame2delete.pack_forget() # erase panel from User Interface # Delete the 'modes' of the buttons that we are to delete... for idx in range(3, len(self.buttonList[index])): # iterate on the buttons of the meta-model button2delete = self.buttonList[index][idx] # get the idx-button of the mm if button2delete in self.modes.keys(): # check if the button has an associated mode mode2delete = self.modes[button2delete] del self.userActionsMap[mode2delete] # delete the associated action to that mode del self.modes[button2delete] # delete that mode erased = erased + 1 # increment the counter of erased metamodels self.buttonList.remove(self.buttonList[index]) # remove element from the List if self.console: self.console.appendText('Closing Meta-Model '+mm) if mmName in self.entitiesInMetaModel.keys(): for entity in self.entitiesInMetaModel[mmName]: # for each entity defined in the meta-model del self.CardinalityTable[entity] # delete also the info in CardinalityTable if entity in self.ConnectivityMap.keys(): del self.ConnectivityMap[entity] del self.entitiesInMetaModel[mmName] exec "from ASG_"+mmName+" import ASG_"+mmName+"\n" anASG = eval("ASG_"+mmName+"(self)") result = self.ASGroot.unMerge(anASG) if type(result) != IntType: self.ASGroot = result else: index = index+1 if self.openMetaModels.getValue() == []: # no meta-models are left self.ASGroot.removeContents(self, 1) # clear contents (if any) self.statusbar.event(StatusBar.MODEL, StatusBar.CLEAR, "Nonamed") if self.console: self.console.appendText('Clearing model') self.ASGroot = None # empty the cardinality tables and the connectivity map. self.CardinalityTable = {} self.ConnectivityMap = {} def loadGUImodel(self, file): """ Loads a model of the GUI in the 'Buttons' formalism. Returns this graph. """ oldGraphics = self.genGraphics self.genGraphics = 0 # disble graphics for a while... if file in sys.modules.keys(): # file has already been loaded del sys.modules[file] exec "from "+file+" import *\n" in self.__dict__, self.__dict__ if "loadedMMName" in self.__dict__.keys(): # if we have the meta-model name if self.loadedMMName != "Buttons": # This should be a 'Buttons' model tkMessageBox.showerror( "Couldn't open Formalism!", "Selected file "+self.metaModelName+" does not contain a valid formalism", parent = self ) return # create a 'Buttons' root node buttonsRoot = ASG_Buttons(self) del self.loadedMMName if "loadedTypes" in self.__dict__.keys(): # look for newly defined or loaded types (loadedTypes should be a list) self.genGraphics = 1 self.loadTypes(self.loadedTypes) # load the new types... self.genGraphics = 0 del self.loadedTypes try: self.newfunction(self, buttonsRoot) except TypeError: tkMessageBox.showerror( "Couldn't open Formalism!", "Selected file "+file+" does not contain a valid GUI model", parent = self ) return else: tkMessageBox.showerror( "Couldn't open Formalism!", "Selected file "+file+" does not contain a valid GUI model", parent = self ) return self.genGraphics = oldGraphics # restore graphics return buttonsRoot def openMetaModel(self, GUIModel = None, merge = 1, createNewRoot = 1): """ Opens a meta-model, adding the information to the previous ones (if merge == 1). If GUIModel is None, then opens a dialog box to ask for the name. GUIModel is the name of the GUI Model to be opened previous to the meta-model. """ if GUIModel == None: fileName = tkFileDialog.askopenfilename(filetypes=[("Python files", "*.py")]) if not fileName: return dir, file = os.path.split(fileName) className = string.split (file, ".") # compose class name self.GUIModelName = className[0] else: self.GUIModelName = GUIModel GUIModel = self.loadGUImodel(self.GUIModelName) # load the GUImodel if GUIModel == None: return # retrieve some elements of the GUI... fileName = GUIModel.Formalism_File.toString() # File where the meta-model is stored. dir, file = os.path.split(fileName) # split directory and file name className = string.split (file, ".") # split file name and extension self.metaModelName = className[0] # store the name of the file if not self.ASGroot and not self.editGGLabel: # DO NOT DO THIS IF WE ARE A CHILD WINDOW (THAT IS A gg RULE) if self.metaModelName in sys.modules.keys(): # file has already been loaded del sys.modules[self.metaModelName] try: exec "from "+self.metaModelName+" import *\n" in self.__dict__, self.__dict__ except ImportError: print "MetaModel "+self.metaModelName+" could not be found... Aborting" self.quit() sys.exit(1) except AttributeError: print "File "+self.metaModelName+" is not a valid meta-model... Aborting" self.quit() sys.exit(1) self.parent.config(menu=None) # eliminate old menu self.createMenu() # Creates a topLevel menu self.configureUserActions() self.setConnectivity(self) if self.console: self.console.appendText('Opening Formalism '+self.metaModelName) # delete buttons and then add the new ones if not merge: if self.buttonList != []: for bt in self.buttonList: try: bt.pack_forget() self.buttonList.remove(bt) del bt except AttributeError: buttons = list(bt)[3:] for b in buttons: b.pack_forget() del b self.buttonList.remove(bt) # self.toolBar.pack_forget() oldASGroot = self.ASGroot # keep old instantce of ASGroot newASGroot = self.createNewASGroot(self) # make a new instance of ASG # add the new ones... self.addButtons2ToolBar(GUIModel) # add the buttons to the toolbar if merge and oldASGroot: # if we have to merge... self.ASGroot.merge(newASGroot) elif not oldASGroot: # if we did not have an ASGroot self.ASGroot = newASGroot if createNewRoot == 0 and oldASGroot != None: self.ASGroot = oldASGroot # restore old ASG if we did not have to create a new one self.putMetaModelName() def clearModel(self): "Clears the current model" doClean = 1 st, fl = self.statusbar.getState(StatusBar.MODEL) if st == StatusBar.MODIFIED: if tkMessageBox.askyesno( "Model has changed...", "Are you sure you want to clean the canvas?", parent = self ) == 0: doClean = 0 if doClean: self.ASGroot.removeContents(self, 1) self.statusbar.event(StatusBar.MODEL, StatusBar.CLEAR, "Nonamed") if self.console: self.console.appendText('Clearing model') def saveTrans(self): "Saves a transformation in a file, in a similar way as saving a regular model" fileName = self.statusbar.getState(StatusBar.TRANSFORMATION)[1][1] # check if the transformation already has a name if not fileName or fileName == 'Nonamed': # no filename, so... fileName = tkFileDialog.asksaveasfilename(filetypes=[("Python files", "*.py")]) # ask for the file name if fileName: # if the user pressed ok... file = open(fileName, "w+t") # open file # import the subclass ... file.write('from GraphGrammarEdit import *\n') # generate imports... file.write('from GGruleEdit import *\n\n') file.write('def savedTrans(self):\n') # create a method called 'savedTrans' self.EditingGraphGrammar.writeConstructor2File(file, " ", "self.EditingGraphGrammar", 0, 0) # call the method to generate the constructor... file.write('\n\n') self.statusbar.event(StatusBar.TRANSFORMATION, StatusBar.SAVE, None, fileName ) transName = self.statusbar.getState(StatusBar.TRANSFORMATION)[1][0] if self.console: self.console.appendText('Saving transformation '+transName+' into file '+fileName) def globalPrecondition(self, whichRoot = None ): """ Evaluates a global precondition, on CREATE. This method is usually called while a model is being loaded. This API is intended to make such models more readable """ if whichRoot == None: # By default evaluate the preCondition on the ASGroot... root = self.ASGroot else: root = whichRoot # Unless another 'root' node is passed res= root.preCondition(ASG.CREATE) if res: self.constraintViolation(res) self.mode=self.IDLEMODE return def globalAndLocalPostcondition(self, node, whichRoot = None): """ Evaluates a global and local postcondition, on CREATE. This method is usually called while a model is being loaded. This API is intended to make such models more readable """ if whichRoot == None: # By default evaluate the preCondition on the ASGroot... root = self.ASGroot else: root = whichRoot # Unless another 'root' node is passed res= root.postCondition(ASG.CREATE) if res: self.constraintViolation(res) self.mode=self.IDLEMODE return res= node.postCondition(ASG.CREATE) if res: self.constraintViolation(res) self.mode=self.IDLEMODE return def loadTrans(self): """ Loads a transformation for editing. In the future, this must be the same as opening a model. """ fileName = tkFileDialog.askopenfilename(filetypes=[("Python files", "*.py")]) # ask for the file name if fileName: # if the user pressed ok... dir, file = os.path.split(fileName) # split path and file name className = string.split (file, ".") # separate file name and extension if className[0]: # if successful... self.checkInSearchPath(dir) print "Executing: "+repr(className[0]) exec "from "+className[0]+" import *\n" in self.__dict__, self.__dict__ # import file try: self.savedTrans(self) # call method to load data except AttributeError: tkMessageBox.showerror( "Couldn't load transformation!", "Selected file "+file+" does not contain a valid transformation", parent = self ) except TypeError: tkMessageBox.showerror( "Couldn't load transformation!", "Selected file "+file+" does not contain a valid transformation", parent = self ) else: transName = self.EditingGraphGrammar.Name.toString() print "GG name: "+repr(transName) self.statusbar.event(StatusBar.TRANSFORMATION, StatusBar.LOAD, transName, fileName ) if self.console: self.console.appendText('Loading transformation '+transName+' from file '+fileName) def editTrans(self): """ Edits the current graph grammar """ if self.EditingGraphGrammar == None: # checks if there are some graph grammar being edited... self.EditingGraphGrammar = GraphGrammarEdit(None, self) # none ma = ATOM3TypeDialog(self, self.EditingGraphGrammar) # edit the GG if ma.result_ok: self.statusbar.event(StatusBar.TRANSFORMATION, StatusBar.MODIFY, self.EditingGraphGrammar.Name.toString(), None) def createTrans(self): """ creates and edits a new graph grammar """ self.EditingGraphGrammar = GraphGrammarEdit(None, self) # Create a new GraphGrammarEdit object ma = ATOM3TypeDialog(self, self.EditingGraphGrammar) # Open a Dialog to edit it if ma.result_ok: self.statusbar.event(StatusBar.TRANSFORMATION, StatusBar.CREATE, self.EditingGraphGrammar.Name.toString(), "Nonamed") def genCode4Trans(self): """ Generates code for the current graph grammar """ if not self.EditingGraphGrammar: tkMessageBox.showerror( "Couldn't generate code!", "There is no transformation loaded", parent = self ) return if self.console: self.console.appendText('Generating code for transformation '+self.EditingGraphGrammar.Name.toString()) self.EditingGraphGrammar.genCode() # Call the object's code-generating method def executeTrans(self): """ loads an executable Graph Grammar and executes it on the actual graph... """ if not self.coupledGG: self.coupledGG = GrammarExecution(self) exeT = ATOM3TypeDialog(self, self.coupledGG ) if exeT.result_ok: # OK pressed... graphGrammars, stepByStep, entitiesMove, execution = self.coupledGG.getValue() # get the attributes values else: return self.GraphGrammars = [] # empty the list of loaded Graph Grammars for gg in graphGrammars: # for each Graph Grammar fileName, directory = gg.getValue() className = string.split (fileName, ".") # separate file name and extension # import the selected file... if className[0]: # if successful... self.checkInSearchPath(directory) # first check if it has been loaded before, to force a recompilation if className[0] in sys.modules.keys(): # file has already been loaded del sys.modules[className[0]] # delete to force a reload exec "from "+className[0]+" import *\n" # import the file name try: GG = eval(className[0])(self) # create an instance of the last GG loaded except NameError: tkMessageBox.showerror( "Couldn't execute transformation!", "Selected file "+fileName+" does not contain a valid transformation", parent = self ) return except TypeError: tkMessageBox.showerror( "Couldn't execute transformation!", "Selected file "+fileName+" does not contain a valid transformation", parent = self ) return if self.console: self.console.appendText('Executing transformation '+GG.__class__.__name__) self.GraphGrammars.append(GG) # append it to the list self.grs = GraphRewritingSys(self, self.GraphGrammars, self.ASGroot) # create a new rewriting system self.grs.evaluate(stepByStep[1], entitiesMove[1], execution[1]) # evaluate the GG using the current graph def options(self): """ Show the dialog for the options and saves them if the user pressed ok. """ op = ATOM3TypeDialog(self, self.option) if op.result_ok: # save options... (indeed save the entity...) file = open("MyOptions.py", "w+t" ) file.write('from Options import *\n') file.write('option = Options(None)\n') self.option.writeValue2File( file, '', 'option' ) file.close() self.loadOptions() def exitFromATOM3(self, unUsed = None ): """ Exits from ATOM3 if this instance is the main kernel, otherwise calls destroy. 2nd argument added because we´ve binded Alt-X to this method """ if self.IsMainKernel: # check status, and if we have not saved, present a message... st, fl = self.statusbar.getState(StatusBar.MODEL) if st == StatusBar.MODIFIED: if tkMessageBox.askyesno( "Model has changed...", "Would you like to save the model before leaving the application?", parent = self ) == 1: if fl != 'Nonamed': self.save(0, fl[0]) else: self.save(0) self.quit() sys.exit(1) else: self.destroy() def genPostscript (self): """ Generates a postscript file with the canvas information """ bbox = self.UMLmodel.bbox(ALL) if bbox: # if we have something in the canvas... po = PostscriptOptions(self) res= ATOM3TypeDialog(self, po) if res.result_ok: cm = po.colormode.getValue() rot= po.rotate.getValue() fileName = tkFileDialog.asksaveasfilename(filetypes=[("Encapsulated Postscript", "*.eps"), ("Postscript", "*.ps")]) # Modified 9 July 2002, HV # HV 8/7/2002 # To get a tight, correct boundingbox: # removed # self.UMLmodel.scan_dragto(bbox[0], bbox[1]) # and changed # bbox[2] -> bbox[2]-bbox[0] # bbox[3] -> bbox[3]-bbox[1] if fileName: self.UMLmodel.postscript(file=fileName, x=bbox[0], y=bbox[1], width=bbox[2]-bbox[0], height=bbox[3]-bbox[1], colormode = cm[0][cm[1]], rotate = rot[1]) # # Note: when using "smooth" connections, control points # may lie invisibly, far away. They are included in the bbox # calculation, which may lead to seemingly non-tight bboxes # (lots of white space). # Solution: look at non-smooth version to find the control points # and insert points in the connection. def rbuttonPressed (self, event): """ Callback method called when the user presses the right mouse button in the modelling zone (canvas). """ if self.mode == self.CONNECTstate and self.fromClass: # insert intermediate points... px, py = self.UMLmodel.canvasx(event.x), self.UMLmodel.canvasy(event.y) # convert into canvas coordinates... self.inter_connect_points.append(px) # add it to the list of intermediate points self.inter_connect_points.append(py) # add it to the list of intermediate points def buttonPressed (self, event): """ Callback method called when the user presses the left mouse button in the modelling zone (canvas) """ for action in self.userActionsMap.keys(): if self.mode == action: self.userActionsMap[action](self, event.x, event.y) return def closeDialog (self, unused, eventx, eventy): ct = self.find_visible_closest(eventx, eventy, self.UMLmodel) # find the closest thing tags = self.UMLmodel.gettags(ct) # get the tags if tags: if len(tags) >= 2 and tags[1][:4] == 'ASG_': # it's an embedded model # open an instance of ATOM3 to select the entity to connect to obj = VisualObj.Tag2ObjMap[tags[0]] # get the graphical object ma = ATOM3TypeDialog(self, obj.semanticObject, ATOM3TypeDialog.OPEN, ( None, self.setConnectMode)) elif tags[0][:3] == 'Obj': # then it's a class if self.ATOM3parent.fromClass and self.ATOM3parent.toClass: # it is the 2nd one self.ATOM3parent.sem_objTo = VisualObj.Tag2ObjMap[tags[0]].semanticObject # get the semantic object else: # it is the 1st one self.ATOM3parent.sem_objFrom = VisualObj.Tag2ObjMap[tags[0]].semanticObject # get the semantic object self.dialogInstance.ok() del self.dialogInstance del self.ATOM3parent def setEditGGLabel (self, AT3Dialog, ATOM3instance, semanticObject): "Sets the flag to edit the Graph Grammar Numbering Label conveniently" semanticObject.editGGLabel = self.editGGLabel def setConnectMode (self, AT3Dialog, ATOM3instance, semanticObject): "sets the mode of the ATOM3instance to CONNECTstate" ATOM3instance.mode = self.CONNECTstate ATOM3instance.dialogInstance = AT3Dialog ATOM3instance.ATOM3parent = self ATOM3instance.userActionsMap[self.CONNECTstate] = ATOM3instance.closeDialog def connectClass (self, unUsed, wx, wy): """ Find the closest class. If this is the second time this function is called, performs the actual connection. It may be possible that an intermediate object has to be added. """ x = self.UMLmodel.canvasx(wx) # convert into canvas coordinates y = self.UMLmodel.canvasy(wy) ct = self.find_visible_closest(x, y, self.UMLmodel) # find the closest thing tags = self.UMLmodel.gettags(ct) # get the tags if tags: obj = VisualObj.Tag2ObjMap[tags[0]] # get the graphical object if len(tags) >= 2 and tags[1][:4] == 'ASG_': # it's an embedded model # open an instance of ATOM3 to select the entity to connect to if self.fromClass: # store the graphical tag (a model) self.toClass = tags[0] else: self.fromClass = tags[0] ma = ATOM3TypeDialog(self, obj.semanticObject, ATOM3TypeDialog.OPEN, (None, self.setConnectMode)) # open to select the entity inside the model # check if we have both sides... if self.sem_objFrom and self.sem_objTo: # we have both sides... self.drawConnection() self.fromClass = None self.toClass = None self.sem_objFrom = None self.sem_objTo = None self.mode = self.IDLEMODE # update statusbar... self.statusbar.event(StatusBar.MODEL, StatusBar.MODIFY) elif tags[0][:3] == 'Obj': # then it's a class if self.fromClass: # it is the 2nd. one, because the 1st. one is filled self.toClass = tags[0] # get the graphical tag self.sem_objTo = obj.semanticObject # get the semantic object self.drawConnection () # draw the connection self.fromClass = None # empty variables self.toClass = None self.sem_objFrom = None self.sem_objTo = None self.mode = self.IDLEMODE # update statusbar... self.statusbar.event(StatusBar.MODEL, StatusBar.MODIFY) else: # it is the 1st. one self.inter_connect_points = [] self.fromClass = tags[0] # get the graphical tag self.sem_objFrom = obj.semanticObject # get the semantic object def chooseLinkType(self, listOfLinks): """ Function that presents a dialog box to choose a link type - listOfLinks: is a list of tuples (, ) - returns the tuple that's been selected """ # first we create a list of ATOM3Strings using the first component of the tuples... A3StringList = [] for link in listOfLinks: ns = ATOM3String(link[0]) A3StringList.append(ns) # create an ATOM3List of Strings with the initial value set to the previous list atl = ATOM3List([0,0,0,0], ATOM3String) atl.setValue(A3StringList) # Present the previous list in a dialog box dlb = ATOM3TypeDialog(self, atl) if dlb.result_ok: return listOfLinks[dlb.widget.lastSelection] def showConnection(self, fromClass, toClass, smoothConn = 0, x0 = None, y0 = None, x1 = None, y1 = None, num1st = 2): """ Connects graphically two entities of the model. Checks if graphical objects have connectors and if an intermediate object has to bee created. """ if fromClass : self.fromClass = fromClass if toClass : self.toClass = toClass maxDistance, maxFreeDistance = 10000, 10000 actDistance = 0 xc0, yc0, xc1, yc1 = 0, 0, 0, 0 import math if type(self.fromClass) == StringType: objFrom = VisualObj.Tag2ObjMap[self.fromClass] # changed 15 July by JL else: objFrom = self.fromClass # changed 15 July by JL if type(self.toClass) == StringType: objTo = VisualObj.Tag2ObjMap[self.toClass] # changed 15 July by JL else: objTo = self.toClass # changed 15 July by JL if objFrom.isSubclass("graphEntity") and not objFrom.connectors: tkMessageBox.showwarning( "Don´t know how to connect objects!", "First object does not have connectors", parent = self ) return (None, None, None, None, None) if objTo.isSubclass("graphEntity") and not objTo.connectors: tkMessageBox.showwarning( "Don´t know how to connect objects!", "Second object does not have connectors", parent = self ) return (None, None, None, None, None) print "in connect object" # see if we can connect the objects directly (they are Entity and Link), or we must put something in between. class1Name = objFrom.semanticObject.getClass() class2Name = objTo.semanticObject.getClass() if class1Name in self.ConnectivityMap.keys() and class2Name in self.ConnectivityMap.keys(): if class2Name in self.ConnectivityMap[class1Name].keys(): # if this is the case, probably the entities belong to the same meta-model betweenClasses = self.ConnectivityMap[class1Name][class2Name] # we should put an instance of this in between (but this may be a list) if len(betweenClasses) > 1: # we should choose one! btClass = self.chooseLinkType(betweenClasses) # we must choose one elif len(betweenClasses)==1: # Only one possibility btClass = betweenClasses[0] else: # no possibilities -> objects cannot be connected if objFrom.isSubclass("graphEntity") and objTo.isSubclass("graphEntity"): tkMessageBox.showwarning( "Don´t know how to connect objects!", "None of them is a link!", parent = self ) return (None, None, None, None, None) if smoothConn: connHandler = objFrom.connect(objTo, self.inter_connect_points, x0, y0, x1, y1, 1, num1st) else: connHandler = objFrom.connect(objTo, self.inter_connect_points, x0, y0, x1, y1, 0, num1st) self.inter_connect_points = [] return (connHandler, objFrom, objTo, -1, None) # now btClass is a tuple (class, method-to-create-a-class-instance), create object in the middle of both selected objects... newObjX, newObjY = (objFrom.x+objFrom.sizeX/2+objTo.x+objTo.sizeX/2)/2, (objFrom.y+objFrom.sizeY/2+objTo.y+objTo.sizeY/2)/2 newSemObj = btClass[1](self, newObjX, newObjY, 0) # now connect objects with the newly created one... # ey! we should evaluate pre and post conditions!! connHandle1 = objFrom.connect(newSemObj.graphObject_) connHandle2 = newSemObj.graphObject_.connect(objTo) return (connHandle1, objFrom, objTo, connHandle2, newSemObj) if smoothConn: connHandler = objFrom.connect(objTo, self.inter_connect_points, x0, y0, x1, y1, 1, num1st) else: connHandler = objFrom.connect(objTo, self.inter_connect_points, x0, y0, x1, y1, 0, num1st) self.inter_connect_points = [] return (connHandler, objFrom, objTo, -1, None) def drawConnections(self, * listOfConnections ): """Same as drawConnection but a tuple of tuples is given... tuples can be : - Size 2 and then it means (sem_objFrom, sem_objTo) and connecting respective icons... - Size 4 and then it means (fromClass, toClass, sem_objFrom, sem_objTo) """ for connection in listOfConnections: num1st = 2 smoothConn = 0 interPoints = [] lconn = len(connection) if issubclass(connection[0].__class__, VisualObj): # A 'Visual connection' # then we must have something like (fromClass, toClass, sem_objFrom, sem_objTo, [inter-points], smooth, num-points-of-1st-connection) # the three last parameters are optional. self.fromClass = connection[0] self.toClass = connection[1] self.sem_objFrom = connection[2] self.sem_objTo = connection[3] if lconn >= 5: interPoints = connection[4] if lconn >= 6: smoothConn = connection[5] if lconn >= 7: num1st = connection[6] else: # A 'non-visual' connection # then we must have something like (sem_objFrom, sem_objTo, [inter-points], smooth, num-points-of-1st-connection) # the three last parameters are optional. self.sem_objFrom = connection[0] self.sem_objTo = connection[1] if lconn >= 3: interPoints = connection[2] if lconn >= 4: smoothConn = connection[3] if lconn >= 5: num1st = connection[4] if self.sem_objFrom.graphObject_: # check if we have graphics... self.fromClass = self.sem_objFrom.graphObject_.tag self.toClass = self.sem_objTo.graphObject_.tag else: self.fromClass = None self.toClass = None tkMessageBox.showinfo( "Connecting non-graphical entities", "From '"+self.sem_objFrom.toString()+"' to '"+self.sem_objTo.toString()+"'", parent = self ) if len(interPoints) > 4: self.inter_connect_points = []+interPoints[2:len(interPoints)-2] else: self.inter_connect_points = [] if len (interPoints) > 1: self.drawConnection(smoothConn, interPoints[0], interPoints[1], interPoints[len(interPoints)-2], interPoints[len(interPoints)-1], num1st) else: print "in draw Connection (2)" self.drawConnection(smoothConn, None, None, None, None, num1st) self.fromClass = None self.toClass = None self.sem_objFrom = None self.sem_objTo = None def checkCardinalities(self, objFrom, objTo): """ Before connecting these two objects, check if the connection is valid. If it is valid, it returns None, if invalid, returns a tuple with the error. The 1st component of the tuple is the error message, the second one is the actual object (to be highlighted) """ classFrom, classTo = objFrom.__class__.__name__, objTo.__class__.__name__ cardinality1 = self.CardinalityTable[classFrom][classTo] # get cardinality info cardinality2 = self.CardinalityTable[classTo][classFrom] # get cardinality info if cardinality1 == [] or cardinality2 == []: # if something is missing, then raise an error return ("Objects of types "+classFrom+" and "+classTo+" cannot be connected.", objFrom) card1 = self.checkDirectionOfCardinality(cardinality1, "Source") # Check that the direction of connection is allowed if not card1: if objFrom.keyword_: return ("Wrong connection direction for object: "+objFrom.keyword_.toString(), objFrom) else: return ("Wrong connection direction for source object.", objFrom) numObjects1 = self.countObjectOfClass(objFrom.out_connections_, objTo.__class__.__name__) min1, max1 = self.getCardinalityValues(card1) if numObjects1 > max1: if objFrom.keyword_: return ("Too many objects of type "+classTo+" connected to "+objFrom.keyword_.toString(), objFrom) else: return ("Too many objects of type "+classTo+" connected to source object", objFrom) card2 = self.checkDirectionOfCardinality(cardinality2, "Destination") # Check that the direction of connection is allowed if not card2: if objTo.keyword_: return ("Wrong connection direction for object: "+objTo.keyword_.toString(), objTo) else: return ("Wrong connection direction for destination object.", objTo) numObjects2 = self.countObjectOfClass(objTo.in_connections_, objFrom.__class__.__name__) min2, max2 = self.getCardinalityValues(card2) if numObjects2 > max2: if objTo.keyword_: return ("Too many objects of type "+classFrom+" connected to "+objTo.keyword_.toString(), objTo) else: return ("Too many objects of type "+classFrom+" connected to source object", objTo) return None def getCardinalityValues (self, cardinality ): """ gets the numeric values of the cardinality tuple """ if cardinality[0] in ["n", "N", "m", "M"]: min = 1000000 else: min = int(cardinality[0]) if cardinality[1] in ["n", "N", "m", "M"]: max = 1000000 else: max = int(cardinality[1]) return (min, max) def hardCardinalityCheck(self, node): """ Performs a cardinality check of node 'node'. If the check is not passed, then a tuple with the error is returned, else None. """ entities = self.CardinalityTable.keys() # Get the type of nodes we will have to check... nodeClass= node.getClass() # Get node's class name for entity in entities: if entity in self.CardinalityTable[nodeClass].keys(): cards = self.CardinalityTable[nodeClass][entity] # Get cardinalities to check. for card in cards: if card[2] == "Source": theList = node.out_connections_ else: theList = node.in_connections_ numObjects = self.countObjectOfClass(theList, entity) min, max = self.getCardinalityValues ( card ) if numObjects < min: if node.keyword_: return ("Too few objects of type "+entity+" connected to "+node.keyword_.toString()+"("+str(min)+" are needed)", node) else: return ("Too few objects of type "+entity+" connected to source object ("+str(min)+" are needed)", node) elif numObjects > max: if node.keyword_: return ("Too many objects of type "+entity+" connected to "+node.keyword_.toString()+"( "+str(max)+" is the maximum)", node) else: return ("Too many objects of type "+entity+" connected to source object ( "+str(max)+" is the maximum)", node) return None def checkModel(self): """ Iterates over all the objects of the model, performing a hard cardinality check. """ for nodeType in self.ASGroot.listNodes.keys(): for object in self.ASGroot.listNodes[nodeType]: res = self.hardCardinalityCheck(object) # perform a 'hard' cardinality check... if res: return res return None def countObjectOfClass(self, objectList, className): """ Count the number of objects of class 'className' that there are in objectList """ counter = 0 for obj in objectList: if obj.getClass() == className: counter = counter + 1 return counter def checkDirectionOfCardinality(self, cardInfo, direction): """ cardInfo is a list of tuples with a connection information: (min, max, 'direction'). Tries to find a tuple that has the direction 'direction'. """ card1 = None for card in cardInfo: # look for source information for objFrom if card[2] == direction: card1 = card break return card1 def drawConnection(self, smoothConnection = 0, x0 = None, y0 = None, x1 = None, y1 = None, num1st = 2): """ Connects two entities of the model, whether they are given as graphical entities (self.fromClass and self.toClass must have a vlue then) or just semantically, (self.sem_objFrom and self.sem_objTo must have a value then). """ if self.fromClass and self.toClass: # if we have graphical information... connHandler, objFrom, objTo, auxHandler, newObj = self.showConnection(self.fromClass, self.toClass, smoothConnection, x0, y0, x1, y1, num1st) if not connHandler and not objFrom and not objTo: # something went wrong in showConnection return # now set the sematic objects to be connected if we don't have them yet... if not self.sem_objFrom: self.sem_objFrom = objFrom.semanticObject if not self.sem_objTo: self.sem_objTo = objTo.semanticObject # try the global constraints (if not drawing a rule)... if not self.editGGLabel: res = self.ASGroot.preCondition(ASG.CONNECT) if res: # global constraint do not hold! self.undoConnection(res, objFrom, objTo, connHandler) return res = self.sem_objFrom.preCondition(ASGNode.CONNECT, "SOURCE") # local constraint do not hold! if res: self.undoConnection(res, objFrom, objTo, connHandler) return res = self.sem_objTo.preCondition(ASGNode.CONNECT, "DESTINATION") # local constraint do not hold! if res: self.undoConnection(res, objFrom, objTo, connHandler) return # ey! also in the newly created entity (if any has been created) if newObj: res = newObj.preCondition(ASGNode.CONNECT, "DESTINATION") # local constraint do not hold! if res: self.undoConnection(res, objFrom, newObj.graphObject_, connHandler) return res = newObj.preCondition(ASGNode.CONNECT, "SOURCE") # local constraint do not hold! if res: self.undoConnection(res, newObj.graphObject_, objTo, auxHandler) return self.sem_objFrom.preAction(ASGNode.CONNECT, "SOURCE") self.sem_objTo.preAction(ASGNode.CONNECT, "DESTINATION") if newObj: newObj.preAction(ASGNode.CONNECT, "SOURCE") newObj.preAction(ASGNode.CONNECT, "DESTINATION") if newObj == None: # No new objects created self.sem_objFrom.out_connections_.append(self.sem_objTo) self.sem_objTo.in_connections_.append(self.sem_objFrom) else: # A new object had to be created in between self.sem_objFrom.out_connections_.append(newObj) newObj.in_connections_.append(self.sem_objFrom) newObj.out_connections_.append(self.sem_objTo) self.sem_objTo.in_connections_.append(newObj) # shouldn't we test constraints in the newly created object?? # Check the cardinalities if we are not editing a Graph Grammar...(we have them in cardinalityTable)... if not self.editGGLabel: if newObj == None: # 1st check that no object has been created in between res = self.checkCardinalities (self.sem_objFrom, self.sem_objTo) # if it returns !=0, then the connection is illegal... if res: self.undoConnection(res, objFrom, objTo, connHandler) return else: # check the cardinalities with the object in between res = self.checkCardinalities (self.sem_objFrom, newObj) # if it returns 0, then the connection is illegal... if res: self.undoConnection(res, objFrom, objTo, connHandler) # remove newObj newObj.graphObject_.erase(self) newObj.removeNode() return res = self.checkCardinalities (newObj, self.sem_objTo) # if it returns 0, then the connection is illegal... if res: self.undoConnection(res, objFrom, objTo, connHandler) return # try the global constraints... if not self.editGGLabel: res = self.ASGroot.postCondition(ASG.CONNECT) if res: self.undoConnection(res, objFrom, objTo, connHandler) return res = self.sem_objFrom.postCondition(ASGNode.CONNECT, "SOURCE") # try the local post-conditions (1st object) if res: self.undoConnection(res, objFrom, objTo, connHandler) return res = self.sem_objTo.postCondition(ASGNode.CONNECT, "DESTINATION") # try the local post-conditions (2nd object) if res: self.undoConnection(res, objFrom, objTo, connHandler) return if newObj: res = newObj.postCondition(ASGNode.CONNECT, "DESTINATION") # try the local post-conditions (1st object) if res: self.undoConnection(res, objFrom, newObj.graphObject_, connHandler) # remove newObj newObj.graphObject_.erase(self) newObj.removeNode() return res = newObj.postCondition(ASGNode.CONNECT, "SOURCE") # try the local post-conditions (2nd object) if res: self.undoConnection(res, newObj.graphObject_, objTo, auxHandler) # remove newObj newObj.graphObject_.erase(self) newObj.removeNode() return self.ASGroot.postAction(ASGNode.CONNECT, "SOURCE") self.ASGroot.postAction(ASGNode.CONNECT, "DESTINATION") self.sem_objFrom.postAction(ASGNode.CONNECT, "SOURCE") self.sem_objTo.postAction(ASGNode.CONNECT, "DESTINATION") if newObj: newObj.postAction(ASGNode.CONNECT, "DESTINATION") newObj.postAction(ASGNode.CONNECT, "SOURCE") def undoConnection(self, res, objFrom, objTo, connHandler): """ Undoes a connection due to constraint fail. res is the resulting message """ self.constraintViolation(res) if self.sem_objTo in self.sem_objFrom.out_connections_: self.sem_objFrom.out_connections_.remove(self.sem_objTo) if self.sem_objFrom in self.sem_objTo.in_connections_: self.sem_objTo.in_connections_.remove(self.sem_objFrom) if self.fromClass and self.toClass: # if we have graphical information... self.UMLmodel.delete(connHandler) # UNDO THE PREACTIONS!!!!! result1 = objFrom.removeConnection(self, connHandler) result2 = objTo.removeConnection(self, connHandler) if result1 == 2 and objFrom.isSubclass("graphLink"): objFrom.semanticObject.removeNode() if result2 == 2 and objTo.isSubclass("graphLink"): objTo.semanticObject.removeNode() return def deleteGraphicalConnections(self, node): "deletes graphical connections of the given entity" obj = node.graphObject_ while obj.connections != []: c = obj.connections[0] obj.connections.remove(c) self.UMLmodel.delete(c[0]) return obj def showGraphicalConnections(self, node): "Given the node 'node', shows its connections (none of them must be visible!)" for conObject in node.in_connections_: if node.graphObject_.hasGraphicalConnection(conObject.graphObject_) < 0: # no connections between them... self.fromClass = conObject.graphObject_.tag self.toClass = node.graphObject_.tag self.showConnection(conObject.graphObject_.tag, node.graphObject_.tag) self.fromClass = None self.toClass = None for conObject in node.out_connections_: if node.graphObject_.hasGraphicalConnection(conObject.graphObject_) < 0: # no connections between them... self.fromClass = node.graphObject_.tag self.toClass = conObject.graphObject_.tag self.showConnection(node.graphObject_.tag, conObject.graphObject_.tag) self.fromClass = None self.toClass = None def deleteGraphicsOfSemanticEntity (self, node): "deletes the corresponding graphic entity of the given node" obj = self.deleteGraphicalConnections(node) cts = self.UMLmodel.find_withtag(obj.tag) for c in cts: self.UMLmodel.delete(c) def getConnectedEntities (self, grHandler): "Returns a tuple with the semantic entities connected by grHandler" source, destination = None, None for nt in self.ASGroot.listNodes.keys(): for node in self.ASGroot.listNodes[nt]: if node.graphObject_: # if node has graphical Object htuple = node.graphObject_.hasConnectHandler(grHandler) # see if the object has this handler if htuple: if htuple[1] == 0: source = node else: destination = node return (source, destination) def deleteEntity(self, unUsed, wx, wy): """ delete the nearest thing, be an object, a link or a segment """ x = self.UMLmodel.canvasx (wx) # convert to canvas coordinate system y = self.UMLmodel.canvasy (wy) self.fromClass = None self.toClass = None ct = self.find_visible_closest(x, y, self.UMLmodel) tags = self.UMLmodel.gettags(ct) if tags: if len(tags)>1 and tags[1] != 'current': # it is an entity... self.deleteRealEntity(tags[0]) else: # it is a connection self.deleteConnection(ct[0], tags[0]) # delete it! # also check status bars... self.statusbar.event(StatusBar.MODEL, StatusBar.MODIFY) self.mode = self.IDLEMODE def deleteRealEntity (self, tag): "Deletes the entity with tag 'tag' invoking corresponding pre and post conditions " obj = VisualObj.Tag2ObjMap[tag] # obtain the visual object res = self.ASGroot.preCondition (ASGNode.DELETE) # Test global pre condition if res: self.constraintViolation(res) return res = obj.semanticObject.preCondition (ASGNode.DELETE) # Test local pre condition if res: self.constraintViolation(res) return self.ASGroot.preAction ( ASGNode.DELETE ) obj.semanticObject.preAction ( ASGNode.DELETE ) obj.erase(self) # remove from ASG and from all its connected nodes... obj.semanticObject.removeNode() res = self.ASGroot.postCondition (ASGNode.DELETE) # Test global post condition if res: self.constraintViolation(res) return res = obj.semanticObject.postCondition (ASGNode.DELETE) # Test local post condition if res: self.constraintViolation(res) return self.ASGroot.postAction ( ASGNode.DELETE ) obj.semanticObject.postAction ( ASGNode.DELETE ) def deleteConnection(self, handler, tag): "deletes the connection given by handler, invoking the corresponding pre and post action" obj = VisualObj.Tag2ObjMap[tag] # obtain the visual object # preconditions and preactions res = self.ASGroot.preCondition (ASGNode.DISCONNECT) if res: self.constraintViolation(res) return # look for the (semantic) entities being disconnected source, destination = self.getConnectedEntities(handler) if source: # may be the connection is still not connected res = source.preCondition (ASGNode.DISCONNECT,destination, "SOURCE" ) if res: self.constraintViolation(res) return if destination: # may be the connection is still not connected res = destination.preCondition (ASGNode.DISCONNECT, source, "DESTINATION") if res: self.constraintViolation(res) return self.ASGroot.preAction(ASGNode.DISCONNECT) if source: source.preAction(ASGNode.DISCONNECT, destination, "SOURCE") if destination: destination.preAction(ASGNode.DISCONNECT, source, "DESTINATION") # Delete graphical and semantic connections link_removed = obj.removeConnection(self, handler) if destination: destination.graphObject_.removeConnection(self, handler) # may be destination is None (an unconnected connection) self.deleteSemConnection( [source, destination] ) if link_removed == 2: # a 2 means that the whole link has been removed obj.semanticObject.removeNode() # Post Conditions and Post Actions res = self.ASGroot.postCondition (ASGNode.DISCONNECT) if res: self.constraintViolation(res) return if source: res = source.postCondition (ASGNode.DISCONNECT,destination, "SOURCE" ) if res: self.constraintViolation(res) return if destination: res = destination.postCondition (ASGNode.DISCONNECT, source, "DESTINATION") if res: self.constraintViolation(res) return self.ASGroot.postAction(ASGNode.DISCONNECT) if source: source.postAction(ASGNode.DISCONNECT, destination, "SOURCE") if destination: destination.postAction(ASGNode.DISCONNECT, source, "DESTINATION") def deleteSemConnection (self, objects): """ delete the connections from the semantic entities...""" if not objects[0]: # it was a unary connection or (an unconnected relationship...) objects[0] = objects[1] # both ends of connnections are the same object elif not objects[1]: objects[1] = objects[0] if objects[1] in objects[0].out_connections_: objects[0].out_connections_.remove(objects[1]) if objects[0] in objects[1].in_connections_: objects[1].in_connections_.remove(objects[0]) self.mode = self.IDLEMODE def changeMode (self, event): """ Changes the mode (the user clicked in a button created on the fly). """ self.mode = self.modes[event.widget] def addButtons2ToolBar (self, GUIModel): """ Adds the buttons to the toolbar. The specific buttons (that must be created here on the fly) of the meta-model are in the GUIModel. """ formalismName = GUIModel.Formalism_Name.toString() # 'User-friendly' name of the formalism rowSize = GUIModel.RowSize.getValue() # buttons per row formalismFile = GUIModel.Formalism_File.toString() # meta-model file fName = string.replace(formalismName, " ", "_") mmToolBar = Frame(self.toolBar, relief = RAISED) b = Label(mmToolBar, text = formalismName, fg="darkgreen", bg="white", font = ("Helvetica",10), relief = GROOVE, padx=1) b.pack(side = TOP, fill = X, ipady = 5) self.openMetaModels.newItem(ATOM3String(formalismName)) metaModelInfo = [mmToolBar, formalismName, formalismFile] auxPanel = Frame(mmToolBar, relief = GROOVE) counter = 0 # img1 = PhotoImage(file="~/images/red_dot.gif") # b1 = Button(self, image=img1) # b1.pack(side=LEFT) buttonImageList = [] for node in GUIModel.listNodes['ButtonConfig']: # for each 'ButtonConfig' in the model... text = 1 if node.Contents.lastSelected == 'Text': # check if we should put a text in the button buttonText = node.Contents.Text.toString() # get the button text... elif node.Contents.lastSelected == 'Image': text = 0 buttonImageFileName = node.Contents.Image.toString() if buttonImageFileName[0] == '~': import os.path buttonImageFileName = os.path.expanduser(buttonImageFileName) buttonImage = PhotoImage(file = buttonImageFileName, format = "gif", ).copy() buttonImageList.append(buttonImage) newDrawingMode = node.Drawing_Mode.getValue()[1] # see if we have to create a new mode name, lang, kind, act, code = node.Action.getValue() # Unwrap action... #if newDrawingMode: functionName = fName+str(counter) # compose function name # see if the function is present yet if not functionName in self.__dict__.keys(): functionHeader = "def "+functionName+"(self, wherex, wherey ):\n" # compose function header functionBody = " " +string.replace(code,'\n', '\n ')+"\n" # compose function body exec functionHeader+functionBody in self.__dict__, self.__dict__ # 'create' new method method = self.__dict__[functionName] # obtain a reference to the method newMode = "NEWMODE"+fName+str(counter) self.userActionsMap[newMode] = method if text: newButton = Button(auxPanel, text = buttonText) print "Text button created: "+buttonText else: newButton = Button(auxPanel, image = buttonImage, activeforeground = 'green', #default = NORMAL, highlightcolor = 'red', highlightbackground = 'blue', state = NORMAL) print "Image button created: "+buttonImageFileName self.modes[newButton] = newMode newButton.bind("", self.changeMode) #else: # functionName = formalismName+str(counter) # compose function name # if not functionName in self.__dict__.keys(): # functionHeader = "def "+functionName+"(self, event ):\n" # compose function header # functionBody = " " +string.replace(code,'\n', '\n ')+"\n" # compose function body # exec functionHeader+functionBody in self.__dict__ # 'create' new method # # method = self.__dict__[functionName] # obtain a reference to the method # newButton=Button(auxPanel, text = buttonText ) # self.modes[newButton] = newMode # newButton.bind("", self.changeMode) newButton.pack(side=LEFT, padx=2, pady=2, fill=X, expand=1) counter = counter + 1 if counter % rowSize == 0: # check if we have rowSize elements in the row auxPanel.pack(side=TOP, fill=X, ipady=2) auxPanel = Frame(mmToolBar, relief = GROOVE) metaModelInfo.append(newButton) self.buttonList.append(tuple(metaModelInfo)) auxPanel.pack(side=TOP, fill=X, ipady=2) mmToolBar.pack(side=TOP) # To push the buttons to the top of the panel, put an empty frame # in the bottom. emptyPanel = Frame(self.toolBar) emptyPanel.pack(side=BOTTOM) # OpBar is the operations bar, i.e. it contains buttons for the model # operations: insert model, edit entity, expand model, connect, delete. def addButtons2OpBar(self): if not self.isOpBarPresent: self.isOpBarPresent = 1 barlbl = Label(self.opBar, text="Model ops", width = 10, relief=GROOVE) barlbl.pack(side=LEFT, padx=2, pady=1) bEdit = Button(self.opBar, text="Edit entity", command=self.editMode ) bEdit.pack(side=LEFT, padx=2, pady=1) bconnect = Button( self.opBar, text="Connect", command=self.connMode ) bconnect.pack(side=LEFT, padx=2, pady=1) bdelete = Button( self.opBar, text="Delete", command= self.deleteMode ) bdelete.pack(side=LEFT, padx=2, pady=1) bInsert=Button(self.opBar, text="Insert model", command=self.newModeModel ) bInsert.pack(side=LEFT, padx=2, pady=1) bExp = Button( self.opBar, text="Expand model", command=self.expandModeModel ) bExp.pack(side=LEFT, padx=2, pady=1) if self.IsMainKernel: bexit = Button( self.opBar, text="Exit", fg="red", command = self.exitFromATOM3) # add a Button to exit bexit.pack(side=RIGHT, fill=Y, padx=2, pady=1) ## self.buttonList.append(bInsert) ## self.buttonList.append(bEdit) ## self.buttonList.append(bExp) ## self.buttonList.append(bconnect) ## self.buttonList.append(bdelete) # only generate the button if we are a main window... ## if self.IsMainKernel: ## self.buttonList.append(bexit) # GrphBar is the graphics bar, i.e. it contains buttons for the graphics # operations: insert model, edit entity, expand model, connect, delete. def addButtons2GrphBar(self): if not self.isGrphBarPresent: self.isGraphBarPresent = 1 barlbl = Label(self.grphBar, text="Visual ops", width = 10, relief=GROOVE) barlbl.pack(side=LEFT, padx=2, pady=1) bSmooth = Button(self.grphBar, text="Smooth", command=self.smoothConnection) bSmooth.pack(side=LEFT, padx=2, pady=1) bIns = Button( self.grphBar, text="Insert point", command=self.insertPointInConnection) bIns.pack(side=LEFT, padx=2, pady=1) bDel = Button( self.grphBar, text="Delete point", command=self.deletePointFromConnection) bDel.pack(side=LEFT, padx=2, pady=1) bChange=Button(self.grphBar, text="Change connector", command=self.changeConnector) bChange.pack(side=LEFT, padx=2, pady=1) ## self.buttonList.append(bIns) ## self.buttonList.append(bDel) ## self.buttonList.append(bSmooth) ## self.buttonList.append(bChange) def deleteMode(self): """ enters in the delete mode """ self.mode = self.DELETEstate def newModeModel(self): """ enters in the INSERTModel mode """ self.mode = self.INSERTModel def expandModeModel(self): """ enters in the EXPANDModel mode """ self.mode = self.EXPANDModel def mouseMove(self, event): """ Callback method called when the user moves the mouse with the left button pressed """ if self.isMousePressed: # evaluate preConditions... cx, cy = self.UMLmodel.canvasx(event.x), self.UMLmodel.canvasy(event.y) self.moveBox( cx, cy) self.initDragX, self.initDragY = cx, cy self.hasMoved = 1 def moveBox (self, x, y): """ method to move a graphical node in the canvas, called by the mouseMove method. """ sel = self.UMLmodel.find_withtag("selected") # find the selected 'box' tag = self.UMLmodel.gettags(sel[0])[0] if sel and tag and VisualObj.Tag2ObjMap.has_key(tag): # 1st. try the global constraints... res = self.ASGroot.preCondition(ASG.MOVE) # evaluate global pre-conditions if res: return self.undodrag(res) obj = VisualObj.Tag2ObjMap[tag] res = obj.semanticObject.preCondition(ASG.MOVE) # evaluate global pre-conditions if res: return self.undodrag(res) self.ASGroot.preAction(ASG.MOVE) # execute global pre-actions obj.semanticObject.preAction(ASG.MOVE) # execute local pre-actions dx = x-self.initDragX # calculate the displacement in x and y dy = y-self.initDragY obj.Move(dx, dy) # Move object (We do not care wether it is a link or an entity) res = self.ASGroot.postCondition(ASG.MOVE) # evaluate global pre-conditions if res: return self.undomovebox(res,dx,dy,sel,obj, tag) res = obj.semanticObject.postCondition(ASG.MOVE) # evaluate global pre-conditions if res: return self.undomovebox(res,dx,dy,sel,obj, tag) self.ASGroot.postAction(ASG.MOVE) # execute global post-actions obj.semanticObject.postAction(ASG.MOVE) # execute local post-actions # def undomovebox(self, res, dx, dy, sel, obj, tag): """ undoes an entity movement, due to a constraint failure... """ obj.Move(-dx, -dy) return self.undodrag(res) def drop(self, event): """ Callback method invoked when the mouse left-button is released """ obj = None if self.hasMoved: # if we have started a drag operation previously sel = self.UMLmodel.find_withtag("selected") # find the selected 'box' tag = self.UMLmodel.gettags(sel[0])[0] # 1st. try the global constraints... res = self.ASGroot.preCondition(ASG.DROP) # evaluate global pre-conditions if res: self.hasMoved = 0 return self.undodrag(res) obj = VisualObj.Tag2ObjMap[tag] res = obj.semanticObject.preCondition(ASG.DROP) # evaluate local pre-conditions if res: self.hasMoved = 0 return self.undodrag(res) res = self.ASGroot.preAction(ASG.DROP) # execute global pre-actions res = obj.semanticObject.preAction(ASG.DROP) # execute local pre-actions self.moveBox(self.UMLmodel.canvasx(event.x), self.UMLmodel.canvasy(event.y)) # move box if self.isMousePressed: # if we were currently pressing the mouse... if obj: obj.HighLight(0) else: sel = self.UMLmodel.find_withtag("selected") # find the selected 'box' if sel and sel[0]: tag = self.UMLmodel.gettags(sel[0])[0] if tag: obj = VisualObj.Tag2ObjMap[tag] if obj: obj.HighLight(0) self.UMLmodel.dtag(ALL, "selected") # delete the selected tag from the items self.isMousePressed = 0 else: # unHighlight... if obj: obj.HighLight(0) if self.hasMoved: # if we have started a drag operation previously res = self.ASGroot.postCondition(ASG.DROP) # evaluate global pre-conditions if res: self.hasMoved = 0 return self.undodrag(res) obj = VisualObj.Tag2ObjMap[tag] res = obj.semanticObject.postCondition(ASG.DROP) # evaluate local pre-conditions if res: self.hasMoved = 0 return self.undodrag(res) res = self.ASGroot.postAction(ASG.DROP) # execute global post-actions res = obj.semanticObject.postAction(ASG.DROP) # execute local post-actions self.hasMoved = 0 def highLightGraphs(self, graphList): """ highlights all the graphs contained in the list for the user to select one. - graphList: is a list of graphs. A graph is a tuple ( , [nodes]). Where id is an integer and [nodes] is a list of nodes. Enters in the mode SELECTgraph """ self.graphs2select = graphList # save the list, because, we'll have to wait for the user to click on some node self.highLightNodesInGraphs(self.graphs2select, 1) # Highlight each node self.mode = self.SELECTgraph # put system in select graph mode def selectGraph(self, unUsed, wx, wy): """This function is called when the user clicks on canvas and the mode is SELECTgraph""" x, y = self.UMLmodel.canvasx(wx), self.UMLmodel.canvasy(wy) # convert to canvas coordinate system ct = self.find_visible_closest(x, y, self.UMLmodel) tags = self.UMLmodel.gettags(ct) if tags and tags[0] != 'current': # i.e. we don't have a connection obj = VisualObj.Tag2ObjMap[tags[0]].semanticObject # get semanticObject # Now look for the graph whose node have been clicked for graphTuple in self.graphs2select: id, graph = graphTuple # unwrap graph information if obj in graph: # ey, we've found the subraph that's been clicked self.mode = self.IDLEMODE # only if the clicked node belongs to some graph return to IDLE state self.highLightNodesInGraphs(self.graphs2select, 0) # un-Highlight each node self.grs.executeRule(graphTuple) # execute the rule that's been selected del self.graphs2select return def highLightNodesInGraphs(self, graphList, flag): """ Highlights (flag = 1) or LowLights (flag = 0) all the VISIBLE elements with the 'selected' tag. - graphList: is a list of tuples (id,[node]) """ highLighted = [] # list of highlighted nodes (do not highlight them twice, or we'll lost their color) for grp in graphList: # for each graph id, graph = grp for node in graph: if not node in highLighted: # if not highlighted yet, do it node.graphObject_.HighLight( flag) highLighted.append(node) def __isItemVisible (self, item, canvas ): """ Returns 1 if the item is visible, 0 otherwise """ itemtype = canvas.type(item) if not itemtype in ['line', 'text']: if (canvas.itemcget(item, "outline" ) in ["", None]) and (canvas.itemcget(item, "fill") in ["", None]): return 0 else: return 1 else: #if itemtype == 'text': # print " +++ fill = ", canvas.itemcget(item, "fill" ) # print " +++ text = ", canvas.itemcget(item, "text" ) if canvas.itemcget(item, "fill") in ["", None]: return 0 else: return 1 return 1 def __dist ( self, x0, y0, x1, y1 ): """ calculates the distance between 2 points Added : 10 July 2002 JL """ return math.sqrt(abs((x0-x1)*(x0-x1)+(y0-y1)*(y0-y1))) def __point2Segment ( self, px, py, sx0, sy0, sx1, sy1): """ calculates distance from a point to a segment. I use the algorithm given in: http://geometryalgorithms.com/Archive/algorithm_0102/algorithm_0102.htm Added : 10 July 2002 JL """ def dot ( w, v): """ calculates the dot product of two vectors """ return w[0]*v[0]+w[1]*v[1] v = (sx1 - sx0, sy1 - sy0) # v = s[1]-[s0] w = (px - sx0, py - sy0) # w = P-s[0] c1 = dot(w,v) if ( c1 <= 0 ): return self.__dist(px, py, sx0, sy0) c2 = dot(v,v) if ( c2 <= c1 ): return self.__dist(px, py, sx1, sy1) b = c1 / c2 Pb = sx0 + b * v[0], sy0 + b * v[1] return self.__dist(px, py, Pb[0], Pb[1]) def find_visible_closest (self, x, y, canvas): """ Returns the closest item to (x, y) which is visible. If the item has 4 or more coordinates, then the distance of (x0,y0) to the segments defined by each 4 consecutives coordinates (x1, y1) (x2, y2) are calculated: Added : 10 July 2002 JL """ minDistance = 20000 # mimimum distance minDistItem = -1 # item with minimum distance items = canvas.find_overlapping(x-50, y-50, x+50, y+50) # tuple with all the items within a certain region segmin = None for item in items: # get the item with minimum distance if self.__isItemVisible(item, canvas): if canvas.type(item) == 'text': crd = canvas.bbox(item) else: crd = canvas.coords(item) ncoords = len(crd) if ncoords >=4: for i in range(0, ncoords-3, 2): if crd[i] == crd[i+2] and crd[i+1] == crd[i+3]: distance = self.__dist(crd[i], crd[i+1], x, y) # math.sqrt(abs((crd[i]-x)*(crd[i]-x)+(crd[i+1]-y)*(crd[i+1]-y))) else: distance = self.__point2Segment(x, y, crd[i], crd[i+1], crd[i+2], crd[i+3]) if distance < minDistance: segmin = crd[i], crd[i+1], crd[i+2], crd[i+3] minDistance = distance minDistItem = item else: distance = self.__dist(crd[0], crd[1], x, y) # math.sqrt(abs((crd[0]-x)*(crd[0]-x)+(crd[1]-y)*(crd[1]-y))) if distance < minDistance: segmin = crd[0], crd[1] minDistance = distance minDistItem = item #print " type = ", self.UMLmodel.type(item) #print " - coords = ", crd #print " - dist = ", distance #print "************************", minDistance, x, y return (minDistItem, ) def drag (self, UnUsed, wx, wy): """ Default action if in IDLEMODE. It starts a drag operation on the selected graphical object. """ x = self.UMLmodel.canvasx(wx) # converto to canvas coordinate system y = self.UMLmodel.canvasy(wy) self.initDragX, self.initDragY = x, y # store initial drag operation position #ct = self.UMLmodel.find_closest(x, y) # find the closest items ct = self.find_visible_closest(x, y, self.UMLmodel) # find the closest items tags = self.UMLmodel.gettags(ct) # get the tags if tags and tags[0] != 'current': # 1st. try the global constraints... res = self.ASGroot.preCondition(ASG.DRAG) # evaluate global pre-conditions if res: return self.constraintViolation(res) # contraint violation obj = VisualObj.Tag2ObjMap[tags[0]] res = obj.semanticObject.preCondition ( ASGNode.DRAG ) if res: return self.constraintViolation(res) # contraint violation self.ASGroot.preAction(ASG.DRAG) # execute global pre-actions obj.semanticObject.preAction ( ASGNode.DRAG ) # execute local pre-actions obj.select( tags[0], ct[0], x, y ) # select it obj.HighLight(1) # HighLight the selected things self.isMousePressed = 1 # set mouse pressed to 1 res = obj.semanticObject.postCondition (ASGNode.DRAG) # local post condition if res: self.undodrag(res, obj) # contraint violation res = self.ASGroot.postCondition (ASG.DRAG) # local post condition if res: self.undodrag(res, obj) # contraint violation self.ASGroot.postAction(ASG.DRAG) # execute global post-actions obj.semanticObject.postAction ( ASGNode.DRAG ) # execute local post-actions def undodrag(self, res, graphObj): """ undoes the beginning of a drag operation (due to a constraint failure) """ self.constraintViolation(res) # contraint violation self.isMousePressed = 0 # reset flag for mouse pressed graphObj.HighLight( 0) # turn highlighting off self.UMLmodel.dtag(ALL, "selected") # delete the 'selected' tag from the items def save (self, export = 0, fileName = None): """ Saves the model into disk """ # try the global constraints... res = self.ASGroot.preCondition(ASG.SAVE) # evaluate global pre-conditions if res: return self.constraintViolation(res) # if violated, show warning and do not save # try the local constraints... res = self.ASGroot.evaluateLocalPreCondition(ASG.SAVE) # evaluate global pre-conditions if res: return self.constraintViolation(res) # if violated, show warning and do not save res = self.checkModel() if res: return self.constraintViolation(res) # if violated, show warning and do not save if not fileName or fileName == "Nonamed": # if the fileName is not given, then ask for a new name fileName = tkFileDialog.asksaveasfilename(filetypes=[("Python files", "*.py")]) # ask for the file name if fileName: # if the user pressed ok... self.ASGroot.genCode(fileName, self.types, self.genGraphics, 1, self.GUIModelName, export, self.newTypes) # call the ASG method to save its contents # update status bar information... self.statusbar.event(StatusBar.MODEL, StatusBar.SAVE, fileName) if self.console: self.console.appendText('Saving model in file '+fileName) def loadTypes(self, listOfNewTypes): """ Adds the components of the 2nd paramater into self.typeList, wrapping them into an ATOM3TypeInfo and loads them in the types dictionary. listOfNewTypes is a list of tuples: ( 'user-friendly name', 'class name', tuple-with-parameters, may-edit-model) """ tl = self.typeList.getValue() # obtain the list of types... for newType in listOfNewTypes: # for each tuple in the list... obj = ATOM3TypeInfo(self) # wrap it into a ATOM3TypeInfo ufname, cname, params, medt = newType # unwrap the tuple exec "from "+cname+" import *\n" className = eval(cname) # obtain the class name objType = className() # create a new type object... obj.setValue (newType) # sets the value to the component... objType.createTypeGraph(self, obj.typeModel) # create the graph tl.append(obj) # add the element to the list if not newType in self.newTypes: # add the type in the list of newly added types... self.newTypes.append(newType) self.fillDictionaryWithTypes() # call the function to add new types in the dictionary def checkInSearchPath(self, dir): """ checks if the given directory is in the Python search path. If this is not the case, it adds the directory to the path """ currdir = os.getcwd() for apath in sys.path: if apath and apath[0] != '/' and apath[0] != '\\': # it is NOT an absolute path pathDir = currdir+apath else: pathDir = apath if pathDir == dir: return else: pass sys.path.append(dir) def open (self): """ opens a model from disk """ fileName = tkFileDialog.askopenfilename(filetypes=[("Python files", "*.py")]) dir, file = os.path.split(fileName) className = string.split (file, ".") # compose class name self.newfunction = None if className[0]: self.checkInSearchPath(dir) # first check if it has been loaded before, to force a recompilation if className[0] in sys.modules.keys(): # file has already been loaded del sys.modules[className[0]] # delete to force a reload exec "from "+className[0]+" import *\n" in self.__dict__, self.__dict__ if "loadedMMName" in self.__dict__.keys(): # if we have the meta-model name self.modelFunction = self.newfunction # pointer to the function to build the model if self.loadedMMName != self.GUIModelName: # if we do not have the meta-model opened... # if current model is empty, close meta-model (we don't need it any more) if self.ASGroot and self.ASGroot.isEmpty(): nmm = len(self.openMetaModels.getValue()) self.openMetaModels.setValue([]) self.removeMetaModels (nmm) self.openMetaModel(self.loadedMMName, 0, 1) elif not self.ASGroot: # no root, self.openMetaModel(self.loadedMMName, 0, 1) else: self.openMetaModel(self.loadedMMName, 1, 0) self.newfunction = self.modelFunction # restore the function if "loadedTypes" in self.__dict__.keys(): # look for newly defined or loaded types (loadedTypes should be a list) self.loadTypes(self.loadedTypes) # load the new types... del self.loadedTypes try: self.newfunction(self, self.ASGroot) except TypeError: tkMessageBox.showerror( "Couldn't open model!", "Selected file "+file+" does not contain a valid model", parent = self ) else: # if we have loaded successfully a file, then update status bar... self.statusbar.event(StatusBar.MODEL, StatusBar.LOAD, fileName) # update status bar if self.console: self.console.appendText('Loading model from file '+fileName) self.mode = self.IDLEMODE def editclass(self, x, y): """ Edits the nearest class that can be found in the canvas """ #ct = self.UMLmodel.find_closest(x, y) ct = self.find_visible_closest(x, y, self.UMLmodel) tags = self.UMLmodel.gettags(ct) if tags: # try the global constraints... if not self.editGGLabel: res = self.ASGroot.preCondition(ASG.EDIT) if res: # global constraint do not hold! self.constraintViolation(res) return obj = VisualObj.Tag2ObjMap[tags[0]] if not self.editGGLabel: res = obj.semanticObject.preCondition ( ASGNode.EDIT ) # Local preconditions... if res: self.constraintViolation(res) return self.ASGroot.preAction(ASGNode.EDIT) obj.semanticObject.preAction ( ASGNode.EDIT ) ma = ATOM3TypeDialog(self, obj.semanticObject, 0, (self.setEditGGLabel ,None) ) if ma.result_ok: # update ATOM3Appearance with the keyword change... # check that the keyword of the entity is still unique, if it has one. if obj.semanticObject.keyword_: for element in self.ASGroot.listNodes[obj.semanticObject.__class__.__name__]: if obj.semanticObject != element and obj.semanticObject.keyword_.toString() == element.keyword_.toString(): # different elements and same keyword, so invalidate the previous editing # it is not an error if both are None and we are in a graph-grammar rule if obj.semanticObject.keyword_.isNone() and element.keyword_.isNone() and self.editGGLabel: pass else: self.constraintViolation(("Duplicate keyword: "+element.keyword_.toString(),"")) # we should undo the editing obj.semanticObject.copy(ma.previousObject) # restore old information self.editclass(x, y) return if not self.editGGLabel: res = obj.semanticObject.postCondition ( ASGNode.EDIT ) # if we are not in a GG rule if res: self.constraintViolation(res) # Present an error message obj.semanticObject.copy(ma.previousObject) # restore old information self.editclass(x, y) # edit again! return if not self.editGGLabel: res = self.ASGroot.postCondition(ASGNode.EDIT) if res: self.constraintViolation(res) # Present an error message obj.semanticObject.copy(ma.previousObject) # restore old information self.editclass(x, y) # edit again! return obj.semanticObject.postAction ( ASGNode.EDIT ) self.ASGroot.postAction(ASGNode.EDIT) # Modify the visual attributes for attr in obj.semanticObject.generatedAttributes.keys(): types = obj.semanticObject.generatedAttributes[attr] for t in types: if t == 'ATOM3Appearance': # if it is appearance appAttr = obj.semanticObject.getAttrValue( attr ) if obj.semanticObject.keyword_: appAttr.setValue( (obj.semanticObject.keyword_.toString(), ) ) # modify also the visual attributes for visualAttr in obj.attr_display.keys(): if self.editGGLabel== ASG.INLHS and obj.semanticObject.__dict__[visualAttr].isNone(): obj.ModifyAttribute(visualAttr, "") elif self.editGGLabel== ASG.INRHS: # Modified 22 July 2002, JL if obj.semanticObject.GGset2Any.has_key(visualAttr): if obj.semanticObject.GGset2Any[visualAttr].Copy.getValue()[1]: obj.ModifyAttribute(visualAttr, "") elif not obj.semanticObject.GGset2Any[visualAttr].Specify.getValue()[4] in ["", "\n", None]: obj.ModifyAttribute(visualAttr, "") else: obj.ModifyAttribute(visualAttr, obj.semanticObject.__dict__[visualAttr].toString(25,5)) # set to 25 by JL on 16 July 2002 else: obj.ModifyAttribute(visualAttr, obj.semanticObject.__dict__[visualAttr].toString(25,5)) # set to 25 by JL on 16 July 2002 # update status bars... if self.editGGLabel : self.statusbar.event(StatusBar.TRANSFORMATION, StatusBar.MODIFY) else: self.statusbar.event(StatusBar.MODEL, StatusBar.MODIFY) self.mode = self.IDLEMODE def modelAttributes(self): """ edits model attributes, including global constraints... """ ma = ATOM3TypeDialog(self, self.ASGroot ) def constraintViolation(self, res): """ A constraint violation has occurred, and a message has to be given. The message is the 1st component of the tuple 'res'. The 2nd component is the object to be highlighted. """ if res[1] and (type(res[1])!= StringType): if issubclass(res[1].__class__, VisualObj): res[1].HighLight(1) elif issubclass( res[1].__class__, ASGNode): res[1].graphObject_.HighLight(1) tkMessageBox.showwarning("constraint violation!",res[0],parent = self) if res[1] and type(res[1])!= StringType: if issubclass(res[1].__class__, VisualObj): res[1].HighLight(0) elif issubclass( res[1].__class__,ASGNode): res[1].graphObject_.HighLight(0) return 0 def writeSetValue(self, f, obj, objName, indent, deep = 0): """ writes in the file 'f' the value of obj (an ATOM3Type). The object name must be objName. This is a 'visitor' method. """ f.write(indent+objName+"="+obj.getTypeName()+"()\n") # write the constructor if obj.getTypeName() == 'ATOM3String': # if it is a string, enclose between quotes f.write(indent+objName+".setValue('"+str(obj.getValue())+"')\n") elif obj.getTypeName() == 'ATOM3List': # if it is a list f.write(indent+"objlist"+str(deep)+" =[]\n") value = obj.getValue() for ob in value: self.writeSetValue(f, ob, objName+str(deep+1), indent, deep+1) # write object value f.write(indent+"objlist"+str(deep)+".append("+objName+")\n") f.write(indent+objName+".setValue(objlist"+str(deep)+")\n") actFlags = obj.getActionFlags() # get the ATOM3List actFlags = actFlags[:3].append(0) # eliminate the meta-flag f.write(indent+objName+".setActionFlags("+str(actFlags)+")\n") else: f.write(indent+objName+"="+obj.getTypeName()+"()\n") # write the constructor f.write(indent+objName+".setValue("+str(obj.getValue())+")\n") def findKeywordAndIcons(self, f, item, counter): """Searches for the keyword attribute and icons. Writes the keyword (if any). This ensures that the keyword is written first. - f is the file - item is an instance of ATOM3Attribute - counter: the order of the attribute This is a 'visitor' method to be called by visitorOnAttributes, for code generation purposes. """ value = item.getValue() # (attrName, typeID, initialValue|None, isKey, directEditing) if value[3][1] == 1: # only write to file if it is the keyword. item.initialValue.writeConstructor2File( f, ' ', 'self.'+str(value[0]), 0, 1 ) f.write(' self.keyword_= self.'+str(value[0])+'\n') self.theKeyword = str(value[0]) if value[1] == "Appearance": # if it has an attribute of type appearance, write it down. If it has an appearance... self.hasAppearance = 1 # ... it must also have a keyword. attribType = self.types[str(value[1])][0].__name__ if attribType == 'ATOM3List': if item.initialValue: itl = item.initialValue.itemType.__name__ # get the initial value... if itl == "ATOM3Appearance": self.hasAppearance = 1 elif itl in ["ATOM3List", "ATOM3Attribute"]: items = item.initialValue.getValue() # get items, and look for 'Appearances' for element in items: if self.findIcon(element): return def findIcon(self, item): """ Sets the flag 'self.hasAppearance' to 1 if the item is an ATOM3Appearance. Proceeds recursively if the item is an ATOM3Attibute or a list. This is an auxiliary method for code generation. """ if item.getTypeName() == "ATOM3Appearance": # check if it is an appearance... self.hasAppearance = 1 return 1 elif item.getTypeName() == "ATOM3List": theType = item.itemType.__name__ # get the Type... if theType == "ATOM3Appearance": self.hasAppearance = 1 return 1 elif theType in ["ATOM3List", "ATOM3Attribute"]: items = item.getValue() # get items, and look for 'Appearances' for element in items: if self.findIcon(element): return 1 return 0 elif item.getTypeName() == "ATOM3Attribute": if item.initialValue: return self.findIcon(item.initialValue) # check the initial value return 0 return 0 def writeCreation(self, f, item, counter): """writes in f the statements necessary to create the object. Does not write the keyword, because the previous function was supposed to do it. - f is the file - item is an instance of ATOM3Attribute - counter: the order of the attribute These are 'visitor' methods called by visitorOnAttributes for code generation purposes. """ value = item.getValue() # (attrName, typeID, initialValue|None, isKey, directEditing) if value[3][1] != 1: # only write it if it is not a keyword. item.initialValue.writeConstructor2File( f, ' ', 'self.'+str(value[0]), 0, 1 ) if value[1] == "Appearance": # if it has an attribute of type appearance, write it down. If it has an appearance... self.hasAppearance = 1 # ... it must also have a keyword. def writeGeneratedDictionary(self, f, item, counter): """ write in f the contents of the dicitionary of generated attributes. These are 'visitor' methods called by visitorOnAttributes for code generation purposes. """ value = item.getValue() if counter: f.write(",\n ") f.write( "'"+str(value[0])+"': ('ATOM3"+str(value[1])+"', )") def writeRealOrderList(self, f, item, counter): """ write in f the contents of the list with the order of generated attributes. These are 'visitor' methods called by visitorOnAttributes for code generation purposes. """ value = item.getValue() if counter: f.write(",") f.write( "'"+str(value[0])+"'") def genImport(self, f, item, counter): """ adds in the list importedTypes the necessary types to be imported. These are 'visitor' methods called by visitorOnAttributes for code generation purposes. """ value = item.getValue() attribType = self.types[str(value[1])][0].__name__ if not attribType in self.importedTypes: self.importedTypes.append(attribType) # if it is a list, import the list' type if attribType == 'ATOM3List': if item.initialValue: itl = item.initialValue.itemType.__name__ if not itl in self.importedTypes: self.importedTypes.append(itl) if itl == "ATOM3Attribute": self.addAllTypes2List(self.importedTypes) elif itl == "ATOM3List": # no look for initial items, and import each one type... initialItems = item.initialValue.getValue() # get a list of items... for item in initialItems: self.addItemType2List(item, self.importedTypes) if initialItems == []: # no initial items, so add the default type for lists (attributes) if not "ATOM3Attribute" in self.importedTypes: self.importedTypes.append("ATOM3Attribute") # if it is of type ATOM3Attribute, then add all the available types... self.addAllTypes2List(self.importedTypes) else: if not "ATOM3Attribute" in self.importedTypes: self.importedTypes.append("ATOM3Attribute") # if it is of type ATOM3Attribute, then add all the available types... self.addAllTypes2List(self.importedTypes) def addAllTypes2List(self, list): """ Auxiliary method for genImport. adds the name of all the available types to the list """ for typeName in self.types.keys(): tupleType = self.types[typeName] name = tupleType[0].__name__ if not name in list: list.append(name) def addItemType2List(self, item, list): """ Auxiliary method for genImport. adds the type of item to the list (if it is not present yet) """ theType = item.getTypeName() if not theType in list: list.append(theType) if theType == "ATOM3List": # check for its initial value, and repeat for each item # import the list' type: theType = item.itemType.__name__ if not theType in list: list.append(theType) if theType == "ATOM3Attribute" : # check for it is an Attribute, we have to add each available type... self.addAllTypes2List(list) def visitorOnAttributes(self, f, UMLobject, function): """ iterates over the attributes of type ATOM3Attribute of the object UMLobject, performing a certain function """ counter = 0 # an auxiliary counter for attr in UMLobject.generatedAttributes.keys(): type = UMLobject.generatedAttributes[attr] # A tuple with the types... if type[0] == 'ATOM3Attribute': function(f, UMLobject.getAttrValue(attr), counter) # perform function counter = counter + 1 # increment counter elif type[0] == 'ATOM3List' and type[1] == 'ATOM3Attribute': # A list of generative elements... items = UMLobject.getAttrValue(attr).getValue() # obtain a list of generative elements for item in items: # look over the array function(f, item, counter) counter = counter + 1 return counter def writeActionConstraint (self, file, value, which): """ writes part of the function to evaluate local constraints These are 'visitor' methods called by visitorOnConstraints for code generation purposes. """ listAct, selAct = value[3] listKnd, selKnd = value[2] if listKnd[selKnd] == which: # iterate on the specified event... file.write(" if actionID == ") conta = 0 writed = 0 for event in selAct: if event == 1: if not writed: file.write("self."+listAct[conta]) else: file.write(" or actionID == self."+listAct[conta]) writed = 1 conta = conta + 1 file.write(":\n") file.write(" res = self."+value[0]+"(params)\n") file.write(" if res: return res\n") def writeConstraintCode (self, file, value, unUsed): """ writes the constraint code. These are 'visitor' methods called by visitorOnAttributes for code generation purposes. """ file.write (" def "+value[0]+"(self, params):\n") file.write (" "+string.replace(value[4],'\n', '\n ')) file.write ("\n\n") def visitorOnConstraints (self, which, file, UMLobject, function ): """ Generates code for the constraints. In a 'visitor' pattern way. """ # find a list of constraints, or a single constraint generator for attr in UMLobject.generatedAttributes.keys(): type = UMLobject.generatedAttributes[attr] # A tuple with the types... if type[0] == 'ATOM3Constraint': value = UMLobject.getAttrValue(attr).getValue() # obtain the value function(file, value, which) elif type[0] == 'ATOM3List' and type[1] == 'ATOM3Constraint': items = UMLobject.getAttrValue(attr).getValue() for item in items: value = item.getValue() function(file, value, which) def writeDirectionalCheck(self, file, value, counter, direct): """ Generates type checking for the connection direction I THINK THIS METHOD IS NOT USED ANY MORE. """ # value is a tuple with (className, direction, minValue, maxValue) className, direction, minVal, maxVal = value # unpack the values if direction[1] == direct: # check the constraint direction if self.writedDirection == 0: # if it is the 1st., begin the if file.write(" if ") self.writedDirection = 1 else: file.write(" and ") file.write("( last.getClass()!='"+value[0]+"') ") def writeObjectTypeCheck(self, file, value, counter): """ Generates the type checking for the connection I THINK THIS METHOD IS NOT USED ANY MORE. """ if counter == 0: # if it is the first cardinality, add some preliminary code file.write(" if selfPosition == 'SOURCE':\n") file.write(" last=self.out_connections_[len(self.out_connections_)-1]\n") file.write(" else:\n") file.write(" last=self.in_connections_[len(self.in_connections_)-1]\n") file.write(" if ") else: file.write(" and ") file.write("( last.getClass()!='"+value[0]+"') ") def writeSoftCardinalityCheck(self, file, value, counter): """ Generates soft (not taking into account minimum values) cardinality checking I THINK THIS METHOD IS NOT USED ANY MORE. """ # value is a tuple (, , , ) objectClass, direction, minValue, maxValue = value # unpack ATOM3Connection value file.write(" counter = 0\n") # cardinality counter if direction[1] == 0: # From Entity TO relationship file.write(" for item in self.in_connections_:\n") # WE ARE A RELATIONSHIP else: file.write(" for item in self.out_connections_:\n") file.write(" if '"+ objectClass +"'== item.getClass(): counter = counter+1\n") if not maxValue in ["n", "N", "m", "M"] : file.write(" if counter > "+maxValue+":\n") file.write(" return ( 'Number of "+objectClass+" objects exceeded!', '')\n") def writeHardCardinalityCheck(self, file, value, counter): """ Generates hard (taking into account minimum values) cardinality checking I THINK THIS METHOD IS NOT USED ANY MORE. """ # value is a tuple (, , , ) objectClass, direction, minValue, maxValue = value # unpack ATOM3Connection value file.write(" counter = 0\n") if direction[1] == 0: # From Entity TO relationship file.write(" for item in self.in_connections_:\n") # WE ARE A RELATIONSHIP else: file.write(" for item in self.out_connections_:\n") file.write(" if '"+ objectClass +"'== item.getClass(): counter = counter+1\n") if not maxValue in ["n", "N", "m", "M"] : file.write(" if counter > "+maxValue+":\n") file.write(" return ( 'Number of "+objectClass+" objects exceeded!', '')\n") if not minValue in ["n", "N", "m", "M"]: file.write(" if counter < "+minValue+":\n") file.write(" return ( 'Number of "+objectClass+" objects insuficient("+str(minValue)+" are needed)!', '')\n") else: file.write(" if counter == 0:\n") file.write(" return ( 'Number of "+value[0]+" objects insuficient("+str(minValue)+" are needed)!', '')\n") def visitorOnCardinality (self, file, UMLobject, function, param = None ): """ Generates constraints from the cardinality attributes found, in a 'visitor pattern way' """ counter = 0 for attr in UMLobject.generatedAttributes.keys(): type = UMLobject.generatedAttributes[attr] # A tuple with the types... if type[0] == 'ATOM3Connection': value = UMLobject.getAttrValue(attr).getValue() # obtain the value if param: function(file, value, counter, param ) else: function(file, value, counter ) counter = counter + 1 elif type[0] == 'ATOM3List' and type[1] == 'ATOM3Connection': items = UMLobject.getAttrValue(attr).getValue() for item in items: value = item.getValue() if param != None: function(file, value, counter, param) else: function(file, value, counter) counter = counter+1 return counter def genASGCode(self, cardConstObjects): """ Generates Python code for the ASGroot node """ fileName = "ASG_"+self.ASGroot.keyword_.toString()+".py" if self.console: self.console.appendText('Generating file '+fileName+' in directory '+self.codeGenDir) f = open( self.codeGenDir+"/"+fileName, "w+t" ) f.write("# __"+ fileName +"_____________________________________________________\n") f.write("from ASG import *\n") f.write("from ATOM3Type import *\n") self.importedTypes = [] # list where the necessary types will be placed self.visitorOnAttributes( f, self.ASGroot, self.genImport) # now write each type of the list to the file... for typename in self.importedTypes: f.write("from "+typename+" import *\n") f.write("class ASG_"+self.ASGroot.keyword_.toString()+"(ASG, ATOM3Type):\n\n") # generate class definition f.write(" def __init__(self, parent= None, ASGroot = None):\n") # declare init method metaModelName = self.ASGroot.keyword_.toString() # get the metamodel name f.write(" ASG.__init__(self, '"+metaModelName+"', ASGroot, ['ASG_"+self.ASGroot.keyword_.toString()+"'") # add also the own name (for hierarchical modelling) # add the node types... counter = 0 for nodeType in self.ASGroot.nodeTypes: # for each node type for UMLobject in self.ASGroot.listNodes[nodeType]: # for each object of f.write(" ,") f.write("'"+UMLobject.keyword_.toString()+"'") counter = counter + 1 f.write("])\n\n") f.write(" ATOM3Type.__init__(self)\n") self.genASGNodeCode(f, self.ASGroot, 1) # != None -> globalModel f.write("\n\n") f.close() def genCodeFor ( self, entity, objsWithCardConstraints ): """ Generates Python code for the entity """ # check if not entity.keyword_: # entity does not have a keyword, raise an error, we cannot generate code tkMessageBox.showerror( "Error: entity has no keyword!", "Entity does not have a keyword", parent = self ) return # generate code for the ATOM3Links, because a graphical file must be generated for attr in entity.generatedAttributes.keys(): type = entity.generatedAttributes[attr] # A tuple with the types... if type[0] == 'ATOM3Link': # ey! an ATOM3Link has been found... at3link = entity.getAttrValue(attr) if at3link and not at3link.isNone(): # if it has some value... entity.getAttrValue(attr).genGraphicalFile( self.codeGenDir ) else: tkMessageBox.showerror( "Error: entity has no graphical appearance!", "Entity "+entity.keyword_.toString()+" does not have a graphical appearance", parent = self ) return fileName = entity.keyword_.toString()+".py" # Prepare file name, with the keyword if self.console: self.console.appendText('Generating file '+fileName+' in directory '+self.codeGenDir) f = open( self.codeGenDir+"/"+ fileName, "w+t") # open file name and print header f.write("# __"+ fileName +"_____________________________________________________\n") f.write("from ASGNode import *\n\n") # generate imports f.write("from ATOM3Type import *\n\n") # generate imports self.importedTypes = [] # list where the necessary types will be placed self.visitorOnAttributes( f, entity, self.genImport) # generate import for ATOM3Types # now write each type of the list to the file... for typename in self.importedTypes: f.write("from "+typename+" import *\n") try: # see if we have associated a graphical attribute fgd = open( self.codeGenDir+"/"+"graph_"+entity.keyword_.toString()+".py") # try to open the file with the graphical class except IOError: hasGraph = 0 # if we should generate graphics, then give a warning! if self.option.GenerateGraphics.getValue()[1] == 0: # generate == Yes tkMessageBox.showwarning( "Warning: Undefined icon!", "Entity '"+entity.keyword_.toString()+"' does not have an icon", parent = self ) else: f.write("from graph_"+entity.keyword_.toString()+" import *\n") hasGraph = 1 fgd.close() f.write("class "+entity.keyword_.toString()+"(ASGNode, ATOM3Type):\n\n") # generate class definition f.write(" def __init__(self, parent = None):\n") # declare init method f.write(" ASGNode.__init__(self)\n") f.write(" ATOM3Type.__init__(self)\n") if hasGraph: # then write down the class name f.write(" self.graphClass_ = graph_"+entity.keyword_.toString()+"\n") self.genASGNodeCode(f, entity) # call method to generate the rest of the code f.write("\n\n") f.close() def genCode(self): """ Generates Python code from the model information """ if not self.existGenerativeAttributes(): # no code to generate, model is not generative! tkMessageBox.showerror( "No code can be generated", "Trying to produce code from a non-generative model", parent = self ) return hasGraph = 0 # flag that indicates if we have a graphical attribute cardConstObjects = [] if self.ASGroot.keyword_: if self.console: self.console.appendText('Generating code for model '+self.ASGroot.keyword_.toString()) else: if self.console: self.console.appendText('Generating code for model.') for nodeType in self.ASGroot.nodeTypes: # for each node type for UMLobject in self.ASGroot.listNodes[nodeType]: # for each object of any type self.genCodeFor (UMLobject, cardConstObjects) # Generate code for this particular entity # in cardConstObjects, we are storing the objects with cardinality constraints # see first if we have generative attributes... self.genASGCode(cardConstObjects) # generate code for the ASG node self.genButtons() # generate the file for the syntax actions # now generate the file with the GUI model... self.genButtonsModel() def existGenerativeAttributes(self): """ Returns 1 if the actual model has some generative attributes... """ # first look in the ASGroot if self.ASGroot.hasGenerativeAttributes(): return 1 # now look in each entity of the model... for entype in self.ASGroot.listNodes.keys(): for entity in self.ASGroot.listNodes[entype]: if entity.hasGenerativeAttributes(): return 1 return 0 def genButtonsModel(self): """ Generates a model in the "Buttons" formalism with the button layout and associated actions. It is done by executing the graph grammar createButtons. """ oldGenGraphics = self.genGraphics self.genGraphics = 0 nameButtonBar = self.ASGroot.keyword_.toString() if self.console: self.console.appendText('Generating file '+nameButtonBar+'.py in directory '+self.codeGenDir+' (User Interface file)') file = open( self.codeGenDir+"/"+nameButtonBar+".py", "w+t" ) self.GraphGrammars = [ createButtons(self)] self.grs = GraphRewritingSys(self, self.GraphGrammars, self.ASGroot ) self.grs.evaluate(stepByStep = 0, moveEntities = 0, execute = self.grs.SEQ_RANDOM, graphics = 0) # no graphics (the canvas with the model is closed!) self.genGraphics = oldGenGraphics def genButtons(self): """ generates the file which has the actions to create the defined entities. """ # get the name from the model name... nameButtonBar = self.ASGroot.keyword_.toString()+"_MM" if self.console: self.console.appendText('Generating file '+nameButtonBar+'.py in directory '+self.codeGenDir+' (Meta-model file)') file = open( self.codeGenDir+"/"+nameButtonBar+".py", "w+t" ) # see if we have to import graph_ASG_, or graph_ASG_UMLmetaMetaModel # try to open the file to see if it exists grFName = 'graph_ASG_'+self.ASGroot.keyword_.toString() # check if a drawing was made, and if not, use the default drawing # try to open it... try: f = open (grFName+'.py', "r+t") except IOError: # not found, so use default drawing file grFName = 'graph_ASG_ERmetaMetaModel' else: f.close() file.write('from ASG_'+self.ASGroot.keyword_.toString()+' import *\n') # the class of root node file.write('from '+grFName+' import *\n') # the class of the graphic for the root node file.write('from Tkinter import *\n') file.write('from ATOM3TypeInfo import *\n') file.write('from ATOM3String import *\n') file.write('from StatusBar import *\n') file.write('from ATOM3TypeDialog import *\n\n') # import all the ASGNodes... for UMLclass in self.ASGroot.nodeTypes: for obj in self.ASGroot.listNodes[UMLclass]: file.write('from '+obj.keyword_.toString()+' import *\n') # check if there exist the file with the graphical class nameFile = "graph_"+obj.keyword_.toString()+".py" try: f = open (nameFile, "r+t") except IOError: pass else: f.close () file.write('from graph_'+obj.keyword_.toString()+' import *\n') # generate function "createNewASGroot(self):" file.write('def createNewASGroot(self):\n') file.write(' return ASG_'+self.ASGroot.keyword_.toString()+'(self, None)\n\n') # generate function "createModelMenu" file.write('def createModelMenu(self, modelMenu):\n') file.write(' "Creates a customized Model Menu for the actual formalism"\n') file.write(' modelMenu = Menu(self.mmtoolMenu, tearoff=0)\n') for UMLclass in self.ASGroot.nodeTypes: for obj in self.ASGroot.listNodes[UMLclass]: file.write(' modelMenu.add_command(label="new '+obj.keyword_.toString()+'", command=lambda x=self: x.newModes'+obj.keyword_.toString()+'(x) )\n') # # generate the function setConnectivity self.genSetConnectivity(file) # generate the functions 'createNew(self, wherex, wherey): for UMLclass in self.ASGroot.nodeTypes: for obj in self.ASGroot.listNodes[UMLclass]: file.write('def createNew'+obj.keyword_.toString()+'(self, wherex, wherey, screenCoordinates = 1):\n') file.write(' self.fromClass = None\n') file.write(' self.toClass = None\n') file.write(' # try the global constraints...\n') file.write(' res = self.ASGroot.preCondition(ASG.CREATE)\n') file.write(' if res:\n') file.write(' self.constraintViolation(res)\n') file.write(' self.mode=self.IDLEMODE\n') file.write(' return\n\n') file.write(' new_semantic_obj = '+obj.keyword_.toString()+'(self)\n') file.write(' ne = len(self.ASGroot.listNodes["'+obj.keyword_.toString()+'"])\n') file.write(' if new_semantic_obj.keyword_:\n') file.write(' new_semantic_obj.keyword_.setValue(new_semantic_obj.keyword_.toString()+str(ne))\n') file.write(' if screenCoordinates:\n') file.write(' new_obj = graph_'+obj.keyword_.toString()+'(self.UMLmodel.canvasx(wherex), self.UMLmodel.canvasy(wherey), new_semantic_obj)\n') file.write(' else: # already in canvas coordinates\n') file.write(' new_obj = graph_'+obj.keyword_.toString()+'(wherex, wherey, new_semantic_obj)\n') file.write(' new_obj.DrawObject(self.UMLmodel, self.editGGLabel)\n') file.write(' self.UMLmodel.addtag_withtag("'+obj.keyword_.toString()+'", new_obj.tag)\n') file.write(' new_semantic_obj.graphObject_ = new_obj\n') file.write(' self.ASGroot.addNode(new_semantic_obj)\n') file.write(' res = self.ASGroot.postCondition(ASG.CREATE)\n') file.write(' if res:\n') file.write(' self.constraintViolation(res)\n') file.write(' self.mode=self.IDLEMODE\n') file.write(' return\n\n') file.write(' res = new_semantic_obj.postCondition(ASGNode.CREATE)\n') file.write(' if res:\n') file.write(' self.constraintViolation(res)\n') file.write(' self.mode=self.IDLEMODE\n') file.write(' return\n\n') file.write(' self.mode=self.IDLEMODE\n') file.write(' if self.editGGLabel :\n') file.write(' self.statusbar.event(StatusBar.TRANSFORMATION, StatusBar.CREATE)\n') file.write(' else:\n') file.write(' self.statusbar.event(StatusBar.MODEL, StatusBar.CREATE)\n') file.write(' return new_semantic_obj\n') # generate also the function "createNewModel" to allow hierarchical Modelling file.write('def createNew_Model(self, wherex, wherey, screenCoordinates = 1):\n') file.write(' self.toClass = None\n') file.write(' self.fromClass = None\n') file.write(' new_semantic_obj = ASG_'+self.ASGroot.keyword_.toString()+'(self)\n') file.write(' ne = len(self.ASGroot.listNodes["ASG_'+self.ASGroot.keyword_.toString()+'"])\n') file.write(' if new_semantic_obj.keyword_:\n') file.write(' new_semantic_obj.keyword_.setValue(new_semantic_obj.keyword_.toString()+str(ne))\n') file.write(' if screenCoordinates:\n') file.write(' new_obj = '+grFName+'(self.UMLmodel.canvasx(wherex), self.UMLmodel.canvasy(wherey), new_semantic_obj)\n') file.write(' else: # already in canvas coordinates\n') file.write(' new_obj = '+grFName+'(wherex, wherey, new_semantic_obj)\n') file.write(' new_obj.DrawObject(self.UMLmodel, self.editGGLabel)\n') file.write(' self.UMLmodel.addtag_withtag("ASG_'+self.ASGroot.keyword_.toString()+'", new_obj.tag)\n') file.write(' new_semantic_obj.graphObject_ = new_obj\n') file.write(' self.ASGroot.addNode(new_semantic_obj)\n') file.write(' self.mode=self.IDLEMODE\n') file.write(' if self.editGGLabel :\n') file.write(' self.statusbar.event(StatusBar.TRANSFORMATION, StatusBar.CREATE)\n') file.write(' else:\n') file.write(' self.statusbar.event(StatusBar.MODEL, StatusBar.CREATE)\n') file.write(' return new_semantic_obj\n') # generate fillTypesInformation function file.write('def fillTypesInformation(self):\n') file.write(' objs = []\n') itemList = self.typeList.getValue() # obtain the item list for item in itemList: # obtain the value (ATOM3TypeInfo) of each item file.write(' obj = ATOM3TypeInfo(self)\n') file.write(' params = []\n') value = item.getValue() for param in value[2]: # if we have parameters file.write(' param = ATOM3String("'+param.toString()+'")\n') file.write(' params.append(param)\n') file.write(' obj.setValue(("'+value[0]+'", "'+value[1]+'", params, '+str(value[3])+' ))\n') file.write(' objs.append(obj)\n') file.write(' self.typeList.setValue(objs)\n\n') def genSetConnectivity(self, file): """ Generates the setConnectivity function in the file. """ file.write('def setConnectivity(self):\n') indent = ' ' # set the indentation to be used in the function self.__genConnectivityMap(file, indent) file.write(indent+'\n') self.__genCardinalityTable(file, indent) file.write(indent+'\n') self.__genEntitiesInMetaModel(file, indent) file.write(indent+'\n') def __genConnectivityMap(self, file, indent): """ Generates code in the file to generate the 'ConnectivityMap' dictionary """ entities = {} connections = [] # this will be a list with all the entities with connection possibilities... for UMLclass in self.ASGroot.nodeTypes: # build the dicionary, search for 'entities' for obj in self.ASGroot.listNodes[UMLclass]: hasConns = 0 for attr in obj.generatedAttributes.keys(): # look for ATOM3Connections... if obj.generatedAttributes[attr][0] == 'ATOM3Connection' or (obj.generatedAttributes[attr][0] == 'ATOM3List' and obj.generatedAttributes[attr][1] == 'ATOM3Connection'): hasConns = 1 break if hasConns: connections.append(obj) entities[obj.keyword_.toString()] = {} # initialize dictionary to void for ent1 in entities.keys(): for ent2 in entities.keys(): entities[ent1][ent2] = [] for obj in connections: # for each element with connections... objName = obj.keyword_.toString() # get its name for attr in obj.generatedAttributes.keys(): # look for ATOM3Connections... if obj.generatedAttributes[attr][0] == 'ATOM3List' and obj.generatedAttributes[attr][1] == 'ATOM3Connection': lc = obj.getAttrValue(attr).getValue() # get the list of connections for conn1 in lc: # for each connection... name1, direc1, min1, max1 = conn1.getValue() # unwrap it lc2 = []+lc lc2.remove(conn1) if name1 in entities.keys(): for conn2 in lc2: name2, direc2, min2, max2 = conn2.getValue() if name2 in entities[name1].keys() and name2 in entities.keys() and direc1 != direc2: ntuple = (objName, "self.createNew"+objName) if direc1[1] == 1: if not ntuple in entities[name1][name2]: entities[name1][name2].append(ntuple) else: if not ntuple in entities[name2][name1]: entities[name2][name1].append(ntuple) # now, write the dictionary in the file... for name1 in entities.keys(): file.write(indent+"self.ConnectivityMap['"+name1+"']={") outcont = 0 for name2 in entities[name1].keys(): if outcont > 0: file.write("\n"+indent+" ,'"+name2+"': [") else: file.write("\n"+indent+" '"+name2+"': [") outcont = outcont + 1 count = 0 for element in entities[name1][name2]: if count > 0: file.write(", ") file.write("( '"+element[0]+"', "+element[1]+")") count = count + 1 file.write("]") file.write(" }\n") def __genEntitiesInMetaModel(self, file, indent): """ Generates code in file to write the 'entitiesInMetaModel' list """ metaModelName = self.ASGroot.name.toString() file.write(indent+"self.entitiesInMetaModel['"+metaModelName+"']=[") counter = 0 for entity1 in self.ASGroot.nodeTypes: for obj1 in self.ASGroot.listNodes[entity1]: if counter > 0: file.write(", ") file.write ('"'+obj1.keyword_.toString()+'"') counter = counter + 1 file.write("]\n\n") def __genCardinalityTable(self, file, indent): """ Generates code in file to write the 'CardinalityTable' dictionary """ cardTable = {} # in this function we also include the filling of the dictionary self.CardinalityTable... for entity1 in self.ASGroot.nodeTypes: for obj1 in self.ASGroot.listNodes[entity1]: name1 = obj1.keyword_.toString() cardTable[name1] = {} for entity2 in self.ASGroot.nodeTypes: for obj2 in self.ASGroot.listNodes[entity2]: name2 = obj2.keyword_.toString() cardTable[name1][name2] = [] # Initialize to None for UMLclass in self.ASGroot.nodeTypes: # build the dicionary, search for 'entities' for obj in self.ASGroot.listNodes[UMLclass]: objName = obj.keyword_.toString() # get the entity name for attr in obj.generatedAttributes.keys(): # look for ATOM3Connections... if obj.generatedAttributes[attr][0] == 'ATOM3List' and obj.generatedAttributes[attr][1] == 'ATOM3Connection': lc = obj.getAttrValue(attr).getValue() # get the list of connections for conn1 in lc: name1, direc1, min1, max1 = conn1.getValue() cardTable[objName][name1].append((min1, max1, direc1[0][direc1[1]])) for UMLclass in self.ASGroot.nodeTypes: for obj in self.ASGroot.listNodes[UMLclass]: name1 = obj.keyword_.toString() file.write(indent+"self.CardinalityTable['"+name1+"']={") count = 0 for UMLclass1 in self.ASGroot.nodeTypes: for obj2 in self.ASGroot.listNodes[UMLclass1]: name2 = obj2.keyword_.toString() if count > 0: file.write("\n"+indent+" ,'"+name2+"': "+ str(cardTable[name1][name2])) else: file.write("\n"+indent+" '"+name2+"': "+ str(cardTable[name1][name2])) count = count + 1 file.write(" }\n") def editTypes(self): """ Opens a dialog to edit the types available in the session. If the user creates a new one, then calls the graph grammar to generate code for the type. """ ma = ATOM3TypeDialog(self, self.typeList, ATOM3TypeDialog.OPEN )# edit types... # generate code for the edited type... newTypes = self.typeList.getValue() # obtain the list of existing types oldTypes = [] for name in self.types.keys(): className = str(self.types[name][0]) oldTypes.append(className[string.rfind(className,".")+1:]) newTypesNames = [] for type in newTypes: # search for new defined types typeName = type.getValue()[1] newTypesNames.append(typeName) if not typeName in oldTypes: # This is a new Type! self.GraphGrammars = [ TypeCodeGen(self)] grs = GraphRewritingSys(self, self.GraphGrammars, type.typeModel ) grs.evaluate(stepByStep = 0, moveEntities = 1, execute = grs.SEQ_MANUAL, graphics = 0) # no graphics (the canvas with the model is closed!) self.newTypes.append((typeName, typeName, (), 1)) # add the new type to the list of newly created types # delete the deleted types... elements2delete = [] for nt in self.newTypes: name, name, args, flag = nt if not name in newTypesNames: elements2delete.append(nt) for delElem in elements2delete: self.newTypes.remove(delElem) # delete the element for type in self.types.keys(): # remove all elements del self.types[type] self.fillDictionaryWithTypes() # fill the dictionary again with the types def add2Types(self, ASGNodeType, oldName): """ check if the object whose name was oldName is included in the list of types, and if not, include it """ # create a new ATOM3TypeInfo to add it to the list newType = ATOM3TypeInfo(self) newType.setValue( (ASGNodeType.keyword_.toString(), ASGNodeType.keyword_.toString(), () )) # generate code for the class... self.genCodeFor( ASGNodeType ) # import the class file. exec "from "+ASGNodeType.keyword_.toString()+" import *\n" self.types[ASGNodeType.keyword_.toString()] = ( eval(ASGNodeType.keyword_.toString()), (self, )) # see if the name has changed if (oldName in self.types.keys()) and (oldName != ASGNodeType.keyword_.toString()): del self.types[oldName] os.remove( oldName+".py") typesInList = self.typeList.getValue() # the ATOM3TypeInfo objects in the list counter = 0 for typ in typesInList: val = typ() # get the object´s value if val[0] == oldName: # we have found it typesInList[counter] = newType break counter = counter + 1 def newModeUMLrelationship(self): """ Enters in the mode NEWUMLrelationship """ self.mode = NEWUMLrelationship def addCopyFromLHSButton(self, GGrule): """ Adds a button to copy the nodes in the LHS when editing a graph grammar. - GGrule is an object of type GGruleEdit, which contains the semantic information of the rule being edited. A reference to this object is kept in self.GGSemanticRule for latter use in callback method copyFromLHS. Added 20/July/2002 by JL """ if self.editGGLabel == ASGNode.INRHS: # add a button to copy from LHS's self.GGSemanticRule = GGrule bcopy = Button( self.opBar, text="Copy LHS", command = self.copyFromLHS ) bcopy.pack(side=LEFT, padx=2, pady=1) def copyFromLHS(self): """ Performs the copying from the LHS of one rule. It takes this information from self.GGSemanticRule Added 20/July/2002 """ clonedObjects = [] # A list with the cloned objects for nodeType in self.GGSemanticRule.LHS.listNodes.keys(): for node in self.GGSemanticRule.LHS.listNodes[nodeType]: newObj = node.clone() newObj.parent = self # change the object's parent to myself newObj.editGGLabel = ASG.INRHS # This node is in RHS newObj.GGset2Any = {} # reinitialize GG info newObj.graphObject_ = newObj.graphClass_(node.graphObject_.x, node.graphObject_.y, newObj) # create the graphical object #newObj.graphObject_.DrawObject(self.UMLmodel, self.editGGLabel) #self.UMLmodel.addtag_withtag(nodeType, newObj.graphObject_) self.ASGroot.addNode(newObj) node.clonedObject = newObj # keep a pointer to the cloned object clonedObjects.append(newObj) # copy also the (semantic) connections for obj in clonedObjects: obj.in_connections_nw = [] for icn in obj.in_connections_: obj.in_connections_nw.append(icn.clonedObject) obj.in_connections_ = obj.in_connections_nw obj.out_connections_nw = [] for icn in obj.out_connections_: obj.out_connections_nw.append(icn.clonedObject) obj.out_connections_ = obj.out_connections_nw del obj.in_connections_nw del obj.out_connections_nw # delete the pointer to the cloned object for nodeType in self.GGSemanticRule.LHS.listNodes.keys(): for node in self.GGSemanticRule.LHS.listNodes[nodeType]: del node.clonedObject self.ASGroot.writeContents(self, self.genGraphics, 1, clonedObjects) # show the copied objects in the canvas def reDrawGGLabels(self): """ redraws the GGLabels of each drawn entity, if appropriate """ if self.editGGLabel: for nt in self.ASGroot.listNodes.keys(): for node in self.ASGroot.listNodes[nt]: if node.graphObject_: node.graphObject_.drawGGLabel(self.UMLmodel) for drawnAttribute in node.graphObject_.attr_display.keys(): if node.getAttrValue(drawnAttribute).isNone() and self.editGGLabel == ASG.INLHS : node.graphObject_.ModifyAttribute(drawnAttribute, "") elif self.editGGLabel== ASG.INRHS: # Modified 22 July, JL if node.GGset2Any.has_key(drawnAttribute): if node.GGset2Any[drawnAttribute].Copy.getValue()[1]: node.graphObject_.ModifyAttribute(drawnAttribute, "") elif not node.GGset2Any[drawnAttribute].Specify.getValue()[4] in ["", "\n", None]: node.graphObject_.ModifyAttribute(drawnAttribute, "") def drawEntity(self, semObject, className): """ draws an existing entity """ graphObj = semObject.graphObject_ if graphObj: graphObj.redrawObject(self.UMLmodel, self.editGGLabel) # now evaluate ONLY the graphical constraints! graphObj.postCondition(ASG.CREATE) self.UMLmodel.addtag_withtag(className, graphObj.tag) self.mode=self.IDLEMODE def genASGNodeCode(self, f, UMLobject, isGlobalModel = None): """ generates code for the lower meta-level of a user-defined entity. - isGlobalModel may contain a list with the objects with cardinality constraints""" self.theKeyword = None f.write(" self.parent = parent\n") # search for something that is of Attribute Type or List of Attribute self.hasAppearance = None # flag that indicates if the entity has an attribute of type appearance # if it has, it must have a keyword... self.visitorOnAttributes( f, UMLobject, self.findKeywordAndIcons) if self.hasAppearance and self.theKeyword == None: # ... check that it is the case... tkMessageBox.showerror( "need a keyword!", "Entity "+UMLobject.keyword_.toString()+" did not define a keyword attribute", parent = self ) return 0 self.visitorOnAttributes( f, UMLobject, self.writeCreation) # fill the 'generatedAttributes' dictionary f.write(" self.generatedAttributes = {") self.visitorOnAttributes( f, UMLobject, self.writeGeneratedDictionary) f.write(" }\n") # fill the 'realOrder' list f.write(" self.realOrder = [") self.visitorOnAttributes( f, UMLobject, self.writeRealOrderList) f.write("]\n") # # Generate the clone function # f.write(" def clone(self):\n") if isGlobalModel != None: # we don't clone whole models!! #f.write(" cloneObject = ASG_"+ UMLobject.keyword_.toString() +"( self.parent )\n") #f.write(" cloneObject.listNodes = copy.copy(self.listNodes)\n") f.write(" return self\n") else: f.write(" cloneObject = "+ UMLobject.keyword_.toString() +"( self.parent )\n") f.write(" for atr in self.realOrder:\n") f.write(" cloneObject.setAttrValue(atr, self.getAttrValue(atr).clone() )\n") if self.theKeyword: f.write(" cloneObject.keyword_ = cloneObject."+self.theKeyword+"\n") if isGlobalModel == None: f.write(" ASGNode.cloneActions(self, cloneObject)\n\n") f.write(" return cloneObject\n") # changed 16 July 2002 # # Generate the copy function # f.write(" def copy(self, other):\n") f.write(" ATOM3Type.copy(self, other)\n") f.write(" for atr in self.realOrder:\n") f.write(" self.setAttrValue(atr, other.getAttrValue(atr) )\n") if self.theKeyword: f.write(" self.keyword_ = self."+self.theKeyword+"\n") # only if the object is a descendant of ASGNode if isGlobalModel == None: f.write(" ASGNode.copy(self, other)\n\n") # if we are an ASG class, must override the open() method from ATOM3Type if isGlobalModel != None: f.write(" def open(self, parent, topWindowParent):\n") #f.write(" ATOM3Type.show(self, parent, topWindowParent)\n") f.write(" from ATOM3 import *\n") f.write(" a = ATOM3(topWindowParent, '"+UMLobject.keyword_.toString()+"', 0, 1, self)\n") f.write(" #self.writeContents(a)\n") f.write(" return a\n") # generate code for the constraints... f.write(" def preCondition (self, actionID, * params):\n") self.visitorOnConstraints ( "PREcondition", f, UMLobject, self.writeActionConstraint ) f.write(" if self.graphObject_:\n") f.write(" return self.graphObject_.preCondition(actionID, params)\n") f.write(" else: return None\n") f.write(" def postCondition (self, actionID, * params):\n") self.visitorOnConstraints ( "POSTcondition", f, UMLobject, self.writeActionConstraint ) f.write(" if self.graphObject_:\n") f.write(" return self.graphObject_.postCondition(actionID, params)\n") f.write(" else: return None\n") self.visitorOnConstraints ( "", f, UMLobject, self.writeConstraintCode ) if __name__ == '__main__': # try to open the options file if len(sys.argv) == 1: ATOM3( Tk(), None , 1, 1).mainloop() else: ATOM3( Tk(), sys.argv[1] , 1, 1).mainloop() print "bye!"