*** empty log message ***

git-svn-id: file:///Users/arjan/backup/gaphor/trunk/gaphor@401 a8418922-720d-0410-834f-a69b97ada669
This commit is contained in:
Arjan Molenaar 2004-09-27 06:37:00 +00:00
parent f7055d0df1
commit a9ee9dfeba
18 changed files with 2925 additions and 253 deletions

View File

@ -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.

View 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)

View 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>

View 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'

View 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()

View 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)

View 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

View 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>

File diff suppressed because it is too large Load Diff

View 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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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):

View File

@ -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,

View File

@ -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: