# __ File: ASGNode.py ________________________________________________________________________________________________ # Implements : class ASGNode # Author : Juan de Lara # Description : Base (Abstract) class for Nodes in a ASG # Modified : 16 July 2002 # Changes : # - 23 Oct 2001 : added method "hasGenerativeAttributes" # - 3 Nov 2001 : in methods copy and cloneActions, added statement to copy and clone the rootNode and objectNumber field # - 23 Nov 2001 : added method "updateAppearanceAttributes". Copied from old version of ATOM3 # - 23 Nov 2001 : The method "attributesToDraw" is filled (it was as pass, and then implemented in entities # and relationships of the ERMetaModel. Now this is not necessary. Copied from old version of ATOM3 # - 30 Nov 2001 : AttributesToDraw was fixed, because sometimes the dictionary self.generatedAttributes does not give the complete information # about which type the components of a list have. # - 16 July 2002: Added a list called realOrder which stores the string names of the attributes in the order they've # been created. # _____________________________________________________________________________________________________________________ from types import * import string from ATOM3Integer import * from ATOM3Boolean import * from AttrCalc import * class ASGNode: # static variable to count the number of objects created so far numObjects = 0 # Some useful static constants EDIT = 0 SAVE = 1 CREATE = 2 CONNECT = 3 DELETE = 4 DISCONNECT = 5 TRANSFORM = 6 SELECT = 7 DRAG = 8 DROP = 9 MOVE = 10 # Values that editGGLabel can take... INLHS = 1 # This means that the node is being edited in a Graph Grammar Rule, on the LHS (shows a 'setAny' widget) INRHS = 2 # This means that the node is being edited in a Graph Grammar Rule, on the LHS (shows a 'copy values' widget) INMODEL = 0 # This means that the node is not being edited in a Graph Grammar def __init__(self): self.processed = 0 # flag if we are using the visitor pattern... self.objectNumber = ASGNode.numObjects # get a unique object number ASGNode.numObjects= ASGNode.numObjects+1 # increase the total number of object of this class self.graphClass_ = None # class of the graphical object, may be None self.graphObject_ = None # graphical object, may be None self.in_connections_ = [] # incoming connections, may be empty self.out_connections_ = [] # outgoing connections, may be empty self.keyword_ = None # Pointer to the attribute that is the keyword... self.containerFrame = None # Frame that will be used to edit the class self.generatedAttributes = {} # dicitionary that will contain all the generated attributes self.realOrder = [] # list with the real order in which the attributes have been specified (changed 16 July 2002) self.GGLabel = ATOM3Integer() # Label for Graph Grammar stuff self.GGset2Any= {} # Dictionary that will contain an ATOM3Boolean object for each widget. They will indicate if they must be set to None self.editGGLabel = 0 # Flag that indicates if the Graph Grammar Label must be edited self.rootNode = None # this is the parent node def getLastObjectNumber(self): """ Returns the object number of the last created object """ return ASGNode.numObjects-1 def editGGLabel(self, frame, topWindowParent, showRow): "if the flag 'editGGLabel' is on, presents a widget to edit the Graph Grammar Label" if self.editGGLabel: # if editGGLabel's value is INLHS or INRHS if self.GGLabel == None: self.GGLabel = ATOM3Integer() Label(frame, text="GGLabel").grid(row=showRow, column=0, sticky=W) self.GGLabel.show(frame, topWindowParent).grid(row=showRow, column=1, sticky=W) # constraints def setGGAnyWidget (self, frame, topWindowParent, name, showRow, showCol): "if the flag 'editGGLabel' is on, presents a widget that allows to set the corresponding element to None" if self.editGGLabel == self.INLHS: # if we're in the LHS of a Graph Grammar Rule... self.GGset2Any[name] = ATOM3Boolean('Set to any', 0) # Show the 'Set to any' widget... # set it to any if the corrsponding attribute is None... atr = self.getAttrValue(name) if atr.isNone(): self.GGset2Any[name].setValue((None,1)) self.GGset2Any[name].show(frame, topWindowParent).grid(row=showRow, column=showCol, sticky=W) elif self.editGGLabel == self.INRHS: # If we're on the RHS of a Graph Grammar Rule... if (not name in self.GGset2Any.keys()) or (not self.GGset2Any[name]): self.GGset2Any[name] = AttrCalc() # watch out! we use the same dictionary! self.GGset2Any[name].show(frame, topWindowParent).grid(row=showRow, column=showCol, sticky=W) def GGcheckSetNone(self): "if the flag 'editGGLabel' is on, sets to none the attributes, conforming to the ATOM3Boolean objects in self.GGset2Any" if self.GGLabel: # if we are using the GGLabel... self.GGLabel.destroy() # then destroy it! if self.editGGLabel == self.INLHS: # --- We have Labels --- for name in self.GGset2Any.keys(): # Look over all the attribute names... atr = self.getAttrValue(name) # get the attribute value... if self.GGset2Any[name].getValue()[1] == 1: # see if we should set it to none... atr.setNone() # set it to None else: atr.unSetNone() # unset none (this is done because in some cases isNone is implemented as a flag) elif self.editGGLabel == self.INRHS: # --- We have AttrCalc's --- for name in self.GGset2Any.keys(): # Look over all the attribute names... self.GGset2Any[name].destroy() # destroy it! def resetProcessed(self): "resets the processed flag" self.processed = 0 def getAttributes(self): "Returns a list with the generated attributes" return self.generatedAttributes.keys() def getAttrValue(self, attribute): "returns the value associated with that attribute" return self.__dict__[attribute] def setAttrValue(self, attribute, value): "sets to 'value' the attribute specified" self.__dict__[attribute] = value def getClass(self): "Method that returns a string with the class name" return self.__class__.__name__ def writeConstraint2File(self, file, indent, object, constraintType, constraint): "Writes the handling of the constraint to file" file.write('\n'+indent+'res= '+object+'.'+constraintType+'('+constraint+')') file.write(indent+'if res:\n') file.write(indent+' self.constraintViolation(res)\n') file.write(indent+' self.mode=self.IDLEMODE\n') file.write(indent+' return\n') def genAttributesCode(self, file, genGraphicalInfo = 1, objName = None, isMainModel = 1, rootNodeName = "rootNode", indent = ' ', genConstraints = 0, genSelf = 0, genGGcode = 0, parentName="self"): "Generates code for the constructor and the attributes value" # generate the constructor call if the last argument is None if not objName: # compose a name if not given if genSelf: myName = 'self.obj'+str(self.objectNumber) else: myName = 'obj'+str(self.objectNumber) # write code for the global preCondition evaluation, if genConstraints is set... if genConstraints and isMainModel : file.write( '\n'+indent+'self.globalPrecondition( rootNode )\n') #self.writeConstraint2File(file, indent, 'self.ASGroot', 'preCondition', 'ASG.CREATE') file.write( '\n'+indent+myName+'='+self.getClass()+'('+parentName+')\n\n') # write constructor... else: myName = objName for attr in self.generatedAttributes.keys(): # for each generated attribute self.getAttrValue(attr).writeValue2File ( file, indent, myName+'.'+attr, 1 ) # write its value to file # ey!, write GGLabel value (if appropriate) if self.GGLabel and (genGGcode == self.INLHS or genGGcode == self.INRHS): # if we are using the fields (we're defining a graph grammar rule) self.GGLabel.writeValue2File(file, indent, myName+'.GGLabel', 1) drawObject = 0 if genGraphicalInfo: if self.graphClass_: # check if the graphical class attribute has value file.write(indent+myName+'.graphClass_= graph_'+self.getClass()+'\n') if self.graphObject_: # check if the graphical object attribute has value graphicalFile = 'graph_'+self.getClass() # see if the graphical file exists... try: # see if we have associated a graphical attribute fgd = open( graphicalFile+".py", "r+t") # try to open the file with the graphical class except IOError: exists = 0 else: fgd.close() exists = 1 file.write(indent+'if '+parentName+'.genGraphics:\n') if not exists and self.isSubclass(self, 'ASG'): file.write(indent+' new_obj = graph_ASG_ERmetaMetaModel('+str(self.graphObject_.x)+','+str(self.graphObject_.y)+','+myName+')\n') else: self.graphObject_.writeConstructor2File (file, 'new_obj', indent+" ", myName) if isMainModel: file.write(indent+' new_obj.DrawObject(self.UMLmodel)\n') file.write(indent+' self.UMLmodel.addtag_withtag("'+self.getClass()+'", new_obj.tag)\n') if self.graphObject_.ChangesAtRunTime: # if the object changes at run-time, save its properties (Added 11-7-2002) self.graphObject_.write2File(file, indent+' ') file.write(indent+'else: new_obj = None\n') file.write(indent+myName+'.graphObject_ = new_obj\n') # if genGGcode == 1, the we must generate code for the self.GGset2Any... if genGGcode == self.INRHS: ggcounter = 0 for name in self.GGset2Any.keys(): # iterate on all the attributes... nobjName = myName+str(ggcounter) file.write(indent+nobjName+"= AttrCalc()\n") self.GGset2Any[name].writeConstructor2File(file, indent, nobjName, 0, 0) # write AttrCalc value or "set to Any" file.write(indent+myName+".GGset2Any['"+name+"']= "+nobjName+"\n") ggcounter = ggcounter + 1 if not objName: if not rootNodeName: rootNodeName = "rootNode" file.write(indent+rootNodeName+'.addNode('+myName+')\n') if genConstraints and isMainModel: file.write(indent+'self.globalAndLocalPostcondition('+myName+', rootNode)\n') #self.writeConstraint2File(file, indent, 'self.ASGroot', 'postCondition', 'ASG.CREATE') #self.writeConstraint2File(file, indent, myName, 'postCondition', 'ASG.CREATE') def isSubclass( self, node, which): "returns != 0 if node is a subclass of which (which is the string name of the class)" bases = node.__class__.__bases__ # obtain the list of all the node's base classes for base in bases: # iterate over all the base classes if base.__name__ == which: return 1 # ey! we have found a coincidence return 0 # if we have reached this point, then it is not a subclass def genConnectionsCode(self, file, genGraphicalInfo = 1, isRootNode = 0, indent=' ', genSelf = 0, writeComma = 0 ): """Generates code for the relations. Returns 1 if something has been writed in the file, 0 otherwise""" writed = 0 # Flag that will indicate if something has been writed if genSelf: # check if we should add "self." myName = 'self.obj'+str(self.objectNumber) else: myName = 'obj'+str(self.objectNumber) # Compose file name for obj in self.out_connections_: # For each object in the input connections # looping over the output connections is sufficient if genSelf: otherName = 'self.obj'+str(obj.objectNumber) # Compose other object's name else: otherName = 'obj'+str(obj.objectNumber) # Compose other object's name # now generate code to draw the connection if isRootNode: if (writeComma and isRootNode) or writed: file.write(', ') if genGraphicalInfo: # store information about graphical objects if graphical info has to be generated... # see if both nodes are in the same sub-model... smoothCoord = 0 connCoords, num1st = self.graphObject_.getConnectionCoordinates( "OUT", obj.graphObject_) # if origin is a graphLink then must invert the points... if self.graphObject_.isSubclass("graphLink"): reversed = [] counter = len(connCoords)-1 while counter > 0: px, py = connCoords[counter-1], connCoords[counter] reversed.append(px) reversed.append(py) counter = counter - 2 connCoords = reversed smoothCoord = self.graphObject_.itemcget(self.graphObject_.getConnectionHandler("OUT", obj.graphObject_), "smooth") if self.rootNode != obj.rootNode: # ey! different models! if genSelf: # compose the root's name rootName = 'self.obj'+str(obj.rootNode.objectNumber) else: rootName = 'obj'+str(obj.rootNode.objectNumber) if smoothCoord and smoothCoord != "0": file.write('('+myName+'.graphObject_.tag, '+rootName+'.graphObject_.tag, '+myName+','+otherName+','+str(connCoords)+', "'+smoothCoord+'", '+str(num1st)+')') else: file.write('('+myName+'.graphObject_.tag, '+rootName+'.graphObject_.tag, '+myName+','+otherName+','+str(connCoords)+', 0, '+str(num1st)+')') else: if smoothCoord and smoothCoord != "0": file.write('('+myName+','+otherName+','+str(connCoords)+',"'+smoothCoord+'", '+str(num1st)+')') else: file.write('('+myName+','+otherName+','+str(connCoords)+', 0, '+str(num1st)+')') else: file.write('('+myName+','+otherName+', None, 0)') writed = 1 # set flag to 1, because we have written something! elif self.rootNode == obj.rootNode or (self.level() > 1 and self.level() < obj.level()): file.write(indent+myName+'.out_connections_.append('+otherName+')\n') file.write(indent+otherName+'.in_connections_.append('+myName+')\n') writed = 1 # set flag to 1, because we have written something! # if connections come from other model, they are not still drawn!!! for obj in self.in_connections_: # For each object in the input connections if genSelf: otherName = 'self.obj'+str(obj.objectNumber) # Compose other object's name else: otherName = 'obj'+str(obj.objectNumber) # Compose other object's name # now generate code to draw the connection if self.rootNode != obj.rootNode: # ey! different models! if isRootNode: # see if both nodes are in the same sub-model... if (writeComma and isRootNode) or writed: file.write(', ') if genGraphicalInfo: # store information about graphical objects if graphical info has to be generated... if genSelf: # compose the root's name rootName = 'self.obj'+str(obj.rootNode.objectNumber) else: rootName = 'obj'+str(obj.rootNode.objectNumber) file.write('('+rootName+'.graphObject_.tag, '+myName+'.graphObject_.tag, '+otherName+', '+myName+')') else: file.write('('+otherName+', '+myName+')') writed = 1 # set flag to 1, because we have written something! elif self.rootNode == obj.rootNode or (self.level() > 1 and self.level() < obj.level()): # we are not in the rootModel file.write(indent+myName+'.in_connections_.append('+otherName+')\n') file.write(indent+otherName+'.out_connections_.append('+myName+')\n') writed = 1 # set flag to 1, because we have written something! return writed def genCode(self, fileName, allowedTypes, genGraph = 1, isRootNode = 0): "generates code for creating this graph" return 0 # must be overriden in ASG and ignored in ASGNode def attributesToDraw(self): """ return a (Python) list of all the ATOM3Attribute attributes of this object. """ listOfAttributes = [] for attr in self.generatedAttributes.keys(): infoTuple = self.generatedAttributes[attr] if infoTuple[0] == 'ATOM3Attribute': listOfAttributes.append(self.getAttrValue(attr)) elif infoTuple[0] == 'ATOM3List' and self.getAttrValue(attr).itemType.__name__ == 'ATOM3Attribute': listOfAttributes = listOfAttributes+self.getAttrValue(attr).getValue() return listOfAttributes def setGGLabel(self, intValue): "sets the value for the GGLabel field" if not self.GGLabel: # Label does not exist yet self.GGLabel = ATOM3Integer() # Label is an integer self.GGLabel.setValue(intValue) def showGGLabel(self, parent): "Returns a widget to edit the GGLabel field" if not self.GGLabel: # Label does not exist yet self.GGLabel = ATOM3Integer() # Label is an integer return self.GGLabel.show(parent, None) def removeNode (self ): "removes specified node (removes itself from neighbors connections)" # walk through input connections and remove 'node' from each element for element in self.in_connections_: element.out_connections_.remove(self) # walk through output connections and remove 'node' from each element for element in self.out_connections_: element.in_connections_.remove(self) self.rootNode.listNodes[self.getClass()].remove(self) # now remove itself from the list of nodes of the graph def doPrint(self): print "Type= ", self.getClass(), "(", self, ")" print "Value= ", for attr in self.generatedAttributes.keys(): print self.getAttrValue(attr).toString(), " ", print " " # print connections... print "out connections= ", for con in self.out_connections_: print con, " , ", print " " def doPrintM(self): self.doPrint() print "Matched=", self._matched def level(self ): " returns the hierarchy level in which node is" level = 0 # the lowest level is the 0 (only ownd by the main root node) r = self.rootNode while r != None: # go up trough the hierarchy r = r.rootNode level = level + 1 return level def hasGenerativeAttributes(self): "returns 1 if the entity has generative attributes, 0 otherwise" generativeAttributes = ["ATOM3Attribute", "ATOM3Appearance", "ATOM3Constraint", "ATOM3Cardinality"] for attribs in self.generatedAttributes.keys(): # for each attribute the node has... attr = self.generatedAttributes[attribs] # get its value if attr[0] in generativeAttributes: return 1 # it is a generative attribute elif attr[0] == "ATOM3List": # this list may have geneartive attributes inside... # go until the basic type... btype = self.getAttrValue(attribs).itemType.__name__ if btype in generativeAttributes: return 1 # found it! return 0 def updateAppearanceAttributes(self): """ If the entity has a keyword, calls the setValue method on all the attributes of type ATOM3Appearance. This method is called when the keyword of the entity is changed in it creation. The method updates all the ATOM3Appearance attributes. """ if self.keyword_: for attr in self.generatedAttributes.keys(): if self.generatedAttributes[attr][0] == 'ATOM3Appearance': attrib = self.getAttrValue(attr) attrib.setValue((self.keyword_.toString(), self)) #....................................................................................... # The following functions are used in the children classes which are generated by AToM3 #....................................................................................... def show(self, parent, parentWindowInfo): """ Shows the widget associated with each attribute of the object """ ATOM3Type.show(self, parent, parentWindowInfo) self.containerFrame = Frame(parent) rowCount = 0 for atr in self.realOrder: Label(self.containerFrame, text=atr).grid(row=rowCount,column=0,sticky=W) self.getAttrValue(atr).show(self.containerFrame, parentWindowInfo).grid(row=rowCount,column=1,sticky=W) ASGNode.setGGAnyWidget (self, self.containerFrame, parentWindowInfo, atr, rowCount, 2) rowCount = rowCount + 1 if not self.isSubclass( self, "ASG"): # only show this widget if we are not a subclass of ASG ASGNode.editGGLabel(self, self.containerFrame, parentWindowInfo, rowCount) return self.containerFrame def toString(self, maxWide = None, maxLines = None ): """ Method returns a string with the object's value. If maxWide is specified and the string is greater than maxWide, then it is cutted to this value, adding a "..." """ rs = "" for atr in self.realOrder: rs = rs + self.getAttrValue(atr).toString() if atr != self.realOrder[len(self.realOrder)-1]: rs = rs+" " if maxWide and len(rs) > maxWide : return rs[0:maxWide-3]+'...' else: return rs def getValue(self): """ Method gets the value of the object. Returns a tuple containing the value of each attribute of the object. """ atrList = [] for atr in self.realOrder: atrList.append(self.getAttrValue(atr).getValue()) return tuple(atrList) def setValue(self, value): """ Method sets the value of the object. It receives a tuple containing the value of each attribute of the object. """ count = 0 for atr in self.realOrder: self.getAttrValue(atr).setValue(value[count]) count = count + 1 def writeConstructor2File(self, file, indent, objName="at", depth = 0, generatingCode = 0): """ Method that writes into a file the constructor and the value of the object. """ for atr in self.realOrder: self.getAttrValue(atr).writeConstructor2File(file, indent, objName+"."+atr, depth, generatingCode) def writeValue2File(self, file, indent, objName="at", depth = 0, generatingCode = 0): """ Method that writes into a file the the value of the object. """ for atr in self.realOrder: self.getAttrValue(atr).writeValue2File(file, indent, objName+"."+atr, depth, generatingCode) def invalid(self): """ Returns 1 if the node has some invalid attribute, 0 otherwise """ self.GGcheckSetNone() for atr in self.realOrder: if self.getAttrValue(atr).invalid(): return 1 return 0 def destroy(self): """ Destroys the node, calling destroy on all its generated attributes... """ self.GGcheckSetNone() for atr in self.realOrder: self.getAttrValue(atr).destroy() self.containerFrame = None def cloneActions(self, cloneObject): "clones the common attributes into cloneObject" cloneObject.graphClass_ = self.graphClass_ cloneObject.graphObject_ = self.graphObject_ cloneObject.rootNode = self.rootNode cloneObject.objectNumber = self.objectNumber import copy cloneObject.in_connections_ = copy.copy(self.in_connections_) cloneObject.out_connections_ = copy.copy(self.out_connections_) if self.GGLabel: cloneObject.GGLabel = self.GGLabel.clone() cloneObject.GGset2Any = self.GGset2Any cloneObject.editGGLabel = self.editGGLabel cloneObject.containerFrame= self.containerFrame def copy(self, other): "copies the common attributes from other into self" self.graphClass_ = other.graphClass_ self.graphObject_ = other.graphObject_ self.in_connections_ = other.in_connections_ self.out_connections_ = other.out_connections_ self.containerFrame = other.containerFrame self.keyword_ = other.keyword_ self.editGGLabel = other.editGGLabel self.GGset2Any = other.GGset2Any self.GGLabel = other.GGLabel self.rootNode = other.rootNode self.objectNumber = other.objectNumber def preAction(self, actionID, * params): pass def postAction(self, actionID, * params): pass def preCondition(self, actionID, * params): pass def postCondition(self, actionID, * params): pass