*** empty log message ***
git-svn-id: file:///Users/arjan/backup/gaphor/trunk/gaphor@401 a8418922-720d-0410-834f-a69b97ada669
This commit is contained in:
parent
f7055d0df1
commit
a9ee9dfeba
15
ChangeLog
15
ChangeLog
@ -1,3 +1,18 @@
|
||||
2004-09-26 Arjan Molenaar <arjanmolenaar@hetnet.nl>
|
||||
|
||||
* data/plugins/diagramlayout: new plugin, layout elements in a diagram
|
||||
* data/plugins/pynsource: new plugin. Create diagrams from Python
|
||||
code. Uses Andy Bulka's PyNSource code.
|
||||
# During creation of the plugin I noticed a bug in Associations:
|
||||
# The aggregation attribute of the wrong Property instance is used.
|
||||
# This behavior has been fixed:
|
||||
* gaphor/storage.py: added workaround in load_elements_generator()
|
||||
* gaphor/diagram/association.py: updated code to show aggregation on
|
||||
the right end.
|
||||
* gaphor/UML/uml2.gaphor: loaded and saved (bug no longer in data
|
||||
model)
|
||||
* utils/genUML2.py: updated.
|
||||
|
||||
2004-09-14 Arjan Molenaar <arjanmolenaar@hetnet.nl>
|
||||
|
||||
* gaphor/diagram/itemactions.py: Support new Editable interface.
|
||||
|
202
data/plugins/diagramlayout/__init__.py
Normal file
202
data/plugins/diagramlayout/__init__.py
Normal file
@ -0,0 +1,202 @@
|
||||
# vim:sw=4:et
|
||||
"""This module provides a means to automatocally layout diagrams.
|
||||
|
||||
The layout is done like this:
|
||||
- First all nodes (Classes, packages, comments) on a digram are determined
|
||||
- A vertical ordering is determined based on the inheritance
|
||||
- A horizontal ordering is determined based on associations and dependencies
|
||||
- The nodes are moved to their place
|
||||
- Lines are reconnected to the nodes, so everything looks pretty.
|
||||
"""
|
||||
|
||||
import gaphor.plugin
|
||||
import toposort
|
||||
import gaphor.diagram as diagram
|
||||
import random
|
||||
|
||||
class DiagramLayoutAction(gaphor.plugin.Action):
|
||||
|
||||
def update(self):
|
||||
self.sensitive = bool(self.get_window().get_current_diagram())
|
||||
|
||||
def execute(self):
|
||||
d = self.get_window().get_current_diagram()
|
||||
if d:
|
||||
layout_diagram(d)
|
||||
|
||||
|
||||
MARGIN = 100
|
||||
|
||||
def layout_diagram(diag):
|
||||
"""So an attempt to layout (order) the items on a diagram. The items
|
||||
should already be placed on the diagram and the items should already be
|
||||
connected.
|
||||
|
||||
This function works on the diagram items (hence it does not check relations
|
||||
in the datamodel, only the ones drawn on the diagram) to produce a
|
||||
decent layout.
|
||||
"""
|
||||
nodes = []
|
||||
primary_nodes = []
|
||||
relations = []
|
||||
other_relations = []
|
||||
|
||||
# Make sure all items are updated
|
||||
diag.canvas.update_now()
|
||||
|
||||
# First extract data from the diagram (which ones are the nodes, and
|
||||
# the relationships).
|
||||
for item in diag.canvas.root.children:
|
||||
if isinstance(item, (diagram.GeneralizationItem,
|
||||
diagram.ImplementationItem)):
|
||||
# Primary relationships, should be drawn top-down
|
||||
try:
|
||||
relations.append((item.handles[0].connected_to,
|
||||
item.handles[-1].connected_to))
|
||||
primary_nodes.extend(relations[-1])
|
||||
except Exception, e:
|
||||
log.error(e)
|
||||
elif isinstance(item, diagram.diagramline.DiagramLine):
|
||||
# Secondary (associations, dependencies) may be drawn top-down
|
||||
# or left-right
|
||||
try:
|
||||
other_relations.append((item.handles[0].connected_to,
|
||||
item.handles[-1].connected_to))
|
||||
#other_relations.append((item.handles[-1].connected_to,
|
||||
# item.handles[0].connected_to))
|
||||
except Exception, e:
|
||||
log.error(e)
|
||||
else:
|
||||
nodes.append(item)
|
||||
|
||||
# Add some randomness:
|
||||
random.shuffle(other_relations)
|
||||
primary_nodes = uniq(primary_nodes)
|
||||
|
||||
# Find out our horizontal and vertical sorting
|
||||
sorted = toposort.toposort(nodes, relations, 0)
|
||||
other_sorted = toposort.toposort(nodes, other_relations, 0)
|
||||
|
||||
# Move nodes from the first (generalization) row to the other rows
|
||||
# if they are not superclasses for some other class
|
||||
# Run the list twice, just to ensure no items are left behind.
|
||||
for item in list(sorted[0]) * 2:
|
||||
if item not in primary_nodes and item in sorted[0]:
|
||||
# Find nodes that do have a relation to this one
|
||||
related = find_related_nodes(item, other_relations)
|
||||
# Figure out what row(s) they're on
|
||||
row = find_row(item, related, sorted[1:])
|
||||
if row:
|
||||
#print 'moving', item.subject.name, 'to row', sorted.index(row)
|
||||
sorted[0].remove(item)
|
||||
row.append(item)
|
||||
|
||||
# Order each row based on the sort order of other_sorted
|
||||
# (the secondary sort alg.).
|
||||
for row in sorted:
|
||||
for other_row in other_sorted:
|
||||
for other_item in other_row:
|
||||
if other_item in row:
|
||||
row.remove(other_item)
|
||||
row.append(other_item)
|
||||
|
||||
# Place the nodes on the diagram.
|
||||
y = MARGIN / 2
|
||||
for row in sorted:
|
||||
x = MARGIN / 2
|
||||
maxy = 0
|
||||
for item in row:
|
||||
if not item:
|
||||
continue
|
||||
maxy = max(maxy, item.height)
|
||||
a = item.get_property('affine')
|
||||
a = (a[0], a[1], a[2], a[3], x, y)
|
||||
item.set_property('affine', a)
|
||||
x += item.width + MARGIN
|
||||
y += maxy + MARGIN
|
||||
|
||||
# Reattach the relationships to the nodes, in a way that it looks nice.
|
||||
simple_layout_lines(diag)
|
||||
|
||||
|
||||
def simple_layout_lines(diag):
|
||||
"""Just do the layout of the lines in a diagram. The nodes (class, package)
|
||||
are left where they are (use layout_diagram() if you want to reorder
|
||||
everything).
|
||||
|
||||
The line layout is basically very simple: just draw straight lines
|
||||
between nodes on the diagram.
|
||||
"""
|
||||
lines = {}
|
||||
for item in diag.canvas.root.children:
|
||||
if isinstance(item, diagram.diagramline.DiagramLine):
|
||||
# Secondary (associations, dependencies) may be drawn top-down
|
||||
# or left-right
|
||||
try:
|
||||
lines[item] = (item.handles[0].connected_to,
|
||||
item.handles[-1].connected_to)
|
||||
except Exception, e:
|
||||
log.error(e)
|
||||
|
||||
# Now we have the lines, let's first ensure we only have a begin and an
|
||||
# end handle
|
||||
for line in lines.keys():
|
||||
while len(line.handles) > 2:
|
||||
line.set_property('del_segment', 0)
|
||||
|
||||
# Strategy 1:
|
||||
# Now we have nice short lines. Let's move them to a point between
|
||||
# both nodes and let the connect() do the work:
|
||||
for line, nodes in lines.items():
|
||||
if not nodes[0] or not nodes[1]:
|
||||
# loose end
|
||||
continue
|
||||
center0 = find_center(nodes[0])
|
||||
center1 = find_center(nodes[1])
|
||||
center = (center0[0] + center1[0]) / 2.0, (center0[1] + center1[1]) / 2.0
|
||||
line.handles[0].set_pos_w(*center)
|
||||
line.handles[-1].set_pos_w(*center)
|
||||
nodes[0].connect_handle(line.handles[0])
|
||||
nodes[1].connect_handle(line.handles[-1])
|
||||
|
||||
|
||||
def uniq(lst):
|
||||
d = {}
|
||||
for l in lst:
|
||||
d[l] = None
|
||||
return d.keys()
|
||||
|
||||
|
||||
def find_related_nodes(item, relations):
|
||||
"""Find related nodes of item, given a list of tuples.
|
||||
References to itself are ignored.
|
||||
"""
|
||||
related = []
|
||||
for pair in relations:
|
||||
if pair[0] is item:
|
||||
if pair[1] is not item:
|
||||
related.append(pair[1])
|
||||
elif pair[1] is item:
|
||||
if pair[0] is not item:
|
||||
related.append(pair[0])
|
||||
return uniq(related)
|
||||
|
||||
|
||||
def find_row(item, related_items, sorted):
|
||||
"""Find the row that contains the most references to item.
|
||||
"""
|
||||
max_refs = 0
|
||||
max_row = None
|
||||
for row in sorted:
|
||||
cnt = len([ i for i in row if i in related_items ])
|
||||
if cnt > max_refs:
|
||||
max_row = row
|
||||
max_refs = cnt
|
||||
return max_row
|
||||
|
||||
def find_center(item):
|
||||
"""Find the center point of the item, in world coordinates
|
||||
"""
|
||||
x = item.width / 2.0
|
||||
y = item.height / 2.0
|
||||
return item.affine_point_i2w(x, y)
|
28
data/plugins/diagramlayout/plugin.xml
Normal file
28
data/plugins/diagramlayout/plugin.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- vim:sw=2:et:
|
||||
-->
|
||||
<plugin name="Diagram Layout"
|
||||
version="0.1"
|
||||
author="Arjan Molenaar">
|
||||
<description>
|
||||
Reorder the elements a diagram.
|
||||
</description>
|
||||
<require>
|
||||
<!--
|
||||
<module name="some.module"/>
|
||||
<plugin name="anotherPlugin"/>
|
||||
-->
|
||||
</require>
|
||||
<provide>
|
||||
<action id="DiagramLayout"
|
||||
label="Layout diagram"
|
||||
tooltip="Do layout the elements in a diagram"
|
||||
class="DiagramLayoutAction" slot="DiagramSlot">
|
||||
<!--
|
||||
Add optional dependencies to this action. The action is then updated
|
||||
when actions defined in the depends tag are executed.
|
||||
-->
|
||||
<depends action="TabChange"/>
|
||||
</action>
|
||||
</provide>
|
||||
</plugin>
|
241
data/plugins/diagramlayout/toposort.py
Normal file
241
data/plugins/diagramlayout/toposort.py
Normal file
@ -0,0 +1,241 @@
|
||||
class RecursionError( OverflowError, ValueError ):
|
||||
'''Unable to calculate result because of recursive structure'''
|
||||
|
||||
|
||||
def sort(nodes, routes, noRecursion=1):
|
||||
'''Passed a list of node IDs and a list of source,dest ID routes
|
||||
attempt to create a list of stages where each sub list
|
||||
is one stage in a process.
|
||||
'''
|
||||
children, parents = _buildChildrenLists(routes)
|
||||
# first stage is those nodes
|
||||
# having no incoming routes...
|
||||
stage = []
|
||||
stages = [stage]
|
||||
taken = []
|
||||
for node in nodes:
|
||||
if (not parents.get(node)):
|
||||
stage.append (node)
|
||||
if nodes and not stage:
|
||||
# there is no element which does not depend on
|
||||
# some other element!!!
|
||||
stage.append( nodes[0])
|
||||
taken.extend( stage )
|
||||
nodes = filter ( lambda x, l=stage: x not in l, nodes )
|
||||
while nodes:
|
||||
previousStageChildren = []
|
||||
nodelen = len(nodes)
|
||||
# second stage are those nodes
|
||||
# which are direct children of the first stage
|
||||
for node in stage:
|
||||
for child in children.get (node, []):
|
||||
if child not in previousStageChildren and child not in taken:
|
||||
previousStageChildren.append(child)
|
||||
elif child in taken and noRecursion:
|
||||
raise RecursionError( (child, node) )
|
||||
# unless they are children of other direct children...
|
||||
# TODO, actually do that...
|
||||
stage = previousStageChildren
|
||||
removes = []
|
||||
for current in stage:
|
||||
currentParents = parents.get( current, [] )
|
||||
for parent in currentParents:
|
||||
if parent in stage and parent != current:
|
||||
# might wind up removing current...
|
||||
if not current in parents.get(parent, []):
|
||||
# is not mutually dependent...
|
||||
removes.append( current )
|
||||
for remove in removes:
|
||||
while remove in stage:
|
||||
stage.remove( remove )
|
||||
stages.append( stage)
|
||||
taken.extend( stage )
|
||||
nodes = filter ( lambda x, l=stage: x not in l, nodes )
|
||||
if nodelen == len(nodes):
|
||||
if noRecursion:
|
||||
raise RecursionError( nodes )
|
||||
else:
|
||||
stages.append( nodes[:] )
|
||||
nodes = []
|
||||
return stages
|
||||
|
||||
def _buildChildrenLists (routes):
|
||||
childrenTable = {}
|
||||
parentTable = {}
|
||||
for sourceID,destinationID in routes:
|
||||
currentChildren = childrenTable.get( sourceID, [])
|
||||
currentParents = parentTable.get( destinationID, [])
|
||||
if not destinationID in currentChildren:
|
||||
currentChildren.append ( destinationID)
|
||||
if not sourceID in currentParents:
|
||||
currentParents.append ( sourceID)
|
||||
childrenTable[sourceID] = currentChildren
|
||||
parentTable[destinationID] = currentParents
|
||||
return childrenTable, parentTable
|
||||
|
||||
|
||||
def toposort (nodes, routes, noRecursion=1):
|
||||
'''Topological sort from Tim Peters, fairly efficient
|
||||
in comparison (it seems).'''
|
||||
#first calculate the recursion depth
|
||||
|
||||
dependencies = {}
|
||||
inversedependencies = {}
|
||||
if not nodes:
|
||||
return []
|
||||
if not routes:
|
||||
return [nodes]
|
||||
for node in nodes:
|
||||
dependencies[ node ] = (0, node)
|
||||
inversedependencies[ node ] = []
|
||||
|
||||
|
||||
for depended, depends in routes:
|
||||
# is it a null rule
|
||||
try:
|
||||
newdependencylevel, object = dependencies.get ( depends, (0, depends))
|
||||
except TypeError:
|
||||
print depends
|
||||
raise
|
||||
dependencies[ depends ] = (newdependencylevel + 1, depends)
|
||||
# "dependency (existence) of depended-on"
|
||||
newdependencylevel,object = dependencies.get ( depended, (0, depended) )
|
||||
dependencies[ depended ] = (newdependencylevel, depended)
|
||||
# Inverse dependency set up
|
||||
dependencieslist = inversedependencies.get ( depended, [])
|
||||
dependencieslist.append (depends)
|
||||
inversedependencies[depended] = dependencieslist
|
||||
### Now we do the actual sorting
|
||||
# The first task is to create the sortable
|
||||
# list of dependency-levels
|
||||
sortinglist = dependencies.values()
|
||||
sortinglist.sort ()
|
||||
output = []
|
||||
while sortinglist:
|
||||
deletelist = []
|
||||
generation = []
|
||||
output.append( generation)
|
||||
while sortinglist and sortinglist[0][0] == 0:
|
||||
number, object = sortinglist[0]
|
||||
generation.append ( object )
|
||||
deletelist.append( object )
|
||||
for inverse in inversedependencies.get(object, () ):
|
||||
try:
|
||||
oldcount, inverse = dependencies [ inverse]
|
||||
if oldcount > 0:
|
||||
# will be dealt with on later pass
|
||||
dependencies [ inverse] = (oldcount-1, inverse)
|
||||
else:
|
||||
# will be dealt with on this pass,
|
||||
# so needs not to be in the sorting list next time
|
||||
deletelist.append( inverse )
|
||||
# just in case a loop comes through
|
||||
inversedependencies[object] = []
|
||||
except KeyError:
|
||||
# dealing with a recursion-breaking run...
|
||||
pass
|
||||
del sortinglist [0]
|
||||
# if no elements could be deleted, then
|
||||
# there is something which depends upon itself
|
||||
if not deletelist:
|
||||
if noRecursion:
|
||||
raise RecursionError( sortinglist )
|
||||
else:
|
||||
# hack so that something gets deleted...
|
||||
## import pdb
|
||||
## pdb.set_trace()
|
||||
dependencies[sortinglist[0][1]] = (0,sortinglist[0][1])
|
||||
# delete the items that were dealt with
|
||||
for item in deletelist:
|
||||
try:
|
||||
del dependencies [ item ]
|
||||
except KeyError:
|
||||
pass
|
||||
# need to recreate the sortinglist
|
||||
sortinglist = dependencies.values()
|
||||
if not generation:
|
||||
output.remove( generation )
|
||||
sortinglist.sort ()
|
||||
return output
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
import pprint, traceback
|
||||
nodes= [ 0,1,2,3,4,5 ]
|
||||
testingValues = [
|
||||
[ (0,1),(1,2),(2,3),(3,4),(4,5)],
|
||||
[ (0,1),(0,2),(1,2),(3,4),(4,5)],
|
||||
[
|
||||
(0,1),
|
||||
(0,2),
|
||||
(0,2),
|
||||
(2,4),
|
||||
(2,5),
|
||||
(3,2),
|
||||
(0,3)],
|
||||
[
|
||||
(0,1), # 3-element cycle test, no orphan nodes
|
||||
(1,2),
|
||||
(2,0),
|
||||
(2,4),
|
||||
(2,5),
|
||||
(3,2),
|
||||
(0,3)],
|
||||
[
|
||||
(0,1),
|
||||
(1,1),
|
||||
(1,1),
|
||||
(1,4),
|
||||
(1,5),
|
||||
(1,2),
|
||||
(3,1),
|
||||
(2,1),
|
||||
(2,0)],
|
||||
[
|
||||
(0,1),
|
||||
(1,0),
|
||||
(0,2),
|
||||
(0,3),
|
||||
],
|
||||
[
|
||||
(0,1),
|
||||
(1,0),
|
||||
(0,2),
|
||||
(3,1),
|
||||
],
|
||||
]
|
||||
print 'sort, no recursion allowed'
|
||||
for index in range(len(testingValues)):
|
||||
## print ' %s -- %s'%( index, testingValues[index])
|
||||
try:
|
||||
print ' ', sort( nodes, testingValues[index] )
|
||||
except:
|
||||
print 'exception raised'
|
||||
print 'toposort, no recursion allowed'
|
||||
for index in range(len(testingValues)):
|
||||
## print ' %s -- %s'%( index, testingValues[index])
|
||||
try:
|
||||
print ' ', toposort( nodes, testingValues[index] )
|
||||
except:
|
||||
print 'exception raised'
|
||||
print 'sort, recursion allowed'
|
||||
for index in range(len(testingValues)):
|
||||
## print ' %s -- %s'%( index, testingValues[index])
|
||||
try:
|
||||
print ' ', sort( nodes, testingValues[index],0 )
|
||||
except:
|
||||
print 'exception raised'
|
||||
print 'toposort, recursion allowed'
|
||||
for index in range(len(testingValues)):
|
||||
## print ' %s -- %s'%( index, testingValues[index])
|
||||
try:
|
||||
print ' ', toposort( nodes, testingValues[index],0 )
|
||||
except:
|
||||
print 'exception raised'
|
||||
|
||||
|
||||
|
27
data/plugins/pynsource/__init__.py
Normal file
27
data/plugins/pynsource/__init__.py
Normal file
@ -0,0 +1,27 @@
|
||||
# vim:sw=4:et:
|
||||
|
||||
import gaphor.plugin
|
||||
|
||||
from pynsource import PythonToJava, PySourceAsJava
|
||||
from pynsourcewindow import PyNSourceWindow
|
||||
|
||||
class PyNSourceAction(gaphor.plugin.Action):
|
||||
|
||||
def __init__(self):
|
||||
gaphor.plugin.Action.__init__(self)
|
||||
self.win = None
|
||||
|
||||
def execute(self):
|
||||
if not self.win or self.win.get_state() == self.win.STATE_CLOSED:
|
||||
self.win = PyNSourceWindow(self.get_window())
|
||||
self.win.construct()
|
||||
else:
|
||||
self.win.show()
|
||||
|
||||
if __name__ in ('__main__', '__builtin__'):
|
||||
print 'Loading...'
|
||||
import gtk
|
||||
win = PyNSourceWindow()
|
||||
win.construct()
|
||||
win.get_window().connect('destroy', lambda e: gtk.main_quit())
|
||||
gtk.main()
|
204
data/plugins/pynsource/engineer.py
Normal file
204
data/plugins/pynsource/engineer.py
Normal file
@ -0,0 +1,204 @@
|
||||
# vim:sw=4:et
|
||||
|
||||
"""The code reverse engineer.
|
||||
"""
|
||||
|
||||
import gaphor.UML as UML
|
||||
import gaphor.diagram as diagram
|
||||
|
||||
from pynsource import PySourceAsText
|
||||
|
||||
BASE_CLASSES = ('object', 'type', 'dict', 'list', 'tuple', 'int', 'float')
|
||||
|
||||
class Engineer(object):
|
||||
"""The Engineer class will create a Gaphor model based on a list of Python
|
||||
files.
|
||||
"""
|
||||
|
||||
def process(self, files=None):
|
||||
|
||||
# these are tuples between class names.
|
||||
#self.associations_generalisation = []
|
||||
#self.associations_composition = []
|
||||
|
||||
p = PySourceAsText()
|
||||
self.parser = p
|
||||
|
||||
if files:
|
||||
#u = PythonToJava(None, treatmoduleasclass=0, verbose=0)
|
||||
for f in files:
|
||||
# Build a shape with all attrs and methods, and prepare association dict
|
||||
p.Parse(f)
|
||||
|
||||
print p
|
||||
|
||||
try:
|
||||
self._root_package = UML.select(lambda e: isinstance(e, UML.Package) and not e.namespace)[0]
|
||||
except IndexError:
|
||||
pass # running as test?
|
||||
|
||||
for m in p.modulemethods:
|
||||
print 'ModuleMethod:', m
|
||||
|
||||
# Step 0: create a diagram to put the newly created elements on
|
||||
self.diagram = UML.create(UML.Diagram)
|
||||
self.diagram.name = 'New classes'
|
||||
self.diagram.package = self._root_package
|
||||
|
||||
# Step 1: create the classes
|
||||
for name, clazz in p.classlist.items():
|
||||
print type(clazz), dir(clazz)
|
||||
self._create_class(clazz, name)
|
||||
|
||||
# Create generalization relationships:
|
||||
for name, clazz in p.classlist.items():
|
||||
self._create_generalization(clazz)
|
||||
|
||||
# Create attributes (and associations) on the classes
|
||||
for name, clazz in p.classlist.items():
|
||||
self._create_attributes(clazz)
|
||||
|
||||
# Create operations
|
||||
for name, clazz in p.classlist.items():
|
||||
self._create_methods(clazz)
|
||||
|
||||
try:
|
||||
from diagramlayout import layout_diagram
|
||||
except ImportError, e:
|
||||
log.error('Could not import diagramlayout module', e)
|
||||
else:
|
||||
layout_diagram(self.diagram)
|
||||
|
||||
def _create_class(self, clazz, name):
|
||||
c = UML.create(UML.Class)
|
||||
c.name = name
|
||||
c.package = self.diagram.namespace
|
||||
ci = self.diagram.create(diagram.ClassItem)
|
||||
ci.subject = c
|
||||
clazz.gaphor_class = c
|
||||
clazz.gaphor_class_item = ci
|
||||
|
||||
def _create_generalization(self, clazz):
|
||||
if not clazz.ismodulenotrealclass:
|
||||
for superclassname in clazz.classesinheritsfrom:
|
||||
if superclassname in BASE_CLASSES:
|
||||
continue
|
||||
try:
|
||||
superclass = self.parser.classlist[superclassname].gaphor_class
|
||||
superclass_item = self.parser.classlist[superclassname].gaphor_class_item
|
||||
except KeyError, e:
|
||||
print 'No class found named', superclassname
|
||||
others = UML.select(lambda e: isinstance(e, UML.Class) and e.name == superclassname)
|
||||
if others:
|
||||
superclass = others[0]
|
||||
print 'Found class in factory: %s' % superclass.name
|
||||
superclass_item = self.diagram.create(diagram.ClassItem)
|
||||
superclass_item.subject = superclass
|
||||
else:
|
||||
continue
|
||||
# Finally, create the generalization relationship
|
||||
print 'Creating Generalization for %s' % clazz, superclass
|
||||
gen = UML.create(UML.Generalization)
|
||||
gen.general = superclass
|
||||
gen.specific = clazz.gaphor_class
|
||||
geni = self.diagram.create(diagram.GeneralizationItem)
|
||||
geni.subject = gen
|
||||
superclass_item.connect_handle(geni.handles[0])
|
||||
clazz.gaphor_class_item.connect_handle(geni.handles[-1])
|
||||
|
||||
def _create_attributes(self, clazz):
|
||||
for attrobj in clazz.attrs:
|
||||
# TODO: Check object type and figure out if it should be an
|
||||
# attribute or an association.
|
||||
self._create_attribute(clazz, attrobj)
|
||||
|
||||
def _create_methods(self, clazz):
|
||||
for adef in clazz.defs:
|
||||
op = UML.create(UML.Operation)
|
||||
op.name = adef
|
||||
clazz.gaphor_class.ownedOperation = op
|
||||
|
||||
def _find_class_by_name(self, classname):
|
||||
try:
|
||||
superclass = self.parser.classlist[classname].gaphor_class
|
||||
superclass_item = self.parser.classlist[classname].gaphor_class_item
|
||||
except KeyError, e:
|
||||
print 'No class found named', classname
|
||||
others = UML.select(lambda e: isinstance(e, UML.Class) and e.name == classname)
|
||||
if others:
|
||||
superclass = others[0]
|
||||
print 'Found class in factory: %s' % superclass.name
|
||||
superclass_item = self.diagram.create(diagram.ClassItem)
|
||||
superclass_item.subject = superclass
|
||||
else:
|
||||
return None, None
|
||||
return superclass, superclass_item
|
||||
|
||||
def _visibility(self, attrname):
|
||||
if attrname.startswith('__'):
|
||||
return 'private'
|
||||
elif attrname.startswith('_'):
|
||||
return 'protected'
|
||||
return 'public'
|
||||
|
||||
def _create_attribute(self, clazz, attr):
|
||||
static = False
|
||||
many = False
|
||||
if 'static' in attr.attrtype:
|
||||
static = True
|
||||
if 'many' in attr.attrtype:
|
||||
many = True
|
||||
compositescreated = self.parser.GetCompositeClassesForAttr(attr.attrname, clazz)
|
||||
tail_type = None
|
||||
if compositescreated:
|
||||
tail_type, tail_type_item = self._find_class_by_name(compositescreated[0])
|
||||
|
||||
if tail_type:
|
||||
# Create an association:
|
||||
#print "%s %s <@>----> %s" % (attr.attrname, static, str(compositescreated))
|
||||
# The property on the tail of the association (tail_end) is owned
|
||||
# by the class connected on the head_end (head_type)
|
||||
head_type = clazz.gaphor_class
|
||||
head_type_item = clazz.gaphor_class_item
|
||||
|
||||
relation = UML.create(UML.Association)
|
||||
head_end = UML.create(UML.Property)
|
||||
head_end.lowerValue = UML.create(UML.LiteralSpecification)
|
||||
tail_end = UML.create(UML.Property)
|
||||
tail_end.name = attr.attrname
|
||||
tail_end.visibility = self._visibility(attr.attrname)
|
||||
tail_end.aggregation = 'composite'
|
||||
tail_end.lowerValue = UML.create(UML.LiteralSpecification)
|
||||
relation.package = self.diagram.namespace
|
||||
relation.memberEnd = head_end
|
||||
relation.memberEnd = tail_end
|
||||
head_end.type = head_type
|
||||
tail_end.type = tail_type
|
||||
head_type.ownedAttribute = tail_end
|
||||
tail_type.ownedAttribute = head_end
|
||||
|
||||
# Now create the diagram item:
|
||||
association = self.diagram.create(diagram.AssociationItem)
|
||||
|
||||
# First connect one handle:j
|
||||
head_type_item.connect_handle(association.handles[0])
|
||||
|
||||
association.subject = relation
|
||||
association.set_property('head-subject', head_end)
|
||||
association.set_property('tail-subject', tail_end)
|
||||
|
||||
tail_type_item.connect_handle(association.handles[-1])
|
||||
|
||||
else:
|
||||
# Create a simple attribute:
|
||||
#print "%s %s" % (attr.attrname, static)
|
||||
prop = UML.create(UML.Property)
|
||||
prop.name = attr.attrname
|
||||
prop.visibility = self._visibility(attr.attrname)
|
||||
prop.isStatic = static
|
||||
clazz.gaphor_class.ownedAttribute = prop
|
||||
#print many
|
||||
import pprint
|
||||
pprint.pprint(attr)
|
||||
#print dir(attr)
|
||||
|
510
data/plugins/pynsource/keywords.py
Normal file
510
data/plugins/pynsource/keywords.py
Normal file
@ -0,0 +1,510 @@
|
||||
"""
|
||||
Definitions of python, java and delphi keywords
|
||||
so that pynsource can skip these and not treat them like you are
|
||||
creating an instance of a locally defined class.
|
||||
"""
|
||||
|
||||
pythonbuiltinfunctions_txt = """
|
||||
ArithmeticError
|
||||
AssertionError
|
||||
AttributeError
|
||||
DeprecationWarning
|
||||
EOFError
|
||||
EnvironmentError
|
||||
Exception
|
||||
FloatingPointError
|
||||
FutureWarning
|
||||
IOError
|
||||
ImportError
|
||||
IndentationError
|
||||
IndexError
|
||||
KeyError
|
||||
KeyboardInterrupt
|
||||
LookupError
|
||||
MemoryError
|
||||
NameError
|
||||
NotImplementedError
|
||||
OSError
|
||||
OverflowError
|
||||
OverflowWarning
|
||||
PendingDeprecationWarning
|
||||
ReferenceError
|
||||
RuntimeError
|
||||
RuntimeWarning
|
||||
StandardError
|
||||
StopIteration
|
||||
SyntaxError
|
||||
SyntaxWarning
|
||||
SystemError
|
||||
SystemExit
|
||||
TabError
|
||||
TypeError
|
||||
UnboundLocalError
|
||||
UnicodeError
|
||||
UserWarning
|
||||
ValueError
|
||||
Warning
|
||||
WindowsError
|
||||
ZeroDivisionError
|
||||
Ellipsis
|
||||
False
|
||||
None
|
||||
NotImplemented
|
||||
True
|
||||
UnicodeDecodeError
|
||||
UnicodeEncodeError
|
||||
UnicodeTranslateError
|
||||
__debug__
|
||||
__import__
|
||||
abs
|
||||
apply
|
||||
basestring
|
||||
bool
|
||||
buffer
|
||||
callable
|
||||
chr
|
||||
classmethod
|
||||
cmp
|
||||
coerce
|
||||
compile
|
||||
complex
|
||||
copyright
|
||||
credits
|
||||
delattr
|
||||
dict
|
||||
dir
|
||||
divmod
|
||||
enumerate
|
||||
eval
|
||||
execfile
|
||||
exit
|
||||
file
|
||||
filter
|
||||
float
|
||||
getattr
|
||||
globals
|
||||
hasattr
|
||||
hash
|
||||
help
|
||||
hex
|
||||
id
|
||||
input
|
||||
int
|
||||
intern
|
||||
isinstance
|
||||
issubclass
|
||||
iter
|
||||
len
|
||||
license
|
||||
list
|
||||
locals
|
||||
long
|
||||
map
|
||||
max
|
||||
min
|
||||
object
|
||||
oct
|
||||
open
|
||||
ord
|
||||
pow
|
||||
property
|
||||
quit
|
||||
range
|
||||
raw_input
|
||||
reduce
|
||||
reload
|
||||
repr
|
||||
round
|
||||
setattr
|
||||
slice
|
||||
staticmethod
|
||||
str
|
||||
sum
|
||||
super
|
||||
tuple
|
||||
type
|
||||
unichr
|
||||
unicode
|
||||
vars
|
||||
xrange
|
||||
zip
|
||||
__base__
|
||||
__bases__
|
||||
__basicsize__
|
||||
__class__
|
||||
__dict__
|
||||
__dictoffset__
|
||||
__doc__
|
||||
__flags__
|
||||
__itemsize__
|
||||
__module__
|
||||
__mro__
|
||||
__name__
|
||||
__self__
|
||||
__weakrefoffset__
|
||||
__abs__
|
||||
__add__
|
||||
__and__
|
||||
__call__
|
||||
__cmp__
|
||||
__coerce__
|
||||
__complex__
|
||||
__contains__
|
||||
__del__
|
||||
__delattr__
|
||||
__delitem__
|
||||
__delslice__
|
||||
__div__
|
||||
__divmod__
|
||||
__eq__
|
||||
__float__
|
||||
__floordiv__
|
||||
__ge__
|
||||
__get__
|
||||
__getattribute__
|
||||
__getitem__
|
||||
__getnewargs__
|
||||
__getslice__
|
||||
__gt__
|
||||
__hash__
|
||||
__hex__
|
||||
__iadd__
|
||||
__iand__
|
||||
__idiv__
|
||||
__ifloordiv__
|
||||
__ilshift__
|
||||
__imod__
|
||||
__imul__
|
||||
__init__
|
||||
__int__
|
||||
__invert__
|
||||
__ior__
|
||||
__ipow__
|
||||
__irshift__
|
||||
__isub__
|
||||
__iter__
|
||||
__itruediv__
|
||||
__ixor__
|
||||
__le__
|
||||
__len__
|
||||
__long__
|
||||
__lshift__
|
||||
__lt__
|
||||
__mod__
|
||||
__mul__
|
||||
__ne__
|
||||
__neg__
|
||||
__new__
|
||||
__nonzero__
|
||||
__oct__
|
||||
__or__
|
||||
__pos__
|
||||
__pow__
|
||||
__radd__
|
||||
__rand__
|
||||
__rdiv__
|
||||
__rdivmod__
|
||||
__reduce__
|
||||
__reduce_ex__
|
||||
__repr__
|
||||
__rfloordiv__
|
||||
__rlshift__
|
||||
__rmod__
|
||||
__rmul__
|
||||
__ror__
|
||||
__rpow__
|
||||
__rrshift__
|
||||
__rshift__
|
||||
__rsub__
|
||||
__rtruediv__
|
||||
__rxor__
|
||||
__setattr__
|
||||
__setitem__
|
||||
__setslice__
|
||||
__str__
|
||||
__sub__
|
||||
__subclasses__
|
||||
__truediv__
|
||||
__xor__
|
||||
append
|
||||
capitalize
|
||||
center
|
||||
clear
|
||||
close
|
||||
conjugate
|
||||
copy
|
||||
count
|
||||
decode
|
||||
encode
|
||||
endswith
|
||||
expandtabs
|
||||
extend
|
||||
fileno
|
||||
find
|
||||
flush
|
||||
fromkeys
|
||||
get
|
||||
has_key
|
||||
index
|
||||
indices
|
||||
insert
|
||||
isalnum
|
||||
isalpha
|
||||
isatty
|
||||
isdecimal
|
||||
isdigit
|
||||
islower
|
||||
isnumeric
|
||||
isspace
|
||||
istitle
|
||||
isupper
|
||||
items
|
||||
iteritems
|
||||
iterkeys
|
||||
itervalues
|
||||
join
|
||||
keys
|
||||
ljust
|
||||
lower
|
||||
lstrip
|
||||
mro
|
||||
next
|
||||
pop
|
||||
popitem
|
||||
read
|
||||
readinto
|
||||
readline
|
||||
readlines
|
||||
remove
|
||||
replace
|
||||
reverse
|
||||
rfind
|
||||
rindex
|
||||
rjust
|
||||
rstrip
|
||||
seek
|
||||
setdefault
|
||||
sort
|
||||
split
|
||||
splitlines
|
||||
startswith
|
||||
strip
|
||||
swapcase
|
||||
tell
|
||||
title
|
||||
translate
|
||||
truncate
|
||||
update
|
||||
upper
|
||||
values
|
||||
write
|
||||
writelines
|
||||
xreadlines
|
||||
zfill
|
||||
closed
|
||||
co_argcount
|
||||
co_cellvars
|
||||
co_code
|
||||
co_consts
|
||||
co_filename
|
||||
co_firstlineno
|
||||
co_flags
|
||||
co_freevars
|
||||
co_lnotab
|
||||
co_name
|
||||
co_names
|
||||
co_nlocals
|
||||
co_stacksize
|
||||
co_varnames
|
||||
f_back
|
||||
f_builtins
|
||||
f_code
|
||||
f_exc_traceback
|
||||
f_exc_type
|
||||
f_exc_value
|
||||
f_globals
|
||||
f_lasti
|
||||
f_lineno
|
||||
f_locals
|
||||
f_restricted
|
||||
f_trace
|
||||
func_closure
|
||||
func_code
|
||||
func_defaults
|
||||
func_dict
|
||||
func_doc
|
||||
func_globals
|
||||
func_name
|
||||
gi_frame
|
||||
gi_running
|
||||
im_class
|
||||
im_func
|
||||
im_self
|
||||
imag
|
||||
mode
|
||||
name
|
||||
newlines
|
||||
real
|
||||
softspace
|
||||
start
|
||||
step
|
||||
stop
|
||||
BooleanType
|
||||
BufferType
|
||||
BuiltinFunctionType
|
||||
BuiltinMethodType
|
||||
ClassType
|
||||
CodeType
|
||||
ComplexType
|
||||
DictProxyType
|
||||
DictType
|
||||
DictionaryType
|
||||
EllipsisType
|
||||
FileType
|
||||
FloatType
|
||||
FrameType
|
||||
FunctionType
|
||||
GeneratorType
|
||||
InstanceType
|
||||
IntType
|
||||
LambdaType
|
||||
ListType
|
||||
LongType
|
||||
MethodType
|
||||
ModuleType
|
||||
NoneType
|
||||
NotImplementedType
|
||||
ObjectType
|
||||
SliceType
|
||||
StringType
|
||||
StringTypes
|
||||
TracebackType
|
||||
TupleType
|
||||
TypeType
|
||||
UnboundMethodType
|
||||
UnicodeType
|
||||
XRangeType
|
||||
__builtins__
|
||||
__file__
|
||||
"""
|
||||
pythonbuiltinfunctions = pythonbuiltinfunctions_txt.split()
|
||||
|
||||
javakeywords_txt = """
|
||||
abstract
|
||||
boolean
|
||||
break
|
||||
byte
|
||||
case
|
||||
catch
|
||||
char
|
||||
class
|
||||
continue
|
||||
default
|
||||
delegate
|
||||
do
|
||||
double
|
||||
else
|
||||
extends
|
||||
false
|
||||
final
|
||||
finally
|
||||
float
|
||||
for
|
||||
if
|
||||
implements
|
||||
import
|
||||
instanceof
|
||||
int
|
||||
interface
|
||||
long
|
||||
native
|
||||
new
|
||||
null
|
||||
package
|
||||
private
|
||||
protected
|
||||
public
|
||||
return
|
||||
short
|
||||
static
|
||||
super
|
||||
switch
|
||||
synchronized
|
||||
this
|
||||
throw
|
||||
throws
|
||||
transient
|
||||
true
|
||||
try
|
||||
void
|
||||
volatile
|
||||
while
|
||||
goto
|
||||
const
|
||||
strictfp
|
||||
"""
|
||||
javakeywords = javakeywords_txt.split()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
delphikeywords_txt = """
|
||||
And
|
||||
Array
|
||||
As
|
||||
Begin
|
||||
Case
|
||||
Class
|
||||
Const
|
||||
Constructor
|
||||
Destructor
|
||||
Div
|
||||
Do
|
||||
DownTo
|
||||
Else
|
||||
End
|
||||
Except
|
||||
File
|
||||
Finally
|
||||
For
|
||||
System
|
||||
Goto
|
||||
If
|
||||
Implementation
|
||||
In
|
||||
Inherited
|
||||
Interface
|
||||
System
|
||||
Is
|
||||
Mod
|
||||
Not
|
||||
System
|
||||
Of
|
||||
On
|
||||
Or
|
||||
Packed
|
||||
System
|
||||
System
|
||||
System
|
||||
Raise
|
||||
Record
|
||||
Repeat
|
||||
Set
|
||||
Shl
|
||||
Shr
|
||||
Then
|
||||
ThreadVar
|
||||
To
|
||||
Try
|
||||
Type
|
||||
Unit
|
||||
Until
|
||||
Uses
|
||||
Var
|
||||
While
|
||||
With
|
||||
Xor
|
||||
"""
|
||||
delphikeywords = delphikeywords_txt.split()
|
||||
delphikeywords = [ x.lower() for x in delphikeywords ] # delphi is case insensitive, so convert everything to lowercase for comparisons
|
32
data/plugins/pynsource/plugin.xml
Normal file
32
data/plugins/pynsource/plugin.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- vim:sw=2:et:
|
||||
-->
|
||||
<plugin name="PyNSource"
|
||||
version="0.1"
|
||||
author="Arjan Molenaar">
|
||||
<description>
|
||||
Code reverse engineer plugin for Python source code.
|
||||
|
||||
This plugin uses PyNSource, written by Andy Bulka
|
||||
[http://www.atug.com/andypatterns/pynsource.htm].
|
||||
</description>
|
||||
<require>
|
||||
<!--
|
||||
<module name="some.module"/>
|
||||
-->
|
||||
<plugin name="Diagram Layout"/>
|
||||
</require>
|
||||
<provide>
|
||||
<action id="PyNSource"
|
||||
label="_Import Python files"
|
||||
tooltip=""
|
||||
class="PyNSourceAction" slot="FileImportSlot">
|
||||
<!--
|
||||
Add optional dependencies to this action. The action is then updated
|
||||
when actions defined in the depends tag are executed.
|
||||
- ->
|
||||
<depends action=""/>
|
||||
-->
|
||||
</action>
|
||||
</provide>
|
||||
</plugin>
|
1250
data/plugins/pynsource/pynsource.py
Normal file
1250
data/plugins/pynsource/pynsource.py
Normal file
File diff suppressed because it is too large
Load Diff
154
data/plugins/pynsource/pynsourcewindow.py
Normal file
154
data/plugins/pynsource/pynsourcewindow.py
Normal file
@ -0,0 +1,154 @@
|
||||
# vim:sw=4:et
|
||||
|
||||
"""A GUI for the Plugin Editor.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import gobject
|
||||
import pango
|
||||
import gtk
|
||||
import gaphor
|
||||
from gaphor.ui.abstractwindow import AbstractWindow
|
||||
from gaphor.plugin import resource
|
||||
|
||||
from pynsource import PySourceAsText
|
||||
from engineer import Engineer
|
||||
|
||||
NAME_COLUMN = 0
|
||||
|
||||
class PyNSourceWindow(AbstractWindow):
|
||||
|
||||
menu = ('_File', ('FileClose',))
|
||||
|
||||
def __init__(self, main_window):
|
||||
AbstractWindow.__init__(self)
|
||||
self.main_window = main_window
|
||||
|
||||
def construct(self):
|
||||
filelist = gtk.ListStore(gobject.TYPE_STRING)
|
||||
filelist.connect('row-inserted', self.on_view_rows_changed)
|
||||
filelist.connect('row-deleted', self.on_view_rows_changed)
|
||||
|
||||
hbox = gtk.HBox()
|
||||
|
||||
frame = gtk.Frame('Files to reverse-engineer')
|
||||
frame.set_border_width(8)
|
||||
frame.show()
|
||||
hbox.pack_start(frame, expand=True)
|
||||
|
||||
treeview = gtk.TreeView(filelist)
|
||||
treeview.set_property('headers-visible', False)
|
||||
selection = treeview.get_selection()
|
||||
selection.set_mode('single')
|
||||
treeview.set_size_request(200, -1)
|
||||
|
||||
scrolled_window = gtk.ScrolledWindow()
|
||||
scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
scrolled_window.set_shadow_type(gtk.SHADOW_IN)
|
||||
scrolled_window.set_border_width(4)
|
||||
scrolled_window.add(treeview)
|
||||
frame.add(scrolled_window)
|
||||
scrolled_window.show()
|
||||
|
||||
cell = gtk.CellRendererText()
|
||||
column = gtk.TreeViewColumn("Filename", cell, text=NAME_COLUMN)
|
||||
treeview.append_column(column)
|
||||
|
||||
bbox = gtk.VButtonBox()
|
||||
bbox.set_layout(gtk.BUTTONBOX_SPREAD)
|
||||
bbox.set_border_width(10)
|
||||
button = gtk.Button(stock='gtk-add')
|
||||
button.connect('clicked', self.on_add_clicked)
|
||||
bbox.add(button)
|
||||
self.add_button = button
|
||||
|
||||
button = gtk.Button('Add dir...')
|
||||
button.connect('clicked', self.on_add_dir_clicked)
|
||||
bbox.add(button)
|
||||
self.add_dir_button = button
|
||||
|
||||
button = gtk.Button(stock='gtk-remove')
|
||||
button.connect('clicked', self.on_remove_clicked)
|
||||
button.set_property('sensitive', False)
|
||||
bbox.add(button)
|
||||
self.remove_button = button
|
||||
|
||||
button = gtk.Button(stock='gtk-execute')
|
||||
button.connect('clicked', self.on_execute_clicked)
|
||||
button.set_property('sensitive', False)
|
||||
bbox.add(button)
|
||||
self.execute_button = button
|
||||
|
||||
hbox.pack_start(bbox, expand=False)
|
||||
hbox.show_all()
|
||||
|
||||
self._construct_window(name='pynsourcewindow',
|
||||
title='Reverse engineer Python code',
|
||||
size=(400, 300),
|
||||
contents=hbox)
|
||||
self.filelist = filelist
|
||||
self.treeview = treeview
|
||||
|
||||
treeview.connect_after('cursor_changed', self.on_view_cursor_changed)
|
||||
|
||||
def on_view_cursor_changed(self, view):
|
||||
selection = view.get_selection()
|
||||
filelist, iter = selection.get_selected()
|
||||
if not iter:
|
||||
self.remove_button.set_property('sensitive', False)
|
||||
return
|
||||
#element = filelist.get_value(iter, NAME_COLUMN)
|
||||
self.remove_button.set_property('sensitive', True)
|
||||
#self.update_detail(element)
|
||||
|
||||
def on_view_rows_changed(self, view, *args):
|
||||
iter = None
|
||||
try:
|
||||
iter = view.get_iter('0')
|
||||
except ValueError:
|
||||
pass
|
||||
self.execute_button.set_property('sensitive', bool(iter))
|
||||
|
||||
def on_add_clicked(self, button):
|
||||
filesel = gtk.FileSelection('Open Python file')
|
||||
filesel.hide_fileop_buttons()
|
||||
response = filesel.run()
|
||||
filename = filesel.get_filename()
|
||||
filesel.destroy()
|
||||
if filename and response == gtk.RESPONSE_OK:
|
||||
iter = self.filelist.append()
|
||||
self.filelist.set_value(iter, NAME_COLUMN, filename)
|
||||
#self.execute_button.set_property('
|
||||
|
||||
def on_add_dir_clicked(self, button):
|
||||
pass
|
||||
|
||||
def on_remove_clicked(self, button):
|
||||
selection = self.treeview.get_selection()
|
||||
filelist, iter = selection.get_selected()
|
||||
if not iter:
|
||||
return
|
||||
element = filelist.remove(iter)
|
||||
|
||||
self.remove_button.set_property('sensitive', False)
|
||||
|
||||
def on_execute_clicked(self, button):
|
||||
#print dir(filelist)
|
||||
files = []
|
||||
for row in self.filelist:
|
||||
files.append(row[0])
|
||||
|
||||
engineer = Engineer()
|
||||
engineer.process(files)
|
||||
self.close()
|
||||
|
||||
# Open and select the new diagram in the main window:
|
||||
path = self.main_window.get_model().path_from_element(engineer.diagram)
|
||||
# Expand the first row:
|
||||
self.main_window.get_tree_view().expand_row(path[:-1], False)
|
||||
# Select the diagram, so it can be opened by the OpenModelElement action
|
||||
selection = self.main_window.get_tree_view().get_selection()
|
||||
selection.select_path(path)
|
||||
self.main_window.execute_action('OpenModelElement')
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -130,29 +130,29 @@ class AssociationItem(RelationshipItem, diacanvas.CanvasGroupable):
|
||||
# Update line ends using the aggregation and isNavigable values:
|
||||
|
||||
if head_subject.aggregation == intern('composite'):
|
||||
self.set(has_head=1, head_a=20, head_b=10, head_c=6, head_d=6,
|
||||
head_fill_color=diacanvas.color(0,0,0,255))
|
||||
elif head_subject.aggregation == intern('shared'):
|
||||
self.set(has_head=1, head_a=20, head_b=10, head_c=6, head_d=6,
|
||||
head_fill_color=diacanvas.color(255,255,255,255))
|
||||
elif not head_subject.owningAssociation:
|
||||
# This side is navigable:
|
||||
self.set(has_head=1, head_a=0, head_b=15, head_c=6, head_d=6)
|
||||
else:
|
||||
self.set(has_head=0)
|
||||
|
||||
if tail_subject.aggregation == intern('composite'):
|
||||
self.set(has_tail=1, tail_a=20, tail_b=10, tail_c=6, tail_d=6,
|
||||
tail_fill_color=diacanvas.color(0,0,0,255))
|
||||
elif tail_subject.aggregation == intern('shared'):
|
||||
elif head_subject.aggregation == intern('shared'):
|
||||
self.set(has_tail=1, tail_a=20, tail_b=10, tail_c=6, tail_d=6,
|
||||
tail_fill_color=diacanvas.color(255,255,255,255))
|
||||
elif not tail_subject.owningAssociation:
|
||||
elif not head_subject.owningAssociation:
|
||||
# This side is navigable:
|
||||
self.set(has_tail=1, tail_a=0, tail_b=15, tail_c=6, tail_d=6)
|
||||
else:
|
||||
self.set(has_tail=0)
|
||||
|
||||
if tail_subject.aggregation == intern('composite'):
|
||||
self.set(has_head=1, head_a=20, head_b=10, head_c=6, head_d=6,
|
||||
head_fill_color=diacanvas.color(0,0,0,255))
|
||||
elif tail_subject.aggregation == intern('shared'):
|
||||
self.set(has_head=1, head_a=20, head_b=10, head_c=6, head_d=6,
|
||||
head_fill_color=diacanvas.color(255,255,255,255))
|
||||
elif not tail_subject.owningAssociation:
|
||||
# This side is navigable:
|
||||
self.set(has_head=1, head_a=0, head_b=15, head_c=6, head_d=6)
|
||||
else:
|
||||
self.set(has_head=0)
|
||||
|
||||
RelationshipItem.on_update(self, affine)
|
||||
|
||||
handles = self.handles
|
||||
|
@ -151,6 +151,8 @@ class GaphorLoader(handler.ContentHandler):
|
||||
def startDocument(self):
|
||||
"""Start of document: all our attributes are initialized.
|
||||
"""
|
||||
self.version = None
|
||||
self.gaphor_version = None
|
||||
self.elements = gaphor.misc.odict.odict() # map id: element/canvasitem
|
||||
self.__stack = []
|
||||
self.value_is_cdata = 0
|
||||
@ -230,6 +232,8 @@ class GaphorLoader(handler.ContentHandler):
|
||||
# The <gaphor> tag is the toplevel tag:
|
||||
elif state == ROOT and name == 'gaphor':
|
||||
assert attrs['version'] in ('3.0',)
|
||||
self.version = attrs['version']
|
||||
self.gaphor_version = attrs.get('gaphor_version')
|
||||
self.push(None, GAPHOR)
|
||||
|
||||
else:
|
||||
|
@ -78,8 +78,8 @@ class Plugin(object):
|
||||
pluginstatus[p.name] = bool(p.initialized)
|
||||
for p in self.required_plugins:
|
||||
if not pluginstatus.get(p):
|
||||
self.status = 'Plugin %s is required by this plugin' % p
|
||||
log.debug(self.status)
|
||||
self.status = 'Plugin %s is a prerequisite' % p
|
||||
log.debug(self.status + ' for %s' % self.name)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -148,7 +148,7 @@ def load_elements(elements, factory, status_queue=None):
|
||||
if status_queue:
|
||||
status_queue(status)
|
||||
|
||||
def load_elements_generator(elements, factory):
|
||||
def load_elements_generator(elements, factory, gaphor_version=None):
|
||||
"""Load a file and create a model if possible.
|
||||
Exceptions: IOError, ValueError.
|
||||
"""
|
||||
@ -233,7 +233,9 @@ def load_elements_generator(elements, factory):
|
||||
log.error('Loading %s.%s with value %s failed' % (type(elem.element).__name__, name, ref.element.id))
|
||||
raise
|
||||
|
||||
|
||||
# Fix version inconsistencies
|
||||
version_0_5_2(elements, factory, gaphor_version)
|
||||
|
||||
# do a postload:
|
||||
for id, elem in elements.items():
|
||||
yield update_status_queue()
|
||||
@ -269,6 +271,7 @@ def load_generator(filename, factory=None):
|
||||
else:
|
||||
yield percentage
|
||||
elements = loader.elements
|
||||
gaphor_version = loader.gaphor_version
|
||||
#elements = parser.parse(filename)
|
||||
#yield 100
|
||||
except Exception, e:
|
||||
@ -280,7 +283,7 @@ def load_generator(filename, factory=None):
|
||||
factory = gaphor.resource(UML.ElementFactory)
|
||||
factory.flush()
|
||||
gc.collect()
|
||||
for percentage in load_elements_generator(elements, factory):
|
||||
for percentage in load_elements_generator(elements, factory, gaphor_version):
|
||||
if percentage:
|
||||
yield percentage / 2 + 50
|
||||
else:
|
||||
@ -291,4 +294,24 @@ def load_generator(filename, factory=None):
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
raise
|
||||
|
||||
|
||||
|
||||
# Version inconsistencies can be fixed:
|
||||
|
||||
def version_0_5_2(elements, factory, gaphor_version):
|
||||
"""Before version 0.5.2, the wrong memberEnd of the association was
|
||||
holding the aggregation information.
|
||||
"""
|
||||
if tuple(gaphor_version.split('.')) < (0, 5, 2):
|
||||
log.info('Fix composition on Associations (file version: %s)' % gaphor_version)
|
||||
for elem in elements.values():
|
||||
try:
|
||||
if elem.type == 'Association':
|
||||
a = elem.element
|
||||
agg1 = a.memberEnd[0].aggregation
|
||||
agg2 = a.memberEnd[1].aggregation
|
||||
a.memberEnd[0].aggregation = agg2
|
||||
a.memberEnd[1].aggregation = agg1
|
||||
except Exception, e:
|
||||
log.error('Error while updating Association', e)
|
||||
|
||||
|
@ -29,6 +29,8 @@ class MainWindow(AbstractWindow):
|
||||
'FileSaveAs',
|
||||
'<FileSaveSlot>',
|
||||
'separator',
|
||||
_('_Import'), (
|
||||
'<FileImportSlot>',),
|
||||
_('_Export'), (
|
||||
'FileExportSVG',
|
||||
'<FileExportSlot>'),
|
||||
@ -289,7 +291,6 @@ class MainWindow(AbstractWindow):
|
||||
"""Window is destroyed... Quit the application.
|
||||
"""
|
||||
AbstractWindow._on_window_destroy(self, window)
|
||||
#gaphor.resource(UML.ElementFactory).disconnect(self.__on_element_factory_signal)
|
||||
del self.model
|
||||
del self.view
|
||||
|
||||
@ -308,8 +309,6 @@ class MainWindow(AbstractWindow):
|
||||
|
||||
def on_view_row_activated(self, view, path, column):
|
||||
self._check_state(AbstractWindow.STATE_ACTIVE)
|
||||
node = self.get_model().node_from_path(path)
|
||||
element = self.get_model().element_from_node(node)
|
||||
self.execute_action('OpenModelElement')
|
||||
|
||||
def on_view_cursor_changed(self, view):
|
||||
|
24
setup.py
24
setup.py
@ -133,6 +133,13 @@ class config_Gaphor(Command):
|
||||
self.config_failed.append(module)
|
||||
|
||||
|
||||
class build_Gaphor(build):
|
||||
|
||||
def run(self):
|
||||
self.run_command('config')
|
||||
build.run(self)
|
||||
|
||||
|
||||
class version_py:
|
||||
|
||||
def generate_version(self, dir, data_dir):
|
||||
@ -163,7 +170,6 @@ class build_py_Gaphor(build_py, version_py):
|
||||
description = "build_py and generate gaphor/UML/uml2.py."
|
||||
|
||||
def run(self):
|
||||
self.run_command('config')
|
||||
build_py.run(self)
|
||||
sys.path.insert(0, self.build_lib)
|
||||
# All data is stored in the local data directory
|
||||
@ -300,6 +306,9 @@ class run_Gaphor(Command):
|
||||
sys.exit(not result.wasSuccessful())
|
||||
elif self.file:
|
||||
print 'Executing file: %s...' % self.file
|
||||
dir, f = os.path.split(self.file)
|
||||
print 'Extending PYTHONPATH with %s' % dir
|
||||
sys.path.append(dir)
|
||||
execfile(self.file, {})
|
||||
else:
|
||||
print 'Launching Gaphor...'
|
||||
@ -341,6 +350,9 @@ class run_Gaphor(Command):
|
||||
#else:
|
||||
# pass
|
||||
|
||||
def plugin_data(name):
|
||||
return 'plugins/%s' % name, glob('data/plugins/%s/*.*' % name)
|
||||
|
||||
setup(name='gaphor',
|
||||
version=VERSION,
|
||||
description="Gaphor is a UML modeling tool",
|
||||
@ -362,9 +374,11 @@ setup(name='gaphor',
|
||||
# data files are relative to <prefix>/share/gaphor (see setup.cfg)
|
||||
data_files=[('', ['data/icons.xml']),
|
||||
('pixmaps', glob('data/pixmaps/*.png')),
|
||||
('plugins/plugineditor', glob('data/plugins/plugineditor/*.*')),
|
||||
('plugins/liveobjectbrowser', glob('data/plugins/liveobjectbrowser/*.*')),
|
||||
('plugins/checkmetamodel', glob('data/plugins/checkmetamodel/*.*'))
|
||||
plugin_data('plugineditor'),
|
||||
plugin_data('checkmetamodel'),
|
||||
plugin_data('diagramlayout'),
|
||||
plugin_data('liveobjectbrowser'),
|
||||
plugin_data('pynsource')
|
||||
],
|
||||
scripts=['bin/gaphor'],
|
||||
|
||||
@ -372,7 +386,7 @@ setup(name='gaphor',
|
||||
cmdclass={'config': config_Gaphor,
|
||||
'build_py': build_py_Gaphor,
|
||||
#'install_schemas': install_schemas,
|
||||
'build': build,
|
||||
'build': build_Gaphor,
|
||||
# 'build_ext': BuildExt,
|
||||
'build_mo': build_mo,
|
||||
'build_pot': build_pot,
|
||||
|
@ -192,7 +192,7 @@ class Writer:
|
||||
a += ', lower=%s' % lower
|
||||
if upper != '*':
|
||||
a += ', upper=%s' % upper
|
||||
if tail.get('aggregation') == 'composite':
|
||||
if head.get('aggregation') == 'composite':
|
||||
a += ', composite=True'
|
||||
|
||||
# Add the opposite property if the head itself is navigable:
|
||||
|
Loading…
x
Reference in New Issue
Block a user