Skip to content

Commit

Permalink
*** empty log message ***
Browse files Browse the repository at this point in the history
git-svn-id: file:///Users/arjan/backup/gaphor/trunk/gaphor@365 a8418922-720d-0410-834f-a69b97ada669
  • Loading branch information
amolenaar committed Jun 26, 2004
1 parent 5418f25 commit 969bf37
Show file tree
Hide file tree
Showing 22 changed files with 705 additions and 183 deletions.
7 changes: 7 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
2004-06-23 Arjan Molenaar <arjanmolenaar@hetnet.nl>

* gaphor/ui/mainwindow.py, gaphor/ui/toolbox.py: explicitly import
actions.
* doc/plugin.txt: explenation how plugins work
* setup.py: Boost version to 0.5.0.

2004-06-22 Arjan Molenaar <arjanmolenaar@hetnet.nl>

* gaphor/plugin.py: new file. provided stuff needed by possible
Expand Down
6 changes: 3 additions & 3 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ As always, there is much to do...
#- Make sure than, when a model is loaded and saved again, both save files do
# not differ.

- Exporting diagrams to UML XMI, code, images (SVG/png), etc.
- Exporting diagrams to UML XMI, code, images (SVG/png), etc. - make a plugin!

#- Set up a plugin architecture. Since the internals of gaphor are pretty
modular, plugins should not be that hard.
Expand Down Expand Up @@ -73,10 +73,10 @@ and of course the usual: Comment, Association, Generalization and Dependency.
Thoughts:
- Profiles are reusable and its common to share them with different models.
- Stereotypes can only be owned by Profiles, not by (normal) Packages.
- I think having associations in this one is not very useful, though.
- We have to do a lookup if a MetaClass is actually part of the model.
- a stereotype can contain an image, that can be used in stead of its name
- Profiles should be saved with the model too.
- Profiles should be saved with the model too. And iit should be possible to
"update" a profile within a model.

Maybe it would be nice to create Stereotypes without creating the diagrams.
Via a dialog once can select which class (Operation, Class, etc.) is
Expand Down
15 changes: 8 additions & 7 deletions data/plugins/checkmetamodel/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# vim:sw=4:et

print 'plugin',__file__

import gaphor.plugin
from checkmodelgui import CheckModelWindow
from checkmodel import check_associations, check_attributes

class CheckMetamodelAction(gaphor.plugin.Action):

def init(self, window):
self._window = window

def execute(self):
print 'UMLSanityCheck'

print 'CheckMetamodelAction'
# TODO: override checkmodel.report
cw = CheckModelWindow()
cw.construct()
self.window.add_transient_window(cw)
cw.run()
85 changes: 85 additions & 0 deletions data/plugins/checkmetamodel/checkmodel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# vim:sw=4:et

from gaphor import resource
from gaphor import storage
import gaphor.UML as UML
import os.path as path

def report(element, message):
print '%s: %s' % (type(element).__name__, message)

def get_subsets(tagged_value):
subsets = []
if tagged_value.find('subsets') != -1:
subsets = tagged_value[tagged_value.find('subsets') + len('subsets'):]
subsets = subsets.replace(' ', '').replace('\n', '').replace('\r', '')
subsets = subsets.split(',')
return subsets

def get_redefine(tagged_value):
redefines = tag[tag.find('redefines') + len('redefines'):]
# remove all whitespaces and stuff
redefines = redefines.replace(' ', '').replace('\n', '').replace('\r', '')
redefines = redefines.split(',')
assert len(redefines) == 1
return redefines[0]

def get_superclasses(class_):
for superclass in class_.superClass:
gen = 1

def check_association_end_subsets(end):
subsets = get_subsets(end.taggedValue and end.taggedValue.value or '')
opposite_subsets = get_subsets(end.opposite.taggedValue and end.opposite.taggedValue.value or '')
subset_properties = UML.select(lambda e: e.isKindOf(UML.Property) and e.name in subsets)

# TODO: check if properties belong to a superclass of the end's class

# TODO: check if the association end is navigable when the subsets are navigable
for p in subset_properties:
pass

# Check if bi-directional derived unions are bi-directional on this association
for p in subset_properties:
if p.opposite.name and p.opposite.name not in opposite_subsets:
report(end, 'Subset not defined on both sides. (%s, %s)' % (p.name, p.opposite.name))

# Check if multiplicity of the properties matches the multiplicity of the subsets
for p in subset_properties:
if p.upperValue:
if not end.upperValue:
report(end, 'Association end %s has no upper value, but the subset %s has' % (end.name, p.name))
elif p.upperValue.value < end.upperValue.value:
report(end, 'Association end %s has has a bigger upper value than subse %s' % (end.name, p.name))

def check_association_end(end):
check_association_end_subsets(end)

def check_associations():
for a in UML.select(lambda e: e.isKindOf(UML.Association)):
assert len(a.memberEnd) == 2
head = a.memberEnd[0]
tail = a.memberEnd[1]
check_association_end(head)
check_association_end(tail)

def check_attributes():
for a in UML.select(lambda e: e.isKindOf(UML.Property) and not e.association):
if not a.typeValue or not a.typeValue.value:
report(a,'Attribute has no type: %s' % a.name)
elif a.typeValue.value.lower() not in ('string', 'boolean', 'integer', 'unlimitednatural'):
report(a, 'Invalid attribute type: %s' % a.taggedValue.value)

# TODO: Check the sanity of the generated data model.
def check_UML_module():
all_classes = map(getattr, [UML] * len(dir(UML)), dir(UML))
for c in all_classes:
if not isinstance(c, UML.Element):
continue
# TODO: check derived unions.

if __name__ == '__main__':
storage.load(path.join('gaphor', 'UML', 'uml2.gaphor'))
check_associations()
check_attributes()

87 changes: 87 additions & 0 deletions data/plugins/checkmetamodel/checkmodelgui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# vim:sw=4:et

"""A GUI for the checkmodel plugin.
"""

import sys
import gobject
import gtk
import gaphor
from gaphor.ui.abstractwindow import AbstractWindow
from gaphor.plugin import resource
import checkmodel

PYELEMENT_COLUMN = 0
ELEMENT_COLUMN = 1
REASON_COLUMN = 2

class CheckModelWindow(AbstractWindow):

menu = ('_File', ('FileClose',))

def __init__(self):
AbstractWindow.__init__(self)
# Override the report method
checkmodel.report = self.on_report

def construct(self):
model = gtk.ListStore(gobject.TYPE_PYOBJECT,
gobject.TYPE_STRING,
gobject.TYPE_STRING)

treeview = gtk.TreeView(model)
treeview.connect('row-activated', self.on_row_activated)
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.add(treeview)
scrolled_window.show()

cell = gtk.CellRendererText()
column = gtk.TreeViewColumn("Element", cell, text=ELEMENT_COLUMN)
treeview.append_column(column)

cell = gtk.CellRendererText()
column = gtk.TreeViewColumn("Reason", cell, text=REASON_COLUMN)
treeview.append_column(column)
treeview.show()

self._construct_window(name='checkmodel',
title='Check Model',
size=(400, 400),
contents=scrolled_window)
self.model = model
self.treeview = treeview

def run(self):
# TODO: Let this run in a Thread(?)
checkmodel.check_attributes()
checkmodel.check_associations()

def on_report(self, element, message):
log.info('%s: %s' % (type(element).__name__, message))
model = self.model
iter = model.append()
model.set_value(iter, PYELEMENT_COLUMN, element)
model.set_value(iter, ELEMENT_COLUMN, type(element).__name__)
model.set_value(iter, REASON_COLUMN, message)
main = gobject.main_context_default()
main.iteration(False)

def on_row_activated(self, treeview, row, column):
iter = self.model.get_iter(row)
element = self.model.get_value(iter, PYELEMENT_COLUMN)
print 'Looking for element', element
if element.presentation:
main_window = resource('MainWindow')
presentation = element.presentation[0]
diagram = presentation.canvas.diagram
diagram_tab = main_window.show_diagram(diagram)
view = diagram_tab.get_view()
view_item = view.find_view_item(presentation)
diagram_tab.get_view().focus(view_item)

10 changes: 9 additions & 1 deletion data/plugins/checkmetamodel/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@
<plugin name="UML metamodel sanity check"
version="0.1"
author="Arjan Molenaar">
<description>
This plugin provides a means to check the consistency of an UML model. The
rules have been optimized to work with the data model (gaphor/UML/uml2.gaphor),
so their might not be much use, except for hackers.

Also this plugin is a template for future plugins.
</description>

<require>
<!--
Define modules and plugins that are needed for this plugin to function
Expand All @@ -13,13 +21,13 @@
<plugin name="anotherPlugin"/>
-->
</require>

<provide>
<!--
Actions should be defined on the module's toplevel (like in __init__.py).
-->
<action id="CheckMetamodel"
label="Check UML metamodel"
icon-file="uml.png"
tooltip="Check the UML meta model for errors"
class="CheckMetamodelAction" slot="WindowSlot">
<!--
Expand Down
92 changes: 92 additions & 0 deletions doc/plugins.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,95 @@ for the plugin:
- Code that should be ran before and after installation, before and after
removal.

A typical plugin.xml file looks like this:

<?xml version="1.0"?>
<plugin name="UML metamodel sanity check"
version="0.1"
author="Arjan Molenaar">
<description>
A description of what this thing does.
</description>

<require>
<!--
Define modules and plugins that are needed for this plugin to function
properly.
-->
<module name="os.path"/>
<plugin name="anotherPlugin"/>
</require>

<provide>
<!--
Actions should be defined on the module's toplevel (like in __init__.py).
-->
<action id="MyPlugin"
label="Do a typical thing with the plugin"
icon-file="myicon.png"
tooltip="bla bla"
class="MyPluginAction" slot="WindowSlot">
<!--
Add optional dependencies to this action. The action is then updated
when actions defined in the depends tag are executed.
-->
<depends action="ItemFocus"/>
</action>
</provide>
</plugin>


A plugin contains three sections:
description
A description of the plugin (could be shown in a plugin browser for example)
This is just a text field.
require
Prerequisits for this plugin (such as exptic modules and other plugins)
provide
Actions that are provided by this plugin

The require section can contain modules and plugins that are needed for this plugin
to work.

The <provide> section contains the actions that can be added to the application.
An action is a Python class that extends gaphor.plugin.Action (or CheckAction for
checkbox actions or RadioAction for radiobutton actions). An action has an
id
Id is a unique identifier for the action
label
A label that is shown in the menu
tooltip
some extra information
icon-file
A file containing a nice (24x24) image, preferbly a PNG image.
class
The Action class to load from the module. The Action class should be
visible through __init__.py (class names like 'test.TestAction' do not
work)
slot
Slots are predefined places in a menu where new actions can be added.

Slots
-----
Currently the following slots are defined:

Main window

main menu:
slot name menu comment
FileOpenSlot File after the 'Open' item
FileSaveSlot File after the 'Save' item
FileExportSlot File/Export
FileSlot File After the 'Close' item
EditSlot Edit
DiagramSlot Diagram
WindowSlot Window
HelpSlot Help

Items in a diagram can have additional actions too:

item: slot name:
Class ClassPopupSlot

More slots are likely to be defined in the future.

Loading

0 comments on commit 969bf37

Please sign in to comment.