Weiterentwicklung der Softwarearchitektur von

Werbung
TECHNISCHE UNIVERSITÄT CAROLO-WILHELMINA ZU BRAUNSCHWEIG
Institut für Informationssysteme
Prof. Dr. Wolf-Tilo Balke
Bachelorarbeit
Weiterentwicklung der Softwarearchitektur
von Protégé zur Ontologievisualisierung:
Interaktion
Sergey Kovalev
12. Oktober 2009
Aufgabenstellung und Betreuung durch:
Dr. Silke Eckstein
Dipl.-Inform. Andreas Kupfer
Inhalt:
Im Rahmen dieser Bachelorarbeit wurde das Plugin OWLPropViz weiterentwickelt. Alte Visualisierungsfunktionen
wurden wiederhergestellt und erweitert. Visualisierung von äquivalenten Klassen, sowie detaillierte Anzeige der
Visualisierungsinformationen und ein Kontextemenü zur Verbesserung der Benutzerinteraktion wurden entworfen
und implementiert.
Eidesstattliche Erklärung
Hiermit erkläre ich an Eides statt, dass ich die vorliegende Arbeit selbstständig verfasst und keine anderen als die angegebenen Hilfsmittel verwendet habe.
Braunschweig, 9. September 2009
Vorwort
Danksagung
Die Autoren von Protégé erbitten die Angabe des folgenden Satzes in wissenschaftlichen Arbeiten, die in Zusammenhang mit Protégé erstellt werden:
„This work was conducted using the Protégé resource, which is supported
by grant LM007885 from the United States National Library of Medicine.“
iii
Inhaltsverzeichnis
1. Einleitung
1.1. Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2. Aufgabenstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2. Grundlagen
2.1. Semantic Web . . . . . . . . . . .
2.2. Ontologien . . . . . . . . . . . . .
2.2.1. XML . . . . . . . . . . . .
2.2.2. RDF . . . . . . . . . . . .
2.2.3. RDFS . . . . . . . . . . .
2.2.4. OWL . . . . . . . . . . . .
2.3. Protégé . . . . . . . . . . . . . .
2.4. Eclipse Equinox OSGi Framework
2.5. Entwurfsmuster . . . . . . . . . .
2.5.1. Visitor . . . . . . . . . . .
2.5.2. Command . . . . . . . . .
2.5.3. Singleton . . . . . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
in Protégé
. . . . . .
. . . . . .
. . . . . .
. . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
1
2
6
6
7
7
7
8
9
10
10
12
12
13
13
3. Plugin-Entwicklung mit Eclipse
3.1. Eclipse-Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2. Subclipse-Installation . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3. Protégé 4 und OWLPropViz aus dem Repository beziehen . . . . . .
3.3.1. Protégé 4 aus der Repository beziehen . . . . . . . . . . . . .
3.3.2. OWLPropViz aus dem Repository beziehen . . . . . . . . . .
3.4. Protégé kompilieren und Run Configuration für das Plugin erstellen .
3.4.1. Das Plugin OWLPropViz als getrenntes Eclipse-Projekt . . . .
3.4.2. Integration von Protégé 4 und OWLPropViz zu einem EclipseProjekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.5. Updaten von Protégé . . . . . . . . . . . . . . . . . . . . . . . . . . .
15
15
15
16
16
16
17
17
4. Architektur
4.1. Aufbau von OWLViz . . . . . . . . . . . . . . .
4.1.1. Das Paket uk.ac.man.cs.mig.coode.owlviz
4.1.2. Das Paket uk.ac.man.cs.mig.util.graph .
4.2. Aufbau des alten OWLPropViz . . . . . . . . .
22
22
22
23
24
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
18
20
v
Inhaltsverzeichnis
4.3. Aufbau des erweiterten OWLPropViz . . . . . . . . . . . . . . . . . .
4.3.1. Architektur der neuen Funktionalitäten . . . . . . . . . . . . .
4.3.2. Übersicht: Die Paketstruktur . . . . . . . . . . . . . . . . . . .
24
24
28
5. Implementierung
5.1. Erstellung von angepasstem build.xml . . . . . . . . . . . . . . . . . .
5.2. Realisierung der gestellten Aufgaben . . . . . . . . . . . . . . . . . .
5.2.1. Doppelklick führt zum passendem Tab . . . . . . . . . . . . .
5.2.2. Sofortige Aktivierung der Toolbartasten . . . . . . . . . . . .
5.2.3. Automatische Graphenerstellung beim Laden des Plugins . . .
5.2.4. Anzeige der Zusammensetzung der äquivalenten Klassen . . .
5.2.5. Anzeige der Visualisierungsinformationen . . . . . . . . . . . .
5.2.6. Wiederherstellung und Erweiterung der Show/Hide Funktionen
5.2.7. Hinzufügen und Löschen einzelner Knoten . . . . . . . . . . .
5.2.8. Hinzufügen von Kanten . . . . . . . . . . . . . . . . . . . . .
5.3. Realisierung zusätzlicher Aufgaben . . . . . . . . . . . . . . . . . . .
5.3.1. Anzeige von versteckten Eltern- und Kinderknoten . . . . . .
5.3.2. Bugfix: DotParser Exception . . . . . . . . . . . . . . . . . . .
30
30
31
32
32
33
34
36
39
42
43
46
46
47
6. Evaluation
6.1. Funktionstest . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.1.1. Installation des Plugins . . . . . . . . . . . . . . . . . . .
6.1.2. Automatische Grapherstellung und Informationsanzeige .
6.1.3. Aktivierung der Toolbarbuttons beim Laden des Plugins
6.1.4. Kontextmenü: Funktionen Hide und Show . . . . . . . .
6.1.5. Visualisierung der äquivalenten Klassen . . . . . . . . . .
6.1.6. Doppelklick auf einem Knoten . . . . . . . . . . . . . . .
6.1.7. Kontextmenu: Hinzufüge- und Löschoperationen . . . . .
6.1.8. Kontextmenü: Hinzufügen von Kanten . . . . . . . . . .
6.2. Auswertung . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
49
49
49
50
50
51
51
52
53
53
54
7. Schlussbetrachtungen
7.1. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2. Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
55
55
56
A. Zusätzliche Dateien
A.1. plugin.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
59
59
B. Implementierung: Quelltext
B.1. de.tubs.cs.ifis.owlpropvizgraphinfo . . . . . . . .
B.1.1. OWLPropVizGraphInfoViewComponent
B.1.2. GraphItemsStatistics . . . . . . . . . . .
B.2. de.tubs.cs.ifis.owlpropvizgraphinfo.counters . . .
B.2.1. EdgeCounter . . . . . . . . . . . . . . .
61
61
61
62
64
64
vi
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Inhaltsverzeichnis
B.3.
B.4.
B.5.
B.6.
B.7.
B.8.
B.9.
B.2.2. NodesCounter . . . . . . . . . . . . . . . .
de.tubs.cs.ifis.owlpropviz . . . . . . . . . . . . . .
B.3.1. OWLPropVizView . . . . . . . . . . . . .
de.tubs.cs.ifis.owlpropviz.command . . . . . . . .
B.4.1. ShowAllIndividualsPropVizCommand.java
B.4.2. ShowClassCommandWrapper.java . . . . .
B.4.3. ShowSubclassesCommandWrapper.java . .
B.4.4. ShowSuperclassesCommandWrapper.java .
de.tubs.cs.ifis.owlpropviz.ui.options . . . . . . . .
B.5.1. AutoDisplayOptionsPage.java . . . . . . .
B.5.2. OptionsFileIO.java . . . . . . . . . . . . .
B.5.3. OWLPropVizOptions.java . . . . . . . . .
de.tubs.cs.ifis.owlpropviz.util . . . . . . . . . . . .
B.6.1. DisplayManager.java . . . . . . . . . . . .
B.6.2. HideState.java . . . . . . . . . . . . . . . .
B.6.3. HtmlHighlighter.java . . . . . . . . . . . .
B.6.4. PropVizPopupMenu.java . . . . . . . . . .
de.tubs.cs.ifis.owlpropviz.util.event . . . . . . . .
B.7.1. EventManager.java . . . . . . . . . . . . .
de.tubs.cs.ifis.owlpropviz.util.event.createedge . .
B.8.1. CreateEdgeEvent.java . . . . . . . . . . .
B.8.2. CreateEdgeEventType.java . . . . . . . . .
B.8.3. CreateEdgeListener.java . . . . . . . . . .
B.8.4. EdgeCreator.java . . . . . . . . . . . . . .
de.tubs.cs.ifis.owlpropviz.util.event.graphchange .
B.9.1. GraphChangeEvent.java . . . . . . . . . .
B.9.2. GraphChangeEventType.java . . . . . . .
B.9.3. GraphChangeListener.java . . . . . . . . .
C. Inhalt der CD-ROM
C.1. Ordner Quellenangaben_Seiten
C.2. Ordner Ontologien . . . . . . . .
C.3. Ordner Protege_Distribution .
C.4. Ordner Workspace_Eclipse . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
66
66
66
71
71
72
72
73
73
73
74
75
75
75
78
79
79
84
84
85
85
85
85
86
87
87
88
88
.
.
.
.
89
89
89
89
89
vii
Verzeichnis der Abbildungen
viii
1.1. Benutzeroberfläche nach dem Laden von OWLPropViz . . . . . . . .
1.2. Mit OWLPropViz visualisierte Ontologie uni.owl [Vor09] . . . . . . .
1.3. Die kleinen Pfeile . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2
3
5
3.1. Suche nach „.svn“-Datei im Windows Explorer . . . . . . . . . . . . .
19
4.1. Alte Struktur des OWLPropViz . . . . . . . . . . . . . . . . . . . . .
25
5.1. Kontextmenu Show . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2. Kontextmenu Hide . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.3. Die kleinen Pfeile . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
40
42
46
6.1.
6.2.
6.3.
6.4.
49
51
52
54
Das OWLPropViz-Tab . . . . .
Ergebnis der Testfälle 2 und 3 .
Anzeige der äquivalenten Klasse
Ergebnis des Testfalls 8 . . . . .
. . . . . . .
. . . . . . .
„Professor“
. . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Listings
2.1. Beispiel einer XML Struktur . . . . . .
2.2. Beispiel eines Tripels in Turtle RDF . .
2.3. Prefix . . . . . . . . . . . . . . . . . .
2.4. Ein Tripel im XML Syntax . . . . . . .
2.5. Äquivalente Klassen . . . . . . . . . .
2.6. Property Restriction . . . . . . . . . .
2.7. Manifest.mf des OWLPropViz-Plugins
2.8. Ausschnitt aus plugin.xml . . . . . . .
2.9. Schnittstelle der zu besuchenden Klasse
2.10. Eine Visitor-Klasse . . . . . . . . . . .
2.11. Entwurfsmuster Command . . . . . . .
2.12. Einbinden einer Command-Klasse . . .
2.13. Eine Singleton-Klasse . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7
8
8
8
9
9
11
11
12
12
13
13
14
3.1. Parameter der VM Arguments . . . . . . . . . . . . . . . . . . . . . .
18
4.1. Weitere Pakete von OWLPropViz . . . . . . . . . . . . . . . . . . . .
29
5.1. Neuer Code zur PropViz Kompilierung im build.xml . . . . . .
5.2. Ausgabe der Namen der Workspacetabs . . . . . . . . . . . . .
5.3. Beim Doppelklick ausgeführte Aktion . . . . . . . . . . . . . .
5.4. Realisierung der automatischen Auswahl der Klasse „Thing“ .
5.5. Implementierung der Methode getClassesPastRadius() . . . .
5.6. Behebend des Tooltip-Bugs . . . . . . . . . . . . . . . . . . .
5.7. Registrierung des OwlPropVizGraphView in der plugin.xml . .
5.8. EventManager . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.9. Konstruktoren des GraphChangeEvents . . . . . . . . . . . . .
5.10. Informieren der Listeners über ein Event . . . . . . . . . . . .
5.11. Methoden des Zugriffs auf die sichtbaren Kanten und Knoten .
5.12. Aufbau der Klasse GraphItemsStatistics . . . . . . . . . . . .
5.13. Speichern der vom Benutzer ausgewählten Kanten . . . . . . .
5.14. Implementierung von Show > Domain . . . . . . . . . . . . .
5.15. Hinzufügen einer Klasse und deren Visualisierung . . . . . . .
5.16. Löschen eines Individuums aus einer Ontologie . . . . . . . . .
5.17. Erstellen und Anzeigen einer Class Restriction . . . . . . . . .
5.18. Erstellen einer Class Assertion . . . . . . . . . . . . . . . . . .
5.19. Erstellen einer Property Assertion . . . . . . . . . . . . . . . .
30
32
32
32
33
36
37
37
38
38
38
39
40
41
42
43
44
45
45
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
ix
Listings
5.20. Aufruf des UIHelpers . . . . . . . . . . . . . . . . . . .
5.21. Zugriff auf die neue getChildrenCount Methode . . . .
5.22. Aktualisieren der visibilityMap . . . . . . . . . . . . .
5.23. Konsolenausgabe der Parser Exception . . . . . . . . .
5.24. Die für den Fehler zuständige Zeile der GraphViz Datei
5.25. Preparsen der GraphViz Datei . . . . . . . . . . . . . .
x
.
.
.
.
.
.
45
47
47
48
48
48
A.1. plugin.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
A.2. build.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
59
59
B.1. OWLPropVizGraphInfoViewComponent.java .
B.2. GraphItemsStatistics.java . . . . . . . . . . .
B.3. EdgeCounter.java . . . . . . . . . . . . . . . .
B.4. NodesCounter.java . . . . . . . . . . . . . . .
B.5. OWLPropVizView.java . . . . . . . . . . . . .
B.6. ShowAllIndividualsPropVizCommand . . . . .
B.7. ShowClassCommandWrapper.java . . . . . . .
B.8. ShowSubclassesCommandWrapper.java . . . .
B.9. ShowSuperclassesCommandWrapper.java . . .
B.10.AutoDisplayOptionsPage.java . . . . . . . . .
B.11.OptionsFileIO.java . . . . . . . . . . . . . . .
B.12.OWLPropVizOptions.java . . . . . . . . . . .
B.13.DisplayManager.java . . . . . . . . . . . . . .
B.14.HideState.java . . . . . . . . . . . . . . . . . .
B.15.HtmlHighlighter.java . . . . . . . . . . . . . .
B.16.PropVizPopupMenu.java . . . . . . . . . . . .
B.17.EventManager.java . . . . . . . . . . . . . . .
B.18.CreateEdgeEvent.java . . . . . . . . . . . . .
B.19.CreateEdgeEventType.java . . . . . . . . . . .
B.20.CreateEdgeListener.java . . . . . . . . . . . .
B.21.EdgeCreator.java . . . . . . . . . . . . . . . .
B.22.GraphChangeEvent.java . . . . . . . . . . . .
B.23.GraphChangeEventType.java . . . . . . . . .
B.24.GraphChangeListener.java . . . . . . . . . . .
61
62
64
66
67
71
72
72
73
73
74
75
75
78
79
79
84
85
85
85
86
87
88
88
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Verzeichnis der Abkürzungen
XML
Extensible Markup Language
HTML Hypertext Markup Language
OWL
Web Ontology Language
RDF(S) Resource Description Framework (Schema)
OSGi
Open Services Gateway initiative
SVN
Subversion
xi
1 Einleitung
Im diesem Kapitel der Bachelorarbeit wird zunächst auf die Gründe eingegangen,
die dazu geführt haben sich mit dieser Thematik auseinanderzusetzen. Anschließend
wird die Aufgabenstellung dargestellt.
1.1. Motivation
Ontologien sind Wissensmodelle, die Zusammenhänge zwischen verschiedenen Objekten darstellen. Sie werden mit Hilfe der Sprache OWL formalisiert, sodass aus
dem vorhanden Wissen ein implizites Wissen gefolgert werden kann. Diese Sprache
ist in der Regel nur von Maschinen lesbar. Ein Mensch benötigt aber ein Werkzeug
um mit den Ontologien zu arbeiten oder diese zu erstellen.
Auf der Universität Stanford wurde eine Software namens Protégé entwickelt, die
genau ein solches Werkzeug ist. Protégé ist eine Open-Source-Software und dient dazu
den Umgang mit Ontologien zu ermöglichen. Mit deren Hilfe kann eine Ontologie
erstellt und beliebig erweitert werden. Eine wichtige Funktionalität fehlte dennoch
im ursprünglichen Protégé, und zwar die graphische Anzeige der Ontologien.
Mit der Entwicklung des OWLViz-Plugins wurde dieses Problem gelöst, dennoch
nicht vollständig. OWLViz erweitert die Funktionalitäten von Protégé um graphische
Anzeige einer Ontologie. Diese wird in Form eines Graphen gezeichnet und beinhaltet
nur die Grundarten der Beziehungen zwischen den dargestellten Objekten. Daran
lassen sich Unterklassen, äquivalente und disjunkte Klassen erkennen, aber keine
benutzerdefinierten Beziehungen (Properties).
Mit der Anzeige von Properties beschäftigt sich das OWLPropViz-Plugin. OWLPropViz ist eine Weiterentwicklung von OWLViz, die in Form eines eigenständigen
Plugins realisiert ist. Die Anzeige des Graphen wurde um die Property-Visualisierung
erweitert. Im Rahmen der parallelen Bachelorarbeit [Sch09] wurden zusätzliche Funktionalitäten zur Anzeige von Individuen und den Beziehungen dazwischen hinzugefügt.
Insgesamt erfordert aber Protégé viel Zeit für die Einarbeitung. Auch erfahrene Benutzer, die mit OWLPropViz arbeiten, benötigen eine gewisse Zeit um Änderungen in
einer Ontologie durchführen zu können. Der gewünschte Effekt ist, die Standardfunktionalitäten zur Ontologiebearbeitung direkt innerhalb des OWLPropViz-Plugins zugänglich zu machen.
1
1. Einleitung
1.2. Aufgabenstellung
Die allgemeine Aufgabenstellung kann man als „Verbesserung der Benutzerinteraktion“ formulieren. Dieses beinhaltet zwei Aspekte: Zum einen, soll das Programm auf
eine einfachere Art und Weise die gewünschten Informationen anzeigen, als es in der
alten Version der Fall ist, zum anderen sollen oft benutzte Funktionen zugänglicher,
bzw. intuitiver gemacht werden. Die Kombination dieser beiden Aspekte soll dann
insgesamt einen reibungslosen Ablauf in der Kommunikation zwischen dem Benutzer
und dem Programm ermöglichen. Die einzelnen Aufgaben entstanden während der
Nutzung des OWLPropViz Plugins im Zusammenhang mit der allgemeinen Nutzung
von Protégé. Um diese zu veranschaulichen, wird beispielhaft ein Szenario betrachtet,
bei welchem ein Anwender das Programm bedient. Die beim Durchlauf des Szenarios
entstehenden Hindernisse ergeben die konkreten Aufgaben dieser Bachelorarbeit. Es
wird vorausgesetzt, dass das OWLPropViz Plugin benutz wird und der Nutzer die
zu bearbeitende Ontologie noch nicht kennt, aber dennoch im Laufe seiner Arbeit
bestimmte Änderungen machen will.
Schritt 1: Mit Hilfe von Protégé wird die entsprechende Ontologie aus einer Datei geöffnet. Um sich ein Bild von der Ontologie verschaffen zu können, geht der
Benutzer auf den WorkspaceTab „OWLPropViz“. Das Plugin wird geladen, auf der
Arbeitsfläche (Abb. 1.1) erscheinen ein nicht expandierter Hierarchiebaum und ein
leeres Feld, wo später ein Graph der Ontologie erstellt wird. Bereits an dieser Stelle
Abbildung 1.1.: Benutzeroberfläche nach dem Laden von OWLPropViz
entstehen die ersten Vorschläge zur Verbesserung der Interaktion. Gleich beim Öffnen
des Plugins soll der Nutzer eine Möglichkeit bekommen, sich in Grundzügen mit der
Ontologie vertraut zu machen: eine Vorschau in Form eines automatisch generierten
Graphen soll die Inhalte der Ontologie veranschaulichen, während ein zusätzliches
Panel die Informationen über die Anzahl der vorhandenen und visualisierten Ob-
2
1.2. Aufgabenstellung
jekten samt deren Beziehungen untereinander darstellt. Diese Informationen sollen
dynamisch generiert werden und somit auf alle Änderungen bei der Visualisierung
oder in der Struktur der Ontologie reagieren.
Schritt 2: Der Benutzer will einen Graphen mit Vorgabe seiner eigenen Einstellungen generieren. Dazu muss der entsprechende Knopf in der Toolbar geklickt werden,
welcher derzeit aufgrund der internen Protégé-Einstellungen inaktiv ist. Um diesen
zu aktivieren, muss nun ein beliebiges Element in dem Hierarchiebaum ausgewählt
werden. Dieser Prozess kann verbessert werden. Durch das automatische Auswählen
eines beliebigen Elementes soll der Knopf gleich aktiviert und der Arbeitsfluss nicht
immer an der gleichen Stelle gestört werden.
Schritt 3: Der Graph ist generiert. Der Benutzer möchte die angezeigten Elemente
ein- und ausblenden, was im Falle einer etwas größeren Ontologie durchaus sinnvoll
ist. Das aktuell vorhandene Kontextmenü, welches aus OWLViz stammt, ist aber
nicht vollständig funktionsfähig. Dieses resultiert aus der Erweiterung des Anzeigemodells um die Property- [Wac08] und Individuen-Visualisierung [Sch09]. Die Funktionalitäten des alten Kontextmenüs sind aber wichtig und müssen wiederhergestellt
werden, indem sie auf das neue Modell angepasst werden. Der Benutzer soll in der
Lage sein den ausgewählten Knoten, sowie seine Kinder- und Elternknoten, ein- oder
auszublenden.
Schritt 4: In dem angezeigten Graphen werden farblich die sog. äquivalenten Klassen gekennzeichnet (Abb. 1.2). Eine äquivalente Klasse beinhaltet eine Beschreibung
dessen, welche Eigenschaften eine weitere Klasse besitzen muss, um zu der ausgewählten Klasse äquivalent zu sein. Beispielsweise kann alles, was vier Wände und ein
Dach hat, als ein Äquivalent zu einem Haus bezeichnet werden. Diese Beschreibung
befindet sich in dem Klassen-Editor von Protégé, wofür der Nutzer zu einem anderen
Workspace umschalten muss. Um den Arbeitsfluss zu verbessern, soll eine Möglich-
Abbildung 1.2.: Mit OWLPropViz visualisierte Ontologie uni.owl [Vor09]
3
1. Einleitung
keit zur Anzeige von äquivalenten Klassen realisiert werden. Des Weiteren soll ein
einfacher Doppelklick auf dem ausgewählten Objekt zu dem passenden Workspace
führen, wo die weiteren Eigenschaften angezeigt oder geändert werden können.
Schritt 5: Der passende Abschnitt des Graphen ist angezeigt und der Benutzer hat
sich ein Bild von der Ontologie gemacht: neue Elemente sollen hinzugefügt und die
veralteten gelöscht werden. Das kann aber derzeit nicht visuell innerhalb des Graphen
gemacht werden, da OWLPropViz lediglich eine Visualisierungsmöglichkeit zur Verfügung stellt. Die genannten Aktionen müssen zunächst in dem Hierarchiebaum links
neben dem Graphen und somit in der aktuellen Ontologie gemacht werden. Danach
muss der Graph erneut manuell erstellt werden, um diese Änderungen sichtbar zu machen. Dabei gehen die aktuellen Anzeigeeinstellungen verloren, die im letzen Schritt
mit Hilfe von Hide und Show gemacht worden sind und auf dem Bildschirm erscheint
wieder ein komplett neuer Graph. Um den Arbeitsfluss an dieser Stelle zu verbessern, soll ein Kontextmenü für einen Knoten entworfen werden. Das Kontextmenü
des ausgewählten Knoten soll es dem Benutzer erlauben, das ausgewählte Element
zu löschen oder ein Kinder- bzw. Geschwisterknoten hinzuzufügen. Die Änderungen
sollen sofort in dem aktuellen Graphabschnitt visualisiert und in der Ontologie eingetragen werden.
Schritt 6: Die Knoten stehen fest und die Beziehungen (Kanten) zwischen den angezeigten Objekten sollen erstellt werden. Dies erfordert das Umschalten in andere
Teile von Protégé und nimmt einige Zeit in Anspruch. Danach müssen die gemachten
Änderungen wie im Schritt zuvor erneut visualisiert werden. Das Hinzufügen von
Kanten soll intuitiver gemacht werden und direkt in dem Plugin-Bereich geschehen.
Der Benutzer soll die Möglichkeit bekommen in dem Graphen die Kanten zwischen
den ausgewählten Knoten hinzuzufügen. Die gemachten Änderungen sollen sofort
angezeigt werden, während die entsprechende Beziehung zwischen den Objekten in
der Ontologie registriert werden sollen.
Zusammenfassend ergibt sich daraus folgende Liste mit konkreten Aufgaben.
• Die Toolbarbuttons sollen beim Laden sofort aktiviert werden.
• Ein Graph mit einer Onotologievorschau soll ebenfalls gleich nach dem Laden
erstellt werden.
• Ein Doppelklick auf dem ausgewählten Knoten, soll zum passenden WorkspaceTab führen.
• Eine Möglichkeit zur Visualisierung von äquivalenten Klassen soll entworfen
und umgesetzt werden.
• Es soll eine Übersicht aller angezeigten und versteckten Elementen geben.
4
1.2. Aufgabenstellung
• Die alten Hide- und Show-Funktionen sollen wiederhergestellt und auf das neue
Modell angepasst werden.
• Es soll eine Möglichkeit geschaffen werden, direkt in dem Graphen neue Knoten
hinzuzufügen bzw. alte zu löschen.
• Erstellung von Kanten (Properties) soll ebenfalls im Graphen möglich sein.
Sobald alle diese Aufgaben erfüllt sind, soll zusätzlich versucht werden, eine weitere
Funktionalität wiederherzustellen. Dabei handelt es sich um die Anzeige kleiner Pfeile an einem Knoten, sofern dieser versteckte Eltern- oder Kinderknoten hat. Diese
Funktion ist zurzeit in ihrer alten nicht angepassten Form aktiv und liefert keine
richtigen Ergebnisse.
Abbildung 1.3.: Die kleinen Pfeile
5
2 Grundlagen
Vor der Weiterentwicklung von Protégé, muss zuerst verdeutlicht werden, aus welchen Grundelementen das Programm zusammengesetzt ist und mit welchen Daten
gearbeitet wird. Zu diesem Zweck wird in diesem Kapitel zunächst die Problematik
des WWW beschrieben, welche zu der Idee des Semantic Web und dem Begriff Ontologie führen. Weiter wird über die Sprachen XML, RDF und RDFS die Sprache
OWL eingeführt, in welcher die Ontologien gespeichert werden können und mit welcher Protégé arbeitet. Es wird grob auf die Funktionalitäten von Protégé eingegangen
und die dahinter stehenden Architekturen OSGi und Equinox näher erläutert.
2.1. Semantic Web
Das World Wide Web beinhaltet sehr viele Informationen, welche einen klaren Endnutzer haben - den Menschen selbst. Ein Mensch, im Gegensatz zu Maschinen, kann
die Informationen aus dem WWW lesen, auswerten und interpretieren. Das auf diese
Art gewonnene Wissen kann von einem Menschen in anderen Formen dargestellt und
zu jeder anderen Informationsmenge in Bezug gesetzt werden.
Ein großes Problem dabei ist relevante Informationen zu finden. So können uns
die bereits im Web existierenden Suchmaschinen bei solchen Informationssuchen gut
unterstützen, jedoch uns nicht diese Arbeit abnehmen. Es ist auch zu beachten,
dass die sich im Web befindenden Informationen in einer Großzahl verschiedener
Dateiformate, Zeichensätze, Sprachen usw. gespeichert sind. Das macht es schwierig
oder gar unmöglich, die Informationen aus dem Web einem Gebiet zuzuordnen, sie
darzustellen und weiterzuverwenden. Des Weiteren kann es vorkommen, dass die von
einem Nutzer gesuchte Information nicht in explizierter Form im Web zu finden ist,
sondern aus mehreren Quellen schlussgefolgert werden muss, die sog. Problematik
des impliziten Wissens.
Der Ansatz des Semantic Web [PHll] soll zur Lösung dieses Problems beitragen. Die
Schlüsselidee dabei ist, von vornherein die Information in einer Form darzustellen, die
auch von Maschinen verarbeitet werden kann. Das wird in der ersten Linie durch die
von W3C definierten Standards XML, RDF(S) und OWL erreicht. Die Methoden,
die die in solchen Formaten gespeicherten Informationen auswerten, sind ebenfalls
im Semantic Web definiert, werden aber im Rahmen dieser Bachelorarbeit nicht
behandelt.
6
2.2. Ontologien
2.2. Ontologien
Als Ontologie wird in der Informatik eine formale Repräsentation einer Konzeptmenge innerhalb einer Domäne samt aller Beziehungen zwischen diesen Konzepten verstanden. Zusätzlich beinhalten Ontologien Regeln (z.B. Integritätsregeln) aus welchen
Sachverhalten schlussgefolgert werden können. Eine Ontologie liefert ein gemeinsames Vokabular, mit dem eine Domäne modelliert werden kann: Typen von Objekten
oder Konzepten, deren Eigenschaften und Beziehungen. [Con04a]
Einfacher ausgedrückt, ist der Begriff der Ontologie ein Äquivalent zu dem Begriff
Wissensbasis. Das ist ein z.B. in OWL geschriebenes Dokument, welches Wissen einer
Domäne modelliert [PHll].
Für diese Bachelorarbeit relevante Ontologien werden im OWL Format gespeichert,
welches eine Erweiterung von RDFS ist und auf der Sprache XML basiert. Um die
eigentliche Sprache OWL verständlich zu machen, werden daher im Folgenden die
Sprachen XML, RDF und RDFS beschrieben.
2.2.1. XML
XML ist eine sehr weit verbreitete META-Sprache, welche eine gute Maschinenlesbarkeit bietet, aber für die Zwecke der Wissensmodellierung nicht ganz ausreicht.
Im Grunde genommen ist XML aus der Sicht des Semantic Web nicht besser als die
natürliche Sprache, was anhand des folgenden Beispiels demonstriert werden kann:
1
2
< knoten > Thing </ knoten >
< asdf > Thing </ asdf >
Listing 2.1: Beispiel einer XML Struktur
Für Menschen hat die erste Zeile eine Bedeutung, die zweite wiederum nicht, während für die Maschine die beiden Zeilen ganz einfach die gleiche Struktur haben. Das
weitere Problem wird bei den Tags <heft> und <papier> sichtbar. Der Mensch weiß,
dass Papier zur Herstellung von Heften benötigt wird, kann dieses Wissen jedoch
nicht aus den Tags erschließen. So stellt man fest, dass die Maschinen anhand von
XML Strukturen die Informationen nicht weiter verarbeiten und erst gar nicht daraus ein implizites Wissen herleiten können. Daher müssen neue Sprachen definiert
werden, die zwar XML als Grundlage haben, aber noch zusätzliche Möglichkeiten
beinhalten.
2.2.2. RDF
RDF steht für Resource Description Framework und ist eine formale Sprache zur
Beschreibung strukturierter Informationen. Bei RDF geht es nicht nur um die reine
Darstellung, wie es z.B. bei XML der Fall ist, sondern auch um die Kombination
und Weiterverarbeitung der dargestellten Informationen. Das Datenmodell von RDF
basiert auf Ausdrücken des Typen: Subjekt-Prädikat-Objekt und wird in RDF als
Tripel bezeichnet. Ein Beispiel solches Tripels in der sogenannten Turtle-Syntax ist
im folgenden Listing zu sehen.
7
2. Grundlagen
1
< uni:Student > < u n i : i s t I m m a t r i k u l i e r t A n > < un i :U ni ve rs i tä t >
Listing 2.2: Beispiel eines Tripels in Turtle RDF
Die Namensgebung in RDF geschieht durch Verwendung von URI (Uniform Resource Identifier), bei welchen es sich um eine einfache und erweiterbare Zeichenfolge handelt, die zur eindeutigen Bezeichnung einer Ressource dienen. Durch deren
Verwendung bekommt man die Möglichkeit abstrakte Ressourcen zu beschreiben.
Im Listing 2.2 tritt ein Präfix auf: uni. Der Präfix uni könnte z.B. <http://www.bachelorarbeit.de/ontologie/uni/> lauten und bildet zusammen mit dem nach dem
Doppelpunkt definierten Namen einen URI für das Objekt. In diesem Fall:
1
@prefix uni: < http: // example . org / ontologie / uni / >
3
< uni:Student >
< -- ist das selbe wie ! -- >
< http: // example . org / ontologie / uni / Student >
4
5
Listing 2.3: Prefix
Die Turtle Syntax ist relativ gut für Menschen lesbar und kann von Maschinen
eindeutig interpretiert werden, wird aber in der Praxis nicht so oft benutzt. Dieses
liegt daran, dass zum Auslesen von Turtle zusätzliche Bibliotheken benutzt werden,
die nicht weit verbreitet sind. Daher wird diese RDF Syntax in eine XML Schreibweise
umgewandelt und ist somit leicht mit einem XML-Parser lesbar. Das im Listing 2.2
aufgeführte Beispiel würde dann wie folgt aussehen:
1
2
3
4
6
7
8
10
<? xml version = " 1.0 " encoding = " utf -8 " ? >
<! ENTITY ex ’ http: // example . org / ontologie / uni / ’ >
< rdf:RDF xmlns:rdf = " http: // www . w3 . org /1999/02/22 - rdf - syntax - ns # "
xml:base = " http: // www . example . org / ontologie / uni / " >
< r df :D es cr i pt io n rdf:about = " Student " >
< e x : i s t I m m a t r i k u l i e r t A n rdf:resource = " Universität " / >
</ r df :D e sc ri pt i on >
</ rdf:RDF >
Listing 2.4: Ein Tripel im XML Syntax
2.2.3. RDFS
Die von Menschen in RDF definierten URI-Bezeichner sind aus Maschinensicht bedeutungslos, denn es handelt sich dabei um Zeichenketten, die der Benutzer vorgibt.
Daher muss die Semantik von RDF erweitert werden. Diese Aufgabe übernimmt
RDFS. RDF Schema [Con04b] ist eine Beschreibungssprache des RDF-Vokabulars.
Mit Hilfe dieses Vokabulars ist es möglich schematisches Wissen zu beschreiben,
es stellt ein Mechanismus zur Beschreibung von verwandten Ressourcen und den
dazwischen liegenden Relationen. Ressourcen werden dafür benutzt Charakteristika
weiterer Ressourcen zu beschreiben, wie z.B. Typen, Unterklassen und Properties.
Das Class- und Property-System in RDFS ähnelt einem System, das in objektorientierten Programmiersprachen wie z.B. Java benutzt wird. Alle Ressourcen werden
8
2.2. Ontologien
in Gruppen unterteilt, welche als Klasse bezeichnet werden. Klasse selbst ist wiederum auch eine Ressource. Ein Mitglied einer Klasse wird als Instanz bezeichnet. Eine
Unterklasse ist wie folgt definiert: wenn Klasse K1 eine Unterklasse der Klasse K ist,
so sind alle Instanzen von K1 gleichzeitig die Instanzen von K. Als Property wird eine
Relation zwischen Subjekt-Ressourcen und Objekt-Ressourcen verstanden.
2.2.4. OWL
OWL steht für Web Ontology Language und erweitert die beschränkten Möglichkeiten von RDFS. OWL beinhaltet noch mehr Vokabular um Properties und Klassen zu
beschreiben [Con04a], unter anderem: Relationen zwischen Klassen (z.B. disjunkte
Mengen), Kardinalitäten (z.B. „exactly one“), Gleichheit, Charakteristika von Properties (z.B. Symmetrie) und Aufzählungen. Diese Erweiterungen ermöglichen den
Maschinen Daten zu interpretieren und daraus zu folgern.
Im Rahmen dieser Bachelorarbeit spielen äquivalente Klassen und Property Restrictions aufgrund deren Visualisierung in dem OWLPropViz-Plugin eine besondere Rolle,
werden daher aus der Gesamtheit der semantischen Erweiterungen von OWL hervorgehoben und im Folgenden genauer erklärt.
Der Begriff einer äquivalenten Klassen bedeutet, dass eine bestimmte Klasse in
einer Ontologie äquivalent zu einer Klasse in einer anderen Ontologie ist. Die Äquivalenz kann auf zwei Arten definiert sein, wie das folgende Listing zeigt.
3
< owl:Class rdf:ID = " Professor " >
< o w l : e q u i v a l e n t C l a s s rdf:resource = " & uni ; Student " / >
</ owl:Class >
5
< -- oder ! -- >
7
< owl:Class rdf:ID = " Student " >
< owl:equivalentClass >
< o wl :R es t ri ct io n >
< owl:onPr operty rdf:resource = " # i m m a t r i k u li e r t A n " / >
< o w l : s o m e V a l u e s F r o m rdf:resource = " # Universität " / >
</ o wl :R e st ri ct i on >
</ o w l : e q u i v a l e n t C l a s s >
</ owl:Class >
1
2
8
9
10
11
12
13
14
Listing 2.5: Äquivalente Klassen
In dem ersten Fall wird explizit angegeben, dass eine neue Klasse „Student“ zu
der Klasse „Student“ in der „uni“-Ontologie äquivalent ist. In dem zweiten Fall wird
die Aussage getroffen, dass alle Klassen, die an einer Universität immatrikuliert sind,
Studenten sind.
Eine Beziehung zwischen zwei Klassen könnte eingeschränkt sein. Das führt zu
dem Begriff einer Property Restriction. Die Einschränkung kann in OWL auf vier
Arten erfolgen, von welchen nur someValuesFrom als Kante in OWLPropViz-Plugin
dargestellt wird.
1
2
3
< owl:Class rdf:ID = " Universität " >
< r df s: su b Cl as sO f rdf:resource = " & uni ; Thing " / >
< r df s: su b Cl as sO f >
9
2. Grundlagen
4
5
6
7
8
9
10
< o wl :R es tr i ct io n >
< o wl:onPro perty rdf:resource = " # studiertAuf " / >
< o w l : s o m e V a l u e s F r o m rdf:resource = " # Student " / >
</ o wl :R e st ri ct i on >
</ r df s: s ub Cl as s Of >
...
</ owl:Class >
Listing 2.6: Property Restriction
Die Beziehung someValuesOf impliziert in diesem Beispiel folgende Aussage: „Für
alle Universitäten gibt es wenigsten eine Klasse, die auf dieser Universität studiert
und ein Student ist“.
2.3. Protégé
Protégé ist eine kostenlose, am Institut für Medizinische Informatik der Stanford University (Kalifornien, USA) entwickelte Open-Source-Plattform, die eine Sammlung
von Tools beinhaltet, mit welchen man Domänen modellieren und wissensbasierte
Applikationen mit Ontologien erstellen kann. Der Protégé-Kern implementiert eine
große Menge von wissensmodellierenden Strukturen und Aktionen, die Erstellung, Visualisierung und Manipulation von Ontologien ermöglichen. Die Protégé-Plattform
unterstützt zwei Arten von Ontologiemodellierung: Protégé-Frames Editor, welcher
zur Bearbeitung von Frame-basierten Ontologien gedacht ist und der für uns relevante Protégé-OWL Editor [Sta09]. Dieser ist eine Erweiterung von Protégé, die es
ermöglicht, Ontologien für das semantische Web in der Ontologie-Sprache OWL zu
erstellen. Die weiteren Funktionen des Protégé-OWL Editor für den Nutzer sind:
• Laden und Speichern von OWL- und RDF-Ontologien
• Bearbeitung und Visualisierung von Klassen und Eigenschaften
• Definieren von logischen Klassencharakteristika als OWL-Ausdrücke
• Ausführen von Reasoners
• Bearbeiten von OWL-Individuen
2.4. Eclipse Equinox OSGi Framework in Protégé
Das Eclipse Equinox OSGi Framework wird in Protégé benutzt um Plugin-Unterstützung zu ermöglichen. Daher wird in diesem Abschnitt das OSGi Framework sowie
Equinox nur aus Sicht des Plugins betrachtet.
OSGi steht für Open Service Gateway Initiative, welche im Jahr 1999 gegründet wurde. OSGi spezialisiert sich darauf in Java programmierte Dienste zu standardisieren, um deren Wiederverwendung zu ermöglichen. Das Kennzeichen für das
OSGi-Framework ist das Hot Plugging: das Hinzufügen und Entfernen von Modulen
10
2.4. Eclipse Equinox OSGi Framework in Protégé
(Bundles) und Diensten (Services) zum laufenden Betrieb [Daull]. Dieses kann dazu
benutz werden um über das Internet oder das lokale Neztwerk einzelne Module zu
aktualisieren oder zu erweitern.
OSGi Anwendungen werden aus Bundles zusammengesetzt, welche auch interne Abhängigkeiten aufweisen können. Jedes Bundle besteht mindestens aus einem
Bundle-Manifest, optional einer Activator-Klasse und weiteren Java Klassen. Die
Manifestdatei META-INF/MANIFEST.MF beinhaltet Metainformationen, wie den
eindeutigen Name oder die Versionsnummer [Thi09]. Die Manifest-Datei für das
OWLPropViz-Plugin ist im folgenden Listing zu sehen.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Manifest - Version: 1.0
Ant - Version: Apache Ant 1.7.0
Created - By: 11.3 - b02 ( Sun Microsystems Inc .)
Bundle - M a n i f es t V e r s i o n : 2
Bundle - Name: OWLPropViz Plug - in
Bundle - Category: protege
Bundle - Vendor: TU Braunschweig
Bundle - DocURL: http: // www . ifis . cs . tu - bs . de
Import - Package: org . osgi . framework , org . apache . log4j , javax . swing
...
Export - Package: de . tubs . cs . ifis . owlpropviz , de . tubs . cs . ifis . owlpropviz .
extractor , ...
Bundle - Version: 1.0.0
Bundle - Description: A plugin for visualizing owl ontologies .
Bundle - SymbolicName: de . tubs . cs . ifis . owlpropviz ; singleton: = true
Require - Bundle: org . protege . common , org . protege . editor . core . application
, org . protege . editor . owl , org . semanticweb . owl . owlapi , org . coode . owlviz
Bundle - ClassPath: .
Built - By: skovalev
Build - Date: 08/19/2009 03 :33 PM
Listing 2.7: Manifest.mf des OWLPropViz-Plugins
Man beachte, dass OSGi nur eine Spezifikation ist. Es gibt viele Implementierungen
dafür und Equinox, entwickelt von Eclipse Foundation (http://www.eclipse.org/),
ist eine davon. Um ein Plugin in Equinox einzubinden, ist außer der Manifest-Datei
noch eine weitere Datei notwendig: plugin.xml. Angepasst für Protégé beschreibt diese
Datei einen eindeutigen Bezeichner des Plugins und die weiteren Optionen, die im
folgenden Listing zu sehen sind.
1
2
3
4
5
6
7
8
9
10
11
12
<? xml version = " 1.0 " ? >
< plugin >
...
< extension point = " org . protege . editor . core . application . ViewComponent "
id = " OWL PropVizV iew " >
< label value = " OWLPrope rtyViz " / >
< class value = " de . tubs . cs . ifis . owlpropviz . OWL PropVizV iew " / >
< headerColor value = " @org . protege . classcolor " / >
< category value = " @org . protege . classcategory " / >
</ extension >
...
</ plugin >
Listing 2.8: Ausschnitt aus plugin.xml
11
2. Grundlagen
2.5. Entwurfsmuster
Die Softwarearchitektur von Protégé arbeitet mit objektorientierten Entwurfsmustern. Im Folgenden werden die drei für das PropViz Plugin relevanten Entwurfsmuster beschrieben. Die einleitenden Zitate stammen aus [Gamll].
2.5.1. Visitor
Kapsle eine auf den Elementen einer Objektstruktur auszuführende Operation als ein Objekt. Das Besuchermuster ermöglicht es Ihnen, eine neue
Operation zu definieren, ohne die Klassen der von ihre bearbeiteten Elemente zu Verändern.
Angenommen es soll ein Besucher entworfen werden, die Elemente in einer Ontologie
löscht und wir als EntityRemover bezeichnet. Mit Hilfe dieses Removers sollen beispielsweise OWL Klassen und Individuen gelöscht werden. Dafür müssen sowohl eine
Klasse als auch ein Individuum zunächst eine Methode besitzen, welche den Besucher
akzeptiert.
1
2
3
4
5
6
7
public class OWLClass { // oder Individuum
...
public void accept ( EntityRemover remover ) {
remover . visit ( this )
}
...
}
Listing 2.9: Schnittstelle der zu besuchenden Klasse
In dieser Methode übergibt das zu besuchende Objekt seine Instanz an den EntityRemover, welcher seinerseits die Operationen zum löschen Beinhaltet.
1
2
3
4
5
6
7
8
9
10
public class EntityRemover {
...
public void visit ( OWLClass cls ) {
// Operationen zum Löschen einer Klasse hier
}
public void visit ( OWLIndividual indi ) {
// Operationen zum Löschen eines Individuums hier
}
...
}
Listing 2.10: Eine Visitor-Klasse
Die Operationen zum Löschen eines Objektes können nun passend definiert und beliebig erweitert werden. Die Klasse EntityRemover ist die zentrale Klasse, in der alle
Löschprozesse kontrolliert werden. Neue Implementierungen oder zusätzliche Operationen können hier definiert werden, ohne dass die Klassen OWLClass und OWLIndividual angepasst oder verändert werden müssen. Um ein Löschprozess durchzuführen
reicht dann z.B. folgender Aufruf: klasse.accept(new EntityRemover()).
12
2.5. Entwurfsmuster
2.5.2. Command
Kapsle einen Befehl als ein Objekt. Dies ermöglicht es, Klienten mit verschiedenen Anfragen zu parametrieren, Operationen in eine Warteschlange zu stellen, ein Logbuch zu führen und Operationen rückgängig zu machen.
In Protégé sind alle Befehle nach dem Command-Entwurfsmuster realisiert. Dieses
ermöglicht eine einfache Einbindung bereits existierender Befehle in weitere Teile des
Programms. Wenn neue Funktionalitäten mit Hilfe dieses Musters entworfen werden,
so können diese ebenfalls später an anderen Stellen wiederverwenden werden. Als
Beispiel wird ShowClassCommand des OWLPropViz-Plugins betrachet.
1
public class S h o w C l a s s C om m a n d extends O W L S e l e c t i o n V i e w A c t i o n {
public S h o w C l a s s C om m a n d ( OWLProp VizView view ) {
...
}
...
public void a ct io nP er f or me d ( ActionEvent e ) {
...
}
3
4
5
6
7
8
9
10
}
Listing 2.11: Entwurfsmuster Command
In dem Konstruktor der Command-Klasse (Zeile 4) wird diejenige Klasse übergeben, die alle Mittel dazu hat, den ShowClass-Befehl durchzuführen. Über OWLPropVizView kann die ausgewählte Klasse ermittelt und auf den Graphen zugegriffen werden. In der Methode actionPerformed() wird die durchzuführende Aktion
spezifiziert: zeige die ausgewählte Klasse in dem aktuellen Graphen mit Hilfe von
OWLPropVizView an.
Steht die Command-Klasse fest, so kann sie als ActionListener des entprechenden Elementes gesetzt werden, z.B. in einem Toolbarbutton oder in einem Kontextmenüeintrag.
1
toolbarbutton . a d d A c t i o n L i s t e n e r ( new S h o w C l a s s C om m a n d ( view ));
Listing 2.12: Einbinden einer Command-Klasse
Damit ist die Command-Klasse eingebunden und kann benutz werden.
2.5.3. Singleton
Sichere ab, dass eine Klasse genau ein Exemplar besitzt, und stelle einen
globalen Zugriffpunkt darauf Bereit.
Dieses Entwurfsmuster eignet sich sehr gut um eine Kommunikationsschnittstelle
zwischen zwei Programmteilen zu verschaffen, welche sonst keinen Zugriff auf gegenseitige Ressourcen hätten. Bei der Implementierung muss beachtet werden, dass der
Kontruktor der Klasse privat ist, d.h. nur von der Klasse selbst aufgerufen werden
13
2. Grundlagen
kann. Eine Variable instance wird definiert, welche die einzige Instanz der Klasse
beinhalten wird. Zum Schluß wird eine öffentliche und statische Methode getInstance() definiert, die einen Zugriff auf diese Instanz erlaubt oder ggfs. eine Instanz der
Klasse erstellt.
1
2
public class Singleton {
private Signleton instance ;
4
private Singleton () {}
6
public static getInstance () {
if ( instance == null ) {
instance = new Singleton ();
}
return instance ;
}
7
8
9
10
11
public void do () {}
13
14
}
Listing 2.13: Eine Singleton-Klasse
Wurde die Klasse Singleton nach dem beschriebenen Muster entworfen, so kann
nun jede Klasse des Programms mit dem Befehl Singleton.getInstance().do()
auf die Methode do() zugreifen.
14
3 Plugin-Entwicklung mit Eclipse
Um Protégé reibungslos weiterentwickeln zu können, muss zunächst eine Entwicklungsumgebung ausgewählt und optimal eingestellt werden. Viele Softwareentwickler
bevorzugen Eclipse als Entwicklungsumgebung, was in diesem Fall eine sehr gute
Wahl ist. Sowohl Eclipse als auch Protégé basieren auf dem OSGI-Framework, was
eine optimale Zusammenarbeit der beiden Plattformen ermöglicht. In diesem Kapitel
wird erklärt, wie Eclipse Schritt für Schritt eingerichtet wird.
3.1. Eclipse-Installation
Auf der offiziellen Seite der Eclipse Foundation kann die aktuelle Version von Eclipse
heruntergeladen werden. Unter http://www.eclipse.org/downloads/ stehen mehrere
Pakete von Eclipse zum Download zur Verfügung. Die Wahl fällt auf „Eclipse for
RCP/Plug-in Developers“. Das Archiv mit Eclipse wird heruntergeladen und kann
danach in das gewünschte Verzeichnis extrahiert werden. Es ist anzumerken, dass
diese Bachelorarbeit mit Eclipse 3.4 Ganymede gemacht worden ist. Das Release
3.5 Galileo wurde während der Entwicklungsarbeiten am OWLPropViz-Projekt veröffentlicht. Die Funktionalitäten wurden aber auch unter Galileo überprüft - beim
Entwickeln des Property Visualisation Plug-Ins haben sich die Änderungen innerhalb
des Eclipse-Updates nicht sichtbar gemacht.
3.2. Subclipse-Installation
Nachdem Eclipse in das gewünschte Arbeitsverzeichnis extrahiert ist, muss die Versionsverwaltung SVN installiert werden. Am besten eignet sich für diese Zwecke das
Eclipse-Plugin Subclipse, da es eine Versionsverwaltung direkt unter Eclipse erlaubt.
Die Installation kann in wenigen Schritten [Tig09] direkt im Eclipse ausgeführt werden.
• Man startet Eclipse und wählt im Menu: „Help > Software Updates > Find
and Install...“
• Im erscheinenden Fenster wird „Search for new features to install“ ausgewählt
und auf Next geklickt
• In diesem Schritt muss man „New Remote Site...“ auswählen. Im erscheinenden
Dialog ist das Feld Name optional und kann z.B. mit „Subclipse Site“ ausge-
15
3. Plugin-Entwicklung mit Eclipse
füllt werden. Als URL muss man „http://subclipse.tigris.org/update_1.6.x“
eintragen.
• Jetzt soll nach Updates in der neu hinzugefügten Seite „Subclipse Site“ gesucht
werden. Ausgewählt wird dabei „Subclipse“. Nach mehrmaligem Bestätigen beginnt Eclipse das Plugin Subclipse herunterzuladen.
• Nach dem erfolgreichen Download kann Subclipse nun installiert werden. Am
Ende wird noch zusätzlich ein Neustart von Eclipse erforderlich, um danach
das Plugin in Eclipse automatisch zu integrieren.
3.3. Protégé 4 und OWLPropViz aus dem Repository
beziehen
3.3.1. Protégé 4 aus der Repository beziehen
In Eclipse wird ein neuer Workspace erstellt und jetzt soll die aktuelle Version von
Protégé und von OWLPropViz aus dem SVN-Repository heruntergeladen werden.
Der erste Schritt dabei ist ein neues Projekt für Protégé mit Checkout Projects from
SVN im Eclipse Menu File > New > Other... anzulegen. Damit wird ein Wizard
gestartet, der dabei helfen wird ein neues Projekt zu erstellen.
Zunächst wird eine neue Repository Location mit der URL „http://smi-protege.stanford.edu/repos/protege“ erstellt. Nach einer kurzen Ladezeit wird die gesamte
Repository des Protégé-Projektes aufgelistet. Die relevanten Quelltexte befinden sich
im Ordner protege4/protege-standalone/trunk. Im nächsten Schritt des Wizards, nach
der Auswahl des Repository-Ordners, sollen keine Änderungen gemacht werden und
werden mit Finish bestätigt. Damit wird Eclipse mitgeteilt, dass die Inhalte der
letzten (HEAD) Revision komplett rekursiv in das Projekt heruntergeladen werden
sollen und dass für die weitere Einrichtung des Projektes ein neuer Wizard gestartet
werden soll.
Als neuen Wizard wird Java Project ausgewählt. Das Projekt kann beliebig benannt werden, z.B. „Protege4“. Im nächsten Schritt soll der Default output folder
auf „Protege4/build/classes“ gesetzt werden. Damit entspricht er dem StandardOutput-Ordner des Protégé Ant-Buildes. Das Dialog wird mit Finish beendet. Im
erschienenen Fenster wird bestätigt, dass die für dieses Projekt von Eclipse erstellten
Dateien durch die aus dem SVN-Repository bezogenen Dateien ersetzt werden sollen.
Ist dieses getan, beginnt Eclipse alle Inhalte aus dem SVN herunterzuladen.
3.3.2. OWLPropViz aus dem Repository beziehen
Ähnlich wie im vorherigen Abschnitt wird auch OWLPropViz runtergeladen.
Ein neues Checkout Projects from SVN Projekt wird erstellt. Als SVN-Repository
soll das bereits existierende „http://smi-protege.stanford.edu/repos/protege“ genom-
16
3.4. Protégé kompilieren und Run Configuration für das Plugin erstellen
men werden. Der Zielordner ist jetzt aber protege4/de.tubs.cs.ifis.owpropviz/trunk.
Wieder wird mit Next und Finish die Arbeit dieses Wizards beendet.
Als nächstes soll erneut der Java Project Wizard ausgewählt werden. Der Name des
Projekts ist wieder frei wählbar, z.B. „OWLPropViz“. Der Output-Ordner soll auch
dieses Mal dem der Ant-Datei entsprechen und lautet „OWLPropViz/build/classes“.
Das Fenster mit der Frage, ob die .project-Datei ersetzt werden soll, wird mit OK
beendet.
Damit sind die beiden Projekte erfolgreich aus dem Repository heruntergeladen
und die Einrichtung der Run Configuration kann beginnen.
3.4. Protégé kompilieren und Run Configuration für
das Plugin erstellen
Es gibt zwei Ansätze mit denen man ein eigenes Plugin für Protégé unter Eclipse
entwickeln kann. Der erste Ansatz basiert darauf, die Plugin-Entwicklung in zwei
verschiedenen Projekten eines Workspaces in Eclipse zu tätigen. Der zweite integriert die beiden Projekte zu einem, was gewisse Vorteile während der Entwicklung
verschafft. Im Folgenden werden die für die Einrichtung nötigen Schritte detailliert
beschrieben. Beide Ansätze brauchen als Grundlage die beiden Eclipse Projekte, die
in dem vorherigen Unterkapitel erstellt worden sind.
3.4.1. Das Plugin OWLPropViz als getrenntes Eclipse-Projekt
Dieser Ansatz wurde bei der Erstentwicklung von OWLPropViz verfolgt. Er spart
etwas Zeit für die Eclipse-Einrichtung und ist eigentlich besser dafür geeignet, ein
komplett neues Plugin zu schreiben, welches die Funktionalitäten von Protégé lediglich erweitert. Der Nachteil liegt aber darin, dass man die Funktionalitäten des
Java-Debuggers nur innerhalb der Plugin Klassen benutzen kann. Der Protégé-Build
dient dabei nur als Ausführungsumgebung für das Plugin und der Java-Debugger
kann bei der Ausführung nicht verwendet werden.
Das erste Projekt, in welchem sich Protégé-Distributionsklassen befinden, wird
geöffnet. Es muss mit Hilfe des mitgelieferten Ant-Buildes einmal kompiliert werden. Ein mögliches Problem, welches während der Einarbeitungszeit auftrat, ist, dass
Eclipse das Ant-Build nicht sofort kompiliert, sondern trotz korrekt installiertem JDK
folgende Fehlermeldung ausgibt:
BUILD FAILED build.xml:27: Unable to find a javac compiler;
Das kann schnell gelöst werden, indem eine Eclipse Verknüpfung mit folgendem
Startparameter erstellt wird: -vm „path-to-javaw.exe“. In diesem Fall sieht es so aus:
-vm "C:\Program Files\Java\jdk1.6.0_13\bin\javaw.exe". Ansonsten muss es
entsprechend angepasst werden. Jetzt soll der Buildvorgang ohne weiterer Komplikationen verlaufen. Das sich am Ende öffnende Protégé Fenster kann ohne Bedenken
geschlossen werden, während der Eclipse Workspace mit Hilfe von dem Tastaturkürzel „F5“ aktualisiert wird. Nun befindet sich der Equinoxbuild von Protégé in dem
17
3. Plugin-Entwicklung mit Eclipse
Ordner build/dist/equinox und kann als Ausführungsumgebung für unser Plugin benutzt werden.
Jetzt muss das zweite Projekt, in dem sich die für das Plugin relevanten Klassen
befinden, enstprechend angepasst werden. Die Idee dabei ist folgende: während mit
Eclipse an der Plugin Entwicklung gearbeitet wird, werden zur Laufzeit vom EclipseCompiler alle Java-Klassen des Plugins in Javacode übersetzt, welcher sich dann
mit Hilfe der Eclipse Run Configuration sofort in der bereits kompilierten ProtégéDistribution einbinden und starten lässt.
Das Projekt wird zurzeit von Eclipse als fehlerhaft angezeigt, weil die Libraries
nicht in dem Buildpath vorhanden sind. Um dieses zu beseitigen, geht man mit der
rechten Maustaste auf das Projekt: Build Path > Configure Build Path.... In dem
Tab Libraries klickt man auf Add JARs.... Aus dem Ordner Protege4 > build >
dist > equinox wird org.eclipse.osgi.jar hinzugefügt. Im Ordner Protege4 > build
> dist > equinox > plugins befinden sich drei weitere jars: org.coode.owlviz.jar,
org.protege.editor.owl.jar, org.semanticweb.owlapi.jar. Zum Schluss wird log4j.jar aus
dem Ordner Protege4 > plugins > org.protege.common > lib hinzugefügt. Damit stellen alle nötigen Bibliotheken dem Projekt zur Verfügung.
Der nächste Schritt ist die Erstellung einer Run Configuration in Eclipse. Ihre
Funktionsweise besteht darin, dass die zur Entwicklungszeit kompilierten Klassen in
den Plugin-Ordner des Equinox Buildes gelinkt und dort ausgeführt werden. So werden alle Änderungen, die neu im Code gemacht worden sind, sofort ohne zusätzlichen
Kompilationsvorgang ausführbar.
Im Eclipse-Menü geht man auf Run > Run Configurations und erstellt eine neue
Konfiguration: rechte Maustaste auf Java Application > New. Diese kann beliebig
benannt werden, z.B. „OWLPropViz“. Das Projekt bleibt unverändert (OWLPropViz) und als Main class wird org.eclipse.core.runtime.adaptor.EclipseStarter gesetzt.
Im Tab Arguments wird das Feld VM Arguments mit folgenden Zeilen ausgefüllt:
1
2
- Dosgi . clean = true
- Dorg . protege . plugin . extra = $ { workspace_loc : OWLPropViz / build / classes }
Listing 3.1: Parameter der VM Arguments
Damit wird dem Protégé Build mitgeteilt, dass beim Start das Plugin geladen werden
soll. Als Working directory des Projektes wird
$workspace_loc:Protege4/build/dist/equinox
gesetzt, der Ordner mit dem Protégé-Build. Als letztes muss z.B. mit Hilfe des Windows Explorers manuell die Dateistruktur META-INF und plugin.xml aus dem OWLPropViz Verzeichnis nach OWLPropViz\build\classes kopiert werden. Mit dem
Knopf Run kann die Konfiguration nun gestartet werden.
3.4.2. Integration von Protégé 4 und OWLPropViz zu einem
Eclipse-Projekt
Zweck dieses Ansatzes ist sowohl Protégé als auch das Plugin OWLPropViz zu einem
Eclipse-Projekt zu integrieren. Diese Integration ermäglicht die Nutzung des Java-
18
3.4. Protégé kompilieren und Run Configuration für das Plugin erstellen
Debuggers in den Quelltexten des ganzen Protégé Buildes.
Die Ausgangssituation ist die gleiche, wie bei dem ersten Ansatz: es liegen zwei
Eclipse-Projekte vor. Der erste Schritt besteht darin, unter Eclipse das Projekt OWLPropViz zu entfernen. Dabei ist zu beachten, dass nicht die Projektdateien auf
der Festplatte gelöscht werden sollen, sondern lediglich die Projektinformationen im
Workspace. Mit der rechten Maustaste auf dem OWLPropViz > Delete wird der
Lösch-Dialog aufgerufen, bei dem kein Haken auf Delete project contents on the disk
gesetzt wird.
Als nächstes wird aus dem SVN Checkout Project ein ganz normales Java Project
gemacht. Dafür beendet man Eclipse und entfernt alle .svn Dateien in beiden Ordnern
auf der Festplatte. Das kann z.B. mit Hilfe des Windows Explorers unter Windows
gemacht werden. Dafür trägt man als Suchbegriff .svn ein. Damit sollen alle .svn
Ordner und .svn-base Dateien gefunden werden. Im Falle dieser Bachelorarbeit sind
es 2224 Elemente, die dann alle gelöscht werden. Dabei muss noch beachtet werden,
dass es sich hierbei meist um versteckte Dateien handelt, d.h. unter Windows muss
die Option des Windows Explorers zur Anzeige von versteckten Dateien aktiviert
werden.
Abbildung 3.1.: Suche nach „.svn“-Datei im Windows Explorer
Nach einem Neustart von Eclipse wird das Projekt Protege4 gelöscht, wieder ohne
die Festplattenressourcen zu löschen. Im Anschluss geht man im Eclipse-Menü auf
File > New > Java Project (oder ein Projekttyp seiner Wahl, falls benötigt) und
gibt als Namen wieder „Protege4“ ein. Unten im Wizard erscheint eine Anmerkung,
dass die Einstellungen des bereits existierenden Ordner auf der Festplatte mit dem
Projekt übernommen werden. Dieses kann mit Finish bestätigt werden. Damit wird
ein komplett neues von Repositories unabhängiges Projekt erstellt, welches später
komplett in eigenes Repository für die Entwicklungszeit hochgeladen werden kann.
Jetzt stellt sich die Aufgabe OWLPropViz in die vorhandene Protege4-Struktur zu
integrieren. Im Ordner Protege4 > plugins auf der Festplatte erstellt man manuell
einen neuen Ordner „de.tubs.cs.ifis.owlpropviz“. Dieser wird mit folgenden Inhalten
des OWLPropViz Odners gefüllt (copy pase): META-INF, resources, src, build.xml,
plugin.xml. Somit erreicht man die Struktur, in welcher auch andere Plugins von
Protégé gespeichert sind. Damit Eclipse mit dem internen Compiler nur die relevanten
OWLPropViz Dateien kompiliert und diese dann in den Equinox-Build von Protégé
19
3. Plugin-Entwicklung mit Eclipse
einbindet, müssen einige Änderungen am Build-Path des Projekts vorgenommen werden. Als erstes wird unter Eclipse der zur Zeit nicht benutze src-Ordner gelöscht.
Danach öffnet man die Einstellungen für den Build-Path mit der rechten Maustaste auf dem Projekt Build Path > Configure Build Path.... In dem Tab Source wird
nun den Quelltextordner des OWLPropViz als „src“ verlinkt. Mit dem Knopf Link
Source... wird als Pfad für Linked folder location „X:\PathToWorkspace\Protege4\plugins\de.tubs.cs.ifis.owlpropviz\src“ eingetragen. Wenn das Projekt zu der Entwicklungszeit in der Repository von mehreren Entwicklern parallel benutzt werden
sollte, kann auch „X:\PathToWorkspace\“ durch eine Path-Variable ersetzt werden, welche jeder Entwickler für sich auf den später benötigten Wert setzen kann.
Als letzes in diesem Tab wird der Default output folder... auf „Protege4/plugins/de.tubs.cs.ifis.owlpropviz/build/classes“ gesetzt. Nach dem Bestätigen dieser Einstellungen erscheint in dem Projekt ein src-Ordner, welcher auch vom internen EclipseCompiler erkannt wird, was durch die in den Dateien erscheinenden Fehlermeldungen
gesehen werden kann. Diese kommen daher, dass noch keine Libraries in den Build
Path eingebunden sind. Das wird genauso wie im vorhergehenden Unterkapitel gemacht. Man fügt org.eclipse.osgi.jar, org.coode.owlviz.jar, org.protege.editor.owl.jar,
org.semanticweb.owlapi.jar, log4j.jar und org.protege.editor.core.application aus dem
Ordner Protege4 > build > dist > equinox > bundles zu dem Build Path hinzu.
Sind diese Schritte erledigt, so ist das Projekt ohne weiterer Eclipse-Fehlermeldungen fertig eingerichtet. Jetzt fehlt nur noch eine angepasste Run Configuration,
welche es ermöglichen wird, die gemachten Änderungen gleich in Protégé in kompilierter Form zu sehen. Das wird ähnlich wie bei den getrennten Eclipse-Projekten
gemacht. Man erstellt die gleiche neue Run Configuration wie zuvor, mit einer einzigen Änderung: der Ordner mit den kompilierten OWLPropViz Klassen soll geändert
und unter VM Arguments auf
-Dorg.protege.plugin.extra=$workspace_loc:Protege4/plugins/
de.tubs.cs.ifis.owlpropviz/build/classes gesetzt werden. Damit Protégé auch dieses
Mal über die Existenz unseres Plugins informiert wird, müssen der Ordner „METAINF“ und die Datei „plugin.xml“ in den Ordner Protege4/plugins/de.tubs.cs.ifis.owlpropviz/build/classes kopiert werden. Damit ist die Eclipse Einrichtung abgeschlossen und OWLPropViz kann bearbeitet werden.
3.5. Updaten von Protégé
Im Laufe der Arbeit kam es zu einem Update von Protégé. Dieses Ereignis bot eine
gute Möglichkeit folgendes zu testen: der Workspace in Eclipse soll auf die neue
Version umgestellt werden, was bei der vorhanden Architektur des Projektes nicht
ohne weiteres möglich ist. Ein kleiner Umweg muss in Anspruch genommen werden.
Zunächst wird die extra SVN-fähige Eclipse-Version genutzt, mit deren Hilfe die
letzte Version von Protégé heruntergeladen wird. Um sicherzustellen, dass es sich
tatsächlich um ein Update handelt, wird die build.xml als Ant-Build ausgeführt.
Ergebnis: im Protégé-Menü „About“ finden wir die aktuelle Buildnummer - 113.
20
3.5. Updaten von Protégé
Die sich unter Versionsverwaltung befindenden Dateien und Ordner werden kopiert
und dem aktuellen Projekt hinzugefügt. Alle Dateien werden überschrieben und die
SVN-Dateien (siehe Kapitel 3.4.2) gelöscht.
21
4 Architektur
4.1. Aufbau von OWLViz
OWLViz ist ein für Protégé entwickeltes Plugin zur Visualisierung von Ontologien. Das Hauptpaket des Plugins ist uk.ac.man.cs.mig.coode.owlviz, welcher alle
Klassen für die graphische Anzeige in Protégé beinhaltet. Das Plugin arbeitet mit einem Graphen, dessen Klassen sich im zweiten Paket uk.ac.man.cs.mig.util.graph
befinden. Das Layout des Graphen wird dabei von einem externen Programm, GraphViz, gemacht. Die Schnittstelle, die die Kommunikation zwischen den beiden Programmen ermöglicht, befindet sich ebenfalls in diesem Paket.
4.1.1. Das Paket uk.ac.man.cs.mig.coode.owlviz
Paket command enthält alle Klassen, die die einzelnen Funktionalitäten der ToolbarKnöpfe beschreiben. So ist z.B. die Klasse HideSubclassesCommand für das Verstecken der Kinderknoten in einem aktiven Graphen des OWLViz zuständig.
Paket export beinhaltet zurzeit eine einzige Klasse DotExportFormat, die eine Implementierung der Klasse ExportFormat für das Exportieren im AT&T GraphvizDOT-Format bietet. Dieses Format wird als Schnittstelle zwischen Protégé und GraphViz benutzt. Weitere Implementierungen befinden sich in dem zweiten Paket util.
graph und werden später beschrieben.
Paket model: hier befinden sich verschiedene Modelle mit Hilfe deren OWLViz
einen Graphen erstellt. Die Klassen, die in OWLViz selbst benutzt werden, sind
OWLClassGraphAssertedModel und OWLClassGraphInferredModel, welche entsprechend für das Aufbauen der asserted- und inferred-Modelle zuständig ist. Bei asserted-Modell handelt es sich um eine ganz normale Klassenhierarchie, die vom Hierarchyprovider des Protégé zur Verfügung gestellt wird, während das inferred-Modell
die vom Reasoner ausgewertete Hierarchie darstellt. Ein weiteres Modell ist das OWLVizAxiomGraphModel. Es ermöglicht einen Graphaufbau anhand von Axiomen zu
erstellen. So können z.B. aus Axiomen die Richtung der Edges oder deren Name
ausgelesen werden. Dieser Ansatz ist die Grundlage für die Entwicklung von OWLPropViz.
Paket ui ist das Hauptpaket des OWLViz-Plugins. Hier befindet sich die Klasse
OWLVizView, die eine Eingangsklasse des Plugins darstellt. In dieser Klasse wird die
22
4.1. Aufbau von OWLViz
ganze graphische Oberfläche des Plugins initialisiert und bietet somit eine graphische
Benutzerschnittschtelle für die Ontologievisualisierung.
4.1.2. Das Paket uk.ac.man.cs.mig.util.graph
Paket controller enthält Schnittstellen zur Verwaltung von Graphen (GraphGenerator), dessen angezeigten und versteckten Objekte
(VisualisedObjectManager) und der Graphselektionen (GraphSelectionModel).
Das Interface Controller und seine Implementierung sind ebenfalls in diesem Paket
enthalten. Controller ist die Schnittstelle aller Komponenten von OWLViz. Hier
findet auch die Initialisierung einzelner Komponenten statt.
Paket event - hier befinden sich alle Events und die Interfaces für die Listener des
OWLViz Plugins. So wird z.B. in der Klasse NodeClickedEvent ein Event behandelt,
bei welchem der Benutzer auf einen Knoten geklickt hat. Der ausgewählte Knoten und
der auslösende MouseEvent werden als Quelle übergeben. Die weiteren Events sind:
GraphGenerator, GraphModel, GraphSelectionModel, VisualisedObjectManager
und ThumbnailViewSource.
Paket export bietet die Möglichkeit den aktuell angezeigten Graphen in einem
der hier vorhandenen Formate zu exportieren. Abstrakte Klassen zur Erstellung von
Raster- und Vektorgraphik sowie konkrete Formate JPEG, PNG und SVG sind als
Implementierungen des allgemeinen Exportformats vorhanden.
Paket factory erbt seinen Namen von dem hier verwendeten Entwurfsmuster Factory. Beinhaltet sind die Interfaces und Default-Implementierungen zur Generierung
von Graphen, Knoten und Kanten.
Paket graph bietet Schnittstellen und Implementierungen von Graphen, Knoten
und Kanten, die dann später im oben beschriebenen Paket factory generiert werden. Deren Form und Farbe werden hier ebenfalls definiert.
Paket layout ist dafür zuständig, die einzelnen Elemente des Graphen anzuordnen. Das Interface GraphLayoutEngine definiert die Integer Konstanten LAYOUT_LEFT_TO_RIGHT und LAYOUT_TOP_TO_BOTTOM, welche für die Richtung
des Graphen zuständig sind, sowie Methoden zur deren Setzung und Erstellung des
Layouts. Das Unterpaket dotlayoutengine beinhaltet die von OWLViz tatsächlich
benutzte Implementierung der GraphLayoutEngine. Dabei handelt es sich um eine
Schnittstelle zu dem externen Programm GraphViz, welches die Aufgabe der Anordnung von Knoten und Kanten übernimmt. Die anzuordnenden Knoten und Kanten
werden in Form einer dot-Datei an GraphViz weitergegeben. Nach der Anordnung
wird die Ausgabedatei wieder von der GraphLayoutEngine eingelesen. Dafür ist das
Unterpaket dotparser zuständig, welcher die Informationen zur dot-Grammatik enthält und das Parsen der GraphViz-Ausgabedatei übernimmt. Die so entstehenden
23
4. Architektur
Anordnungsinformationen werden an OWLViz weitergegeben, wo dann die Visualisierung stattfinden kann.
Paket model beinhaltet die Information über das Graphenmodell. In dem Interface
GraphModel sind die Konstanten für die Pfeilrichtungen definiert (none, forward,
both, backward) und im Unterpaket impl konkrete Implementierungen vorhanden.
Darin sind z.B. die Methoden zum Hinzufügen von Knoten und Kanten, Abfragen
von Eltern- oder Kinderknoten usw. implementiert.
Paket outputrenderer ist dafür zuständig den aktuellen Graphen in das dot-Format
umzusetzen, welches dann von GraphViz zur Anordnung verarbeitet werden kann.
Dabei können auch Kommandozeileparameter durch das Aufrufen der Methode setRendererOption(String attribute, String value) spezifiziert werden.
Paket renderer ist für das Rendern von Knoten und Kanten, sowie deren Label
zuständig. Für diesen Zweck werden dementsprechend 4 Schnittstellen definiert, deren Default-Implementierungen sind hier ebenfalls vorhanden.
Paket ui enthält die Klassen, die die Methoden zum Zeichnen des für den Nutzer sichtbaren Graphens zuständig sind.
4.2. Aufbau des alten OWLPropViz
OWLPropViz ist eine Weiterentwicklung von OWLViz und erweitert die bisherigen
Funktionalitäten um Anzeige von Objekt-Properties. OWLViz besitzt bereits die nötigen Implementierungsansätze, die aber nicht ausgearbeitet oder benutzt werden.
Somit ist die Aufgabe des OWLPropViz, die bereits vorhandenen Ansätze so anzupassen und zu erweitern, dass dadurch die Property-Anzeige ermöglicht wird.
Im Folgenden ist die Paketstruktur des OWLPropViz dargestellt, die als Grundlage
für die Weiterentwicklungen in dieser Bachelorarbeit genommen wurde.
Im Hauptpaket de.tubs.cs.ifis.owlpropviz befinden sich 4 Klassen und 2 Unterpakete, die die vollständige Funktionalität des Plugins gewährleisten.
4.3. Aufbau des erweiterten OWLPropViz
In diesem Unterkapitel wird zunächst der Aufbau einzelner Funktionen betrachtet
und anschließend die Paketstruktur in welcher sich die einzelnen Komponenten befinden.
4.3.1. Architektur der neuen Funktionalitäten
Bevor die Implementierung neuer Funktionen erfolgen kann, müssen bei jeder Aktion die vorhandenen Problematiken besprochen und ein grundlegendes Konzept zu
24
4.3. Aufbau des erweiterten OWLPropViz
Abbildung 4.1.: Alte Struktur des OWLPropViz
deren Lösung festgelegt werden. Die folgende Diskussion einzelner Aufgabenstellungen beschreibt wodurch diese erreicht werden können und welche Grundstrukturen
in dem Interaktionsprozess involviert sind. Dabei dient es sowohl als eine Grundlage
zu Implementierung der gestellten Aufgaben, als auch für die Beschreibung der Implementierungsarchitektur.
Automatische Grapherstellung beim Laden des Plugins. Die Grundidee zur
Realisierung dieser Funktion liegt in der Vervollständigung des Moduls, der gleich
zum Anfang des Ladevorgangs ausgeführt wird. Die Funktionen zur Erstellung eines
Graphen sind bereits in den Quelltexten des OWLPropViz vorhanden und werden mit
vorgegebenen Werten versehen. Diese sollen einen optimalen Graphen liefern, welcher
dem Benutzer den groben Aufbau der Ontologie und deren Inhalte vermitteln kann,
und können aus Erfahrung ermittelt werden. Zusätzlich wird ein Optionendialog hinzugefügt, in welchem die automatische Grapherstellung deaktiviert werden kann.
Darstellung der Visualisierungsinformationen. Hierbei handelt es sich um die
angezeigten Knoten und Kanten. Eine separate Anzeigefläche ist in Form eines Protégé ViewComponents realisiert und trägt den Namen OWLPropVizGraphInfo. Diese
Fläche kann vom Benutzer beliebig platziert werden und ist für die Anzeige der Gesamtzahl so wie der visualisierten Zahl der Elemente zuständig. Es taucht aber das
Problem auf, dass die beiden Arbeitsflächen (Anzeigeinformationen und der Graphbereich) nichts von der gegenseitigen Existenz wissen. Der Ansatz die Kommunikation über die entsprechenden Ereignisse herzustellen, kann aber nicht ohne Weiteres
umgesetzt werden. Die bereits vorhandenen Ereignissysteme, die Protégé zur Verfügung stellt, beinhalten nicht die Events, die bei der Veränderung der Anzahl von
visualisierten Objekten aufgerufen werden. Um diese Problematik zu lösen, ist ein
neues Plugin eigenes Eventmanagement erstellt worden. Zum einen können darin
beliebige Ereignisse und deren Listeners definiert werden, zum anderen - durch das
25
4. Architektur
statische Singleton-Entwurfsmuster existiert zu jedem Zeitpunkt nur eine Instanz des
EventManagers und kann aus beliebigen Programmteilen erreicht werden. Somit ensteht ein neues Kommunikationssystem, welches auch von den beiden Arbeitsflächen
benutzt wird und zukünftig beliebig um neue Ereignisse erweitert werden kann.
Ist die Kommunikationsmöglichkeit einmal gegeben, ergibt sich der Rest von selbst.
Beim Laden der Ontologie werden einmalig alle Elemente durchgezählt. Darunter fallen einfache Klassen, äquivalente Klassen, Individuen und alle Typen der in der Ontologie vorhandenen Beziehungen. Verändert sich die aktuelle Anzeige, wird OWLPropVizGraphInfo darüber informiert und berechnet die aktuellen Anzeigezahlen,
welche sofort auf dem Bildschirm erscheinen. Auch die Veränderung der Ontologie,
z.B. wenn eine neuen Klasse hinzugefügt wird, erhalten eigene Events, die die entsprechenden Zahlen, auch die Gesamtzahl des Objekttyps, erhöhen bzw. verringern.
Sofortige Aktivierung der Toolbartasten. Wie bereits in der Einleitung erwähnt, liegt die Tatsache, dass die Toolbartasten anfangs deaktiviert sind, daran,
dass zur Ladezeit des Plugins keine Klasse in dem Hierarchiebaum ausgewählt wird.
Die Ontologien sind aber so aufgebaut, dass es in jeder Ontologie eine Klasse „Thing“
vorhanden ist. Somit wird die Funktionalität, die für das Auswählen der Klassen im
Hierarchiebaum zuständig ist, zeitlich zum Initialisierungsvorgang aufgerufen und
sorgt für die Aktivierung der Toolbartasten.
Doppelklick auf einem Knoten führt zum passenden Tab. Um dem Benutzer einen schnelleren Zugriff auf den richtigen Tab zu ermöglichen, wird dieser mit
einem Doppelklick aufgerufen. Protégé beinhaltet die Funktion, die die einzelnen
Tabs eindeutig identifizieren und aktiv setzen kann. So findet eine Unterscheidung
zwischen den Knotentypen statt, bei welcher eine Klasse vom Individuum getrennt
wird. Gleich im Anschluss erfolgt der Aufruf des entsprechenden WorkspaceTabs.
Wiederherstellung und Erweiterung der Show und Hide Funktionen. Da
die Hide und Show Funktionen einfach aus dem OWLViz übernommen worden sind,
können sie nicht direkt auf das neue Modell angewandt werden. Im OWLViz sind
weder Properties noch Individuen vorhanden. Das Anzeigen oder Verstecken eines
Objektes ist zwar funktionsfähig, aber die Kinder- und Elternknoten können nicht
korrekt ermittelt werden, da die Informationen über die neuen Objekttypen nicht
von OWLViz abgefragt werden. Dafür wurde eine neue Klasse DisplayManager erstellt, die diese Funktionalitäten zur Verfügung stellt und die Show/Hide-Funktionen
entsprechend auf deren Nutzung einstellt.
Der DisplayManager benutzt die Informationen, die nach der Erstellung des Graphen im OWLViz vorhanden sind. Diese beinhalten die Knoten und Kanten eines
visualisierten Graphen. Beim Laden der Ontologie, noch vor der sichtbaren automatischen Graphenanzeige, wird kurzzeitig ein kompletter Graph samt aller Objekten und Beziehung erstellt. Die darin vorhandenen Knoten und Kanten werden im
DisplayManager abgespeichert. Beim Aufruf der Funktionen zur Ermittlung von
über- und untergeordnetet Knoten werden genau diese Informationen benutzt. Wenn
26
4.3. Aufbau des erweiterten OWLPropViz
der Benutzer im Laufe seiner Aktivitäten eine neue Auswahl für die angezeigten
Kanten trifft, werden diese ebenfalls im DisplayManager aktualisiert, um die ausgewählten Kanten beim Navigieren durch die Baumstrukturen zu benutzen.
Noch eine Anpassung betrifft den Toolbarknopf „Show Class“. Dieser kriegt seine ursprüngliche Funktion, welche in den älteren Versionen von OWLViz vorzufinden
waren. Es wird lediglich die im Hierarchiebaum ausgewählte Klasse zum Graphen hinzugefügt. Damit entstehen zwei Möglichkeit des Einsatzes dieser Funktion. Wenn der
Graph ausgeblendet ist, kann diese ausgewählte Klasse als Startpunkt zur Ontologievisualisierung benutzt werden. Ausgehend von dem Knoten können die Baumstrukturen in deren Hierarchie sowohl nach oben als auch nach unten angezeigt werden.
Steht ein Ausschnitt einer Ontologie wiederum fest, wird das ausgewählte Objekt einfach zum Graphen hinzugefügt und kann z.B. zur Erstellung neuer Kanten benutzt
werden.
Anzeige der Zusammensetzung von äquivalenten Klassen. Die Informationen
darüber, was eine äquivalente Klasse ausmacht, können in Protégé manuell aufgerufen werden. Diese werden in Form einer Description zurückgegeben, welche nach dem
Durchlaufen eines Renderers in einer für den Menschen angenehm lesbaren Textform
ausgegeben wird. Um die Anzeige dieser Informationen innerhalb des Plugins zu
ermöglichen, wird ein Tooltip benutzt. Während der Mauszeiger sich im Anzeigebereich einer äquivalenten Klasse befindet, wird eine Description dafür angefordert
und ebenfalls nach dem Durchlaufen eines Renderers in dem Tooltip angezeigt. Um
diese Anzeige farblich in Einheit mit der von Protégé zu halten, werden die Inhalten
des Tooltips in einem HTML-Format dargestellt. Die Farben einzelner dabei vorkommenden Keywords (and, or, some usw.) werden in der für Protégé festgelegten
KeyColorMap abgefragt und durch das Hinzufügen der entsprechenden HTML-Tags
in der richtigen Farbe dargestellt.
Löschen und Hinzufügen einzelner Knoten. Diese beiden Funktionen haben
nicht nur Auswirkungen auf die Anzeige des Graphen, sondern auch auf die Ontologie selbst. Da jeder Knoten entweder eine Klasse oder ein Individuum repräsentiert,
muss beim expliziten Löschen oder Hinzufügen eines Knotens, die entsprechende Referenz in der Ontologie erneuert werden. Die Änderungen innerhalb der Ontologie
werden mit vorhandenen Protégé-Mitteln durchgeführt. Der Benutzer bekommt die
Möglichkeit mit Hilfe eines Kontextmenüeintrages auf die nötigen Funktionalitäten
zuzugreifen. Beim Löschen des Knoten wird die Unterscheidung bei dem Typen des
Knotens gemacht, um die passende Funktion zum Entfernen einer Klasse bzw. eines Individuums aufzurufen. Um ein Individuum zu löschen ist eine etwas einfachere
Methode mittels OWL-API [Hor09] implementiert, während die anderen Funktionen
aus Protégé übernommen werden.
Hinzufügen von Kanten. Dieser Prozess hat ebenfalls eine Auswirkung sowohl
auf den Graphen als auch auf die Ontologie selbst. Für diese Zwecke wurde zunächst
ein Konzept entwickelt, welchem der Benutzer folgen muss um eine Verbindung zwi-
27
4. Architektur
schen zwei Knoten herstellen zu können. Die Beziehungen haben in einer Ontologie
folgende Form: Subjekt - Prädikat - Objekt, wobei Prädikat graphisch als eine Kante
repräsentiert wird. Beispielsweise wird der Fall betrachtet, in welchem ausgehend von
dem Knoten Professor eine Kante worksAt zu dem Knoten University erstellt wird.
Dafür muss im Kontextmenü des Knoten Professor der Menüeintrag „Add restriction
to...“ ausgewählt werden. Danach nimmt der Mauszeiger die Form eines Kreuzes an,
um dem Benutzer zu signalisieren, dass nun die Auswahl des Zielknotens, also des
Objekts, getroffen werden soll. Ist der Zielknoten University ausgewählt, erscheint
ein Dialog, wo das Prädikat entweder aus den vorhanden ausgewählt oder ein neues hinzugefügt werden kann. Nach der Bestätigung des Dialogs wird mit sofortiger
Wirkung die Änderung sowohl in der Ontologie als auch in dem Graphen durchgeführt. Zunächst erfolgt die Änderung innerhalb der Ontologie, indem wieder auf
die Funktionalitäten von OWL-API zugegriffen wird. Danach wird die neue Kante
aus den Ontologieaxiomen extrahiert und zu den zu visualisierenden hinzugefügt.
Somit kriegt der Benutzer eine sofortige Rückmeldung, dass seine Kante erfolgreich
hinzugefügt werden konnte.
4.3.2. Übersicht: Die Paketstruktur
Nach dem Abschluss der Implementierungsarbeiten der beiden parallel laufenden
Bachelorarbeiten ist eine Großzahl an neuen Klassen hinzugekommen, die im ersten
Augenblick die Struktur der neuen OWLPropViz Version unübersichtlich machen. Jedoch lassen sich dabei 2 Klassentypen unterscheiden: komplett neue und angepasste
Klassen. Die neu hinzugekommenen Klassen tragen neue Funktionalitäten mit sich,
werden in die vorhandene Strukturen integriert und sind nach Möglichkeit in eigenen
Paketen untergebracht. Die angepassten Klassen lassen sich an dem Präfix „PropViz“
des Klassennamens erkennen und dienen dazu, die alten Funktionen an den neuen
Funktionsumfang anzupassen. Die meisten dieser Klassen entstanden während der
Ausarbeitung von der parallelen Bachelorarbeit [Sch09] und werden dort näher erläutert. Untergebracht sind sie in den gleichnamigen Paketen wie in OWLViz. Alle
Pakete befinden sich in dem Hauptpaket de.tubs.cs.ifis.
Paket owlpropvizgraphinfo Dieses Paket beinhaltet die Klassen zur Realisierung des neuen ViewComponents „OWLPropVizGraphView“. OWLPropVizGraphInfoViewComponent ist die Eingangsklasse dieses Components, welche in der Datei
plugin.xml spezifiziert ist. Darin ist die Anzeigetafel für die Visualisierungsstatistiken untergebracht und die Methoden zu deren Ausgabe im bequemen HTML-Format
definiert. GraphItemsStatistics beinhaltet die Elemente zur Speicherung der Statistiken, sowie Funktionen zu deren Initialisierung und Erneuerung.
Paket owlpropvizgraphinfo.counters Hier befinden sich die Zählerklassen der
Kanten und Knoten, welche die Anzahl der einzelnen Objekte speichern können.
Paket owlpropviz.command
28
In diesem Paket befinden sich die angepassten Klas-
4.3. Aufbau des erweiterten OWLPropViz
sen, die dem Command-Entwurfsmuster folgen. Darunter sind z.B. die Klassen zum
Anzeigen und Verstecken der Kinder- und Elternknoten des Graphen.
Paket owlpropviz.controller Dieses Paket besitzt dieselbe Funktionalität wie
in OWLViz. Im Rahmen dieser Bachelorarbeit wurden die Klassen DefaultProperyController und VisualisedObjectManager angepasst, um dem Benutzer im Graphen
die Informationen über die versteckte Knoten anzuzeigen (siehe Kapitel 5.3.1).
Paket owlpropviz.layout Dieses Paket erlaubt den Zugriff auf die Klasse, die
für das Erstellen des Layouts zuständig ist. Damit können wahlweise Parameter an
GraphViz übergeben und die von GraphViz generierte Datei eingelesen werden.
Paket owlpropviz.ui Die innere Struktur dieses Pakets und die Funktionalitäten entsprechen dem gleichnamigen OWLViz-Paket. Zusätzlich sind Klassen für die
Anzeige der „AutoDisplay“-Optionen hinzugefügt, welche dem Benutzer erlauben die
automatische Grapherstellung beim Laden des Plugins auszustellen.
Paket owlpropviz.util Dieses Paket beinhaltet die meisten Klassen, die für die
neuen Funktionalitäten des Plugins verantwortlich sind. Die Klasse PropVizPopupMenu ist eine Implementierung der im PropViz benutzten Kontextmenü, welche den
Aufbau und die bei der Auswahl auszuführenden Befehle enthält. HtmlHighlighter
wird zur Färbung der Schlüsselwörter in dem Tooltip der äquivalenten Klassen benutzt. Der DisplayManager ist die Kernklasse, die das neue Visualisierungsmodell
implementiert. Hier werden alle Informationen über vorhandene und angezeigte Objekte gespeichert und bearbeitet. Das Unterpaket event enthält alle im Rahmen
dieser Bachelorarbeit definierten Events und deren Listeners, während die Klasse
EdgeCreator eine konkrete Implementierung zur Erstellung und Anzeige der Kanten
bietet.
Die im folgenden Listing aufgezählten Pakete haben keine direkten Auswirkungen
auf die Funktionalitäten dieser Bachelorarbeit. Deren Aufgaben entsprechen teilweise denen aus OWLViz (Kap. 4.1). Die Details über die Zusatzfunktionalitäten können
der parallelen Bachelorarbeit [Sch09] entnommen werden.
1
2
3
4
5
6
7
8
9
owlpropviz . axiomtests . d i f f e r e n t i n d i v i d u a l s w r a p p e r
owlpropviz . axiomtests . i n d i v i d u a l r e l a t i o n s h i p w r a p p e r
owlpropviz . axiomtests . s a m e i n d i v i d u a l s w r a p p e r
owlpropviz . extractor
owlpropviz . graph
owlpropviz . layout
owlpropviz . o b j e c t p r o p e r t y w r a p p e r
owlpropviz . output renderer
owlpropviz . renderer
Listing 4.1: Weitere Pakete von OWLPropViz
29
5 Implementierung
5.1. Erstellung von angepasstem build.xml
Bevor auf die Implementierungsdetails dieser Bachelorarbeit eingegangen wird, ist
noch eine Vorbereitung notwendig. Dabei geht es um die Datei build.xml in dem
Protege4-Ordner, welche für den Ant-Build zuständig ist und sowohl am Ende als
auch während der Entwicklungszeit zur Erstellung eines lauffähigen Protégé Buildes
mit kompiliertem OWLPropViz-Plugin benötigt wird.
Im ersten Schritt wird eine Kopie des vorhandenen build.xml gemacht. Der Name
kann beliebig gewählt werden, z.B. build-protegePropViz.xml. Diese Kopie wird dafür angelegt, damit auch in der Zukunft die Gelegenheit besteht zwischen den beiden
Builds zu wählen. Der Code, der für das zusätzliche Kompilieren des Plugins benötigt wird, wird ähnlich wie bei den anderen Plugins in die xml-Datei hinzugefügt.
Die Codesturktur für den Build des Plugins OWLViz dient hierbei als Grundlage.
Mit der Copy-Paste-Methode wird der Teil von build.xml dupliziert, welcher für die
Kompilierung von OWLViz zuständig ist. Es handelt sich hierbei um den Code, der
mit dem OWLVIZ - create distribution for the owlviz bundle Kommentar versehen
ist. Jetzt können die für OWLPropViz relevanten Änderungen gemacht werden.
Am Ende des Änderungsvorgangs, muss es folgende Form haben:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<! - - test -->
< property name = " owlpropviz . name " value = " de . tubs . cs . ifis . owlpropviz " / >
< property name = " owlpropviz . dir " location = " ${ plugin . dir }/${ owlpropviz . name } " / >
< property name = " owlpropviz . classes "
value = " ${ build . dir }/ classes /${ owlpropviz . name } " / >
< property name = " owlpropviz . build . file "
location = " ${ owlpropviz . dir }/ build . xml " / >
< property name = " owlpropviz . jar "
location = " ${ bundles . dir }/${ owlpropviz . name }. jar " / >
< target name = " owlpropviz " depends = " commonlibs , owlapi , core , owleditor "
unless = " owlpropviz . ignore " >
< ant antfile = " ${ owlpropviz . build . file } " target = " \${ sub . target } "
inheritRefs = " true " / >
</ target >
Listing 5.1: Neuer Code zur PropViz Kompilierung im build.xml
Im Folgenden wird beschrieben, welchen Zweck diese Änderungen haben.
• Man definiert eine neue Property, die owlpropviz.name heißt und den Wert
de.tubs.cs.ifis.owlpropviz hat, welcher die Paketstruktur des Plugins beschreibt,
bzw. den Pfad in welchem sich das OWLPropViz-Plugin innerhalb des Protege4/plugins Ordners befindet.
30
5.2. Realisierung der gestellten Aufgaben
• Mit dem Wert $plugin.dir/$owlpropviz.name der Property owlpropviz.dir wird
der oben definierte Ordner in dem allgemeinen Plugin-Ordner festgehalten.
• Die Property owlpropviz.classes gibt an, wohin die kompilierten Java Klassen
vor der JAR-Erstellung kopiert werden.
• owlpropviz.build.file spezifiziert den Ort der Plugin eigener Datei build.xml, die
bei der Erstellung des gesamten Buildes konkret für OWLPropViz aufgerufen
wird.
• Im owlpropviz.jar ist der Ordner angegeben, wo die fertige JAR-Datei abgelegt
wird.
• In der Target owlpropviz sind die für den Kompiliervorgang benötigten Libraries
angegeben.
In der Sektion INNER TARGETS soll auch owlpropviz hinzugefügt werden, damit die gerade definierten Zeilen zur Erstellung von OWLPropViz auch ausgeführt
werden. Anschließend wird an die entsprechenden Stellen <pathelement path = “$owloropviz.dir/src“/> in die xml-Elemente sourcepath und classpath des Javadoc Abschnittes hizugefügt, um nebenbei auch eine Javadoc-Dokumentation für das Plugin
zu erhalten.
Jetzt müssen auch die entsprechenden Änderungen in der Plugin eigenen build.xml
vorgenommen werden, da die Datei von jetzt an für den gesamten Ant-Build benötigt
wird und davon abhängige Ordner- und Dateipfade definiert sein müssen. Die Datei
befindet sich in dem Plugin-Ordner. Folgende Elemente sind zu modifizieren:
• Der Wert location der Property protege.home wird auf „$dist.dir/equinox“ gesetzt und teilt dem Plugin mit, wo sich der Hauptordner von Protégé befindet.
Auch src.dir und build müssen entsprechend auf „{$owlpropviz.dir}/src“ und
„{$owlpropviz.dir}/build“ gesetzt werden.
• Der Wert file von copy tofile=“${manifest“} wird auf „${owlpropviz.dir}/META-INF/MANIFEST.MF“ gesetzt, um die Position der Manifest Datei zu
spezifizieren.
• Der Resources-Ordner stimmt ebenfalls noch nicht und wird in der Target bundle unter <copy todir=“${classes}“> auf fileset dir=“${owlpropviz.dir}/resources“ gesetzt. Das gleiche wird auch für die Datei plugin.xml wiederholt unter copy todir=“${classes}“ indem der Wert file auf „${owlpropviz.dir}/plugin.xml“
gesetzt wird.
Die komplette Datei „build.xml“ kann dem Kapitel A.2 entnommen werden.
5.2. Realisierung der gestellten Aufgaben
Im Folgenden wird beschrieben, wie die jeweiligen Aufgaben umgesetzt sind.
31
5. Implementierung
5.2.1. Doppelklick führt zum passendem Tab
Diese Aufgabenstellung wird mit bereits in Protégé vorhandenen Mitteln umgesetzt.
Dafür wird bei einem Doppelklick auf einem Knoten der Befehl
getOWLWorkspace().setSelectedTab(Workspacetab wt)
benutzt, welcher den Benutzer zu dem angegebenen Tab führt. Die Tabs erben deren
Name aus dem Plugin Namen. Die Liste aller Tabs kann in die Konsole ausgeben
werden, um die Namen anzeigen zu lassen:
1
2
3
4
Set < WorkspaceTab > tabs = ge t OW LW or k sp ac e (). g e t W o r k s p a c e Ta b s ();
for ( int i = 0; i < tabs . size (); i ++) {
System . out . println ((( WorkspaceTab ) tabs . toArray ()[ i ]). getId ());
}
Listing 5.2: Ausgabe der Namen der Workspacetabs
Dabei tauchen auch die Namen der gesuchten Tabs auf:
org.protege.editor.owl.OWLClassesTab
org.protege.editor.owl.OWLIndividualsTab
Im letzten Schritt wird in dem NodeClickedListener in der Klasse OWLPropVizView
überprüft, ob es sich bei dem ausgewählten Knoten um ein Individuum oder eine
Klasse handelt, um dann den Benutzer zu dem entsprechenden Tab zu führen. Hier
ist der Code, der den Tab für die Klassenbearbeitung aufruft:
1
2
g et OW LW o rk sp ac e (). se tSelecte dTab (
g et OW LW o rk sp ac e (). ge tW o rk sp ac e Ta b ( " org . protege . editor . owl . OWLClassesTab " ));
Listing 5.3: Beim Doppelklick ausgeführte Aktion
Bug: handelt es sich bei dem ausgewählten Knoten um ein Individuum, so wird
zwar der passende Tab aufgerufen, das Individuum muss aber manuell im Hierarchiebaum ausgewählt werden. Das kann derzeit nicht beseitigt werden, weil es in Protégé
keine Methoden für den Zugriff auf den Individuenbaum von externen Paketen vorgesehen sind.
5.2.2. Sofortige Aktivierung der Toolbartasten
Die Grundidee bei der Implementierung ist, dass jede Ontologie in OWL die Klasse Thing standardmäßig beinhaltet. Die Modellverwaltung von Protégé besitzt die
in Frage kommende Methode, welche einen Zugriff auf eine Klasse über ihre String
Repräsentation erlaubt. Auch der OWLPropViz-Plugin beinhaltet bereits eine Methode, die das Auswählen einer angegebenen Klasse im Hierarchiebaum ermöglicht.
Die beiden Methoden sollen verkettet beim Laden des Plugins ausgeführt werden.
Der Initialisierungsvorgang findet in der Methode initializeView() der Eingangsklasse OWLVizPropertyView. Das Eintragen der folgenden Zeile in diese Methode führt
somit zu dem gewünschten Effekt.
1
2
32
public void i n i t i a l i s e C l a s s V i e w () throws Exception {
...
5.2. Realisierung der gestellten Aufgaben
s e t S e l e c t e d E n t i t y ( g e t O W L M o d e l M a n a g e r (). getOWLClass ( " Thing " ));
...
3
4
5
}
Listing 5.4: Realisierung der automatischen Auswahl der Klasse „Thing“
5.2.3. Automatische Graphenerstellung beim Laden des Plugins
Ähnlich wie bei der letzten Aufgabe wird diese Funktionalität in dem initialiseViewBlock des OWLVirPropertyView ausgeführt. Die Funktionen sind bereits vorhanden,
werden aber normalerweise vom Benutzer manuell gestartet.
Wichtig ist es festzulegen, was dem Benutzer angezeigt werden soll. Die Fragestellung lautet: welche Informationen und für welchen Zweck sollen visualisiert werden?
Wenn ein Benutzer täglich mit einer einzigen Ontologie arbeitet, wird er sehr wahrscheinlich selber eine Anfangsvisualisierung erstellen und eventuell beim nächsten
Mal ändern wollen. Somit ergibt in diesem Fall die automatische Generierung wenig Vorteile und kann daher im entsprechenden Optionenfenster deaktiviert werden.
Arbeitet der Benutzer wiederum immer mit neuen Ontologien, muss er sich immer
zu Anfang ein grobes Bild der Ontologieinhalte machen. Aus eigener Erfahrung lässt
sich behaupten, dass für diesen Zweck unter dem Plugin OWLPropViz einfach der
Knopf „Show Selected Properties“ aufgerufen wird, alle Optionen ausgewählt werden
und der Graph erstellt wird. Damit hat man den kompletten Graphen mit allen Objekten und Beziehungen. Dabei tritt ein Problem auf, dass der Graph sehr groß und
unübersichtlich wird. Eine mögliche Lösung dafür ist, den Baum samt aller Beziehungen nur bis zu einer gewissen Höhe anzuzeigen, die restlichen Elemente können
versteckt werden. Als optimal hat sich aus der Entwicklersicht die Baumhöhe von
drei erwiesen: ein Graph dieser Höhe passt gut auf dem Bildschirm. Da eine Onotolgie im Regelfall in jeder tieferen Baumstufe verfeinert wird, werden bei der Höhe von
drei die allgemeinen Konzepte angezeigt, die die thematische Richtung der Ontologie
bestimmen. Damit sollte der Benutzer einen guten erten Überblick erhalten. Falls
der Wunsch besteht die Informationen detaillierter angezeigt zu bekommen, kann
z.B. die „Show Domain“-Funktion des Kontextmenüs (siehe Unterkapitel 5.2.6) benutzt werden. Eine Möglichkeit die Konfigurationen des automatischen Ladevorgangs
manuell einzustellen werden aus zeitlichen Gründen nicht im Rahmen dieser Arbeit
betrachtet.
Die Funktion zur automatischen Anzeige des Graphen wird in der Klasse DisplayManager implementiert. Um die Baumhöhe setzen zu können, ist eine zusätzliche rekursive Hilfsmethode getClassesPastRadius() implementiert. Ein Radius (die
Baumhöhe) muss vorgegeben werden und eine Menge von Klassen, von der aus das
Traversieren bis zum angegebenen Radius geschehen soll. Da Klasse „Thing“ immer
die Baumwurzel ist, wird sie als Startklasse beim automatischen Laden übergeben.
1
2
3
4
5
public Collection < OWLClass > g e t C l a s s e s P a s t R a d i u s (
Collection < OWLClass > collection , int radius ) {
Collection < OWLClass > result = new HashSet < OWLClass >();
if ( radius >= 0) {
radius - -;
33
5. Implementierung
for ( OWLClass sub : collection ) {
result . add ( sub );
result . addAll ( g e t C l a s s e s P a s t R a d i u s ( view . g e t O W L M o d e l M a n a g e r ()
. g e t O W L H i e r a r c h y M a n a g e r ()
. g e t O W L C l a s s H i e r a r c h y P r o v i d e r (). getChildren ( sub ) ,
radius ));
}
6
7
8
9
10
11
12
}
return result ;
13
14
15
}
Listing 5.5: Implementierung der Methode getClassesPastRadius()
Diese Methode benutzt den normalen Hierarchiebaum, der auch im Asserted Class
Hierarchy des WorkspaceTabs angezeigt ist. Das bedeutet in diesem Fall, dass nur
diejenige Klasse, die eine „is-a“ Beziehung zu der aktiven Klasse besitzt, als Kind
bezeichnet wird. Zusätzlich werden zur automatischen Anzeige alle Kantentypen aktiviert. Die restlichen Befehle, die dazu nötig sind mit diesen Einstellungen einen
Graphen anzuzeigen, werden aus der Methode actionPerformded() der Klasse ShowSelectedPropertiesCommand übernommen. Als Ergebnis ergibt sich ein Baum der
Höhe drei, mit Darstellung aller Beziehungen zwischen den angezeigten Klassen.
5.2.4. Anzeige der Zusammensetzung der äquivalenten Klassen
Umsetzung: Erst während der Entwicklungszeit ist der endgültige Ansatz zur Realisierung dieser Funktion festgelegt worden. Zweck dieser Funktionalität ist dem Benutzer die Informationen über die Zusammensetzung einer äquivalenten Klasse direkt
im Graph anzuzeigen. Bisher musste man zum Tab „Classes“ im Protégé wechseln
und es unter Description (Beschreibung) der Klasse nachschauen. Der Schritt des
Tabwechsels muss also beseitigt und die in der Beschreibung stehenden Informationen direkt im Graphen angezeigt werden.
Diese Aufgabe wird mit Hilfe eines MouseMotionListeners gelöst, welcher zu jeder
Mausbewegung folgende Aufgaben erfüllt:
1. Die Koordinaten der Mausposition über dem Graphen werden festgehalten.
2. Alle Knoten im Graphen werden abgefragt, ob die Mausposition in der graphischen Repräsentation des Knotens liegt, bis der richtige Knoten gefunden wird.
Anmerkung: diese Art der Knotenbestimmung war im Quellcode des OWLViz
in auskommentier Form vorzufinden.
3. Der Knotenname wird abgefragt und die entsprechende Zusammensetzung in
Form einer Menge von OWLDescriptions angefordert.
4. Mit Hilfe eines Renderers entsteht daraus ein String, welcher genau den gleichen
Text wie in dem Tab Classes unter Description beinhaltet.
5. Ein ToolTip wird über dem Graphen angezeigt, der auf die Mausposition eingestellt ist und den Descriptiontext beinhaltet
34
5.2. Realisierung der gestellten Aufgaben
Zusätzlich werden noch zwei Schritte vorgenommen, die die Anzeige angenehmer
und verständlicher machen sollen. Die Größe des Tooltips wird in Abhängigkeit von
der Textlänge angepasst. Die Einstellungen der Anpassung wurde durch gezieltes
Ausprobieren ermittelt. Die Höhe des Tooltips wird dabei auf konstante 50 Pixel
gesetzt, während die Breite sich aus der einfachen Formel längeDesTextes * 2. Diese
Einstellungen führen zu einer gut auf die Länge des Textes angepassten Anzeige des
Tooltips.
Der zweite Schritt beschäftigt sich damit, die optische Anzeige des Textes zu verbessern, indem die Schlüsselwörter in den entsprechenden Farben hervorgehoben werden. Als Grundformat des Textes innerhalb des Tooltips wurde HTML ausgewählt,
also müssen vor und nach jedem Schlüsselwort die entsprechende Tags gesetzt werden.
Diese Aufgabe übernimmt die neu hinzugekommene Klasse HtmlHighlighter, welche
aus zwei Methoden besteht: performHtmlHighlighting() und die davon aufrufende
Methode setColor().
performHtmlHighlighting() hat als Eingabeparameter den kompletten String
mit der Beschreibung einer OWL Klasse. Dieser Text wird in einzelne Wörter mit
einem Tokenizer zerlegt. Für jedes dieser Tokens wird überprüft ob Protégé eine
konkrete Farbe dafür hat. Trifft man also bei einem Token auf die Schlüsselwörter
wie and, or, so liefert die Methode get aus der in Protégé vorhandenen Klasse
owlEditorKit.getWorkspace().getKeyWordColorMap().get(curToken)
die für das Schlüsselwort festgelegte Farbe, die Nicht-Schlüsselwörter bekommen die
Standardfarbe Schwarz, allerdings als eine Instanz der nativen Java-Klasse Color,
welche noch in die HTML-Darstellung umgewandelt werden muss.
Dafür ist die Methode setColor zuständig. Sie hat als Eingabeparameter die Farbe und das Wort, auf welches diese Farbe angewendet werden soll. Mit der Methode
Integer.toHexString() berechnet man nacheinander die Hex-Werte der roten, grünen und der blauen Farbe, welche dann in einem HTML-Tag
<font color="RRGGBB"></font>
auf das Schlüsselwort angewandt wird.
Sobald diese Schritte implementiert sind, ist auch die gestellte Aufgabe erfüllt.
Der Endnutzer kann während er mit dem Graphen arbeitet in einem Tooltip die
Zusammensetzung einer äquivalenten Klasse sehen, welche durch zusätzliche Farbenhervorhebung der Schlüsselwörter bessere Lesbarkeit ermöglicht.
Angetroffene Schwierigkeiten: bei der Umsetzung dieses Ansatzes gab es einen
sehr schwerwiegenden Bug, welcher zwar durch sehr intensives Testen behoben wurde, aber den Ansatz mit dem Tooltip fast zum Scheitern gebracht hat. Dabei handelt
es sich um die Anzeige des Tooltips, die immer zwischen zwei Positionen gesprungen
ist: zwischen der eigentlichen Position und einer, immer gleichen Position etwa in der
oberen Mitte des Graphen. Sobald dieser Bereich z.B. durch das Scrollen nicht mehr
sichtbar war, wurde auch der Tooltip korrekt an seiner eigentlichen Position angezeigt. Bei größeren Graphen, wie z.B. der komplett visualisierten Pizza Ontologie,
fiel dieser Bug kaum auf, weil die meiste Zeit nicht der Bereich oben links des Graphen angezeigt wird. Anders war es bei der uni.owl. Diese Ontologie passt visualisiert
komplett auf den Bildschirm, was dazu führte, dass immer der Problembereich des
35
5. Implementierung
Graphen anzeigt und der Bug immer sichtbar wurde.
Ein Ansatz dies zu erklären war, dass JToolTip eine statische Klasse ist und falls
ein anderer Teil von Protégé im Hintergrund auch auf den Tooltip zugreift, wäre
die Veränderung der Koordinaten des Tooltips einleuchtend. Dieses wurde aber widerlegt, indem der ganze Quelltext von Protégé nach Tooltips durchsucht und alle
verdächtigen Stellen auskommentiert wurden. Nach einer erneuten Kompilierung von
Protégé blieb der Bug weiterhin bestehen.
Die Lösung entstand durch das Festlegen eines Layoutmanagers für die aktive
Instanz der Klasse PropertyGraphView. Die Besonderheit dabei ist folgende: wird
der Layoutmanager zuerst festgelegt und erst dann der Tooltip hinzugefügt, bleibt
der Bug bestehen. Tauscht man aber die beiden Zeilen und fügt erst den Tooltip
hinzu, ist dieser Bug behoben. So lässt sich der Bug durch das Vertauschen der beiden
folgenden Befehle oder durch das Entfernen des Layoutmanagers für die zukünftigen
Bugfixansätze reproduzieren.
1
2
3
g e t P r o p e r t y G r a p h C o m p o n e n t (). g e t P r o p e r t y G r a p h V i e w (). add ( tt );
g e t P r o p e r t y G r a p h C o m p o n e n t (). g e t P r o p e r t y G r a p h V i e w ()
. setLayout ( new BorderLayout ());
Listing 5.6: Behebend des Tooltip-Bugs
Eine einleuchtende logische Erklärung dieses Phänomens konnte leider nicht gefunden werden.
Aktueller Bug: zurzeit werden die Tooltips nur bei dem Zoomlevel von 100% des
Graphen korrekt angezeigt. Alle anderen Vergrößerungs- und Verkleinerungsstufen
verändern auch die Größe des Tooltips, was zu einer falschen Anzeige und zusätzlichen
Verzerrung führt. Der Code wurde wiederum so geschrieben, dass die Zoomlevels
keinen Einfluss auf die Dimensionen und Position des Tooltips haben, was entweder
auf einen übersehenen Fehler oder auf einen Bug innerhalb des OWLViz Plugins
deutet. Eine weitere Vermutung ist, dass dieser Bug direkt was mit dem in dem
vorherigen Abschnitt beschriebenen Bug zu tun hat. Der Bug selbst konnte aber
leider nicht im Rahmen dieser Bachelorarbeit behoben werden.
5.2.5. Anzeige der Visualisierungsinformationen
Diese Aufgabe besteht aus drei wesentlichen Schritten: ein neues ViewComponent
wird erstellt und in der Pluginstruktur registriert, ein Kommunikationssystem zwischen einzelnen ViewComponents wird implementiert und letztendlich werden die
Informationen über die angezeigten und versteckten Katen und Knoten aus dem
Graphen ausgelesen und angezeigt.
Die Entscheidung die Visualisierungsinformationen in einem getrennten ViewComponent zu implementieren, wird dadurch begründet, dass ein ViewComponent je nach
Bedarf ein- und ausgeblendet und je nach Wunsch innerhalb des WorkspaceTabs verschoben werden kann. Ein ViewComponent ist eine Erweiterung der Protégé Struktur
und muss in der Datei plugin.xml gesondert registriert werden. Im folgenden Listing
5.7 befindet sich der dafür zuständige Abschnitt.
36
5.2. Realisierung der gestellten Aufgaben
1
2
3
4
5
6
7
8
9
...
< extension point = " org . protege . editor . core . application . ViewComponent "
id = " O W L P r o p V i z G r a p h I n f o " >
< label
value = " OWLPropViz Graphinfo " / >
< class
value = " de . tubs . cs . ifis . o w l p r o p v i z g r a p h i n f o .
OWLPropVizGraphInfoViewComponent "/>
< headerColor value = " @org . protege . ontologycolor " / >
< category
value = " @org . protege . o n t o l o g y c a t e g o r y " / >
</ extension >
Listing 5.7: Registrierung des OwlPropVizGraphView in der plugin.xml
Dabei geben extension point und id den Typ, hier ein ViewComponent, sowie
die Identifikation der Erweiterung an. Label gibt an, welchen Namen das ViewComponent in der Überschrift hat. Class spezifiziert die Eingangsklasse, die die abstrakte
Klasse AbstractOWLViewComponent implementiert. headerColor gibt die Farbe der
Überschrift an und category die Kategorie der Erweiterung. Da OwlPropVizGraphView zeitgleich zu den Visualisierungsinformationen eine Aufzählung aller Objekttypen und deren Properties mitliefert, wurde bewusst die ontologycategory ausgewählt.
Die angezeigten Informationen sollen einen guten Überblick über den Aufbau der
geladenen Ontologie verschaffen.
Der nächste Schritt ist die Einführung der Kommunikationsmöglichkeiten zwischen
den beiden ViewComponents: OWLPropViz und OWLPropVizGraphInfo. Jedes der
beiden ViewComponents wird von Protégé als eigenständiges Plugin betrachtet und
somit ist es nicht möglich, aus einem ViewComponent auf die Variablen des anderen
zuzugreifen. Es musste eine Schnittstelle geschaffen werden, auf welche jedes Plugin
zugreifen kann um diese dann in Kommunikationszwecken zu den anderen Plugins
benutzen zu können. Diese Schnittstelle musste aber auch ein standardisiertes Format
haben, um die Architektur verständlicher und zugänglicher zu machen. Die Wahl fiel
auf Events und Listeners, welche in einer nach dem Entwurfsmuster Singleton (Kap.
2.5.3) erstellten Klasse EventManager wie im folgenden Listing 5.8 registriert werden.
1
2
4
5
6
7
8
9
10
11
private EventManager () {
}
public static EventManager getInstance () {
if ( instance == null ) {
instance = new EventManager ();
g r a p h C h a n g e l i s t e n e r s = new ArrayList < GraphChangeListener >();
...
}
return instance ;
}
Listing 5.8: EventManager
Der Konstruktor dieser Klasse ist private und wird, falls noch keine Instanz davon
existiert, über die statische Methode getInstance() aufgerufen. Ist eine Instanz vorhanden, lässt die Methode direkt darauf zugreifen. Dank dieser Architektur kann aus
jedem Protégé Plugin mit dem Befehl EventManager.getInstance() auf diese eine
Instanz zugegriffen werden. Hier werden die Listeners registriert und eine Methode
zu deren Benachrichtigung zur Verfügung gestellt.
37
5. Implementierung
Die Kombination aus Singleton und Events erlaubt es mit einer weiteren Besonderheit den Zugriff auf die Methoden und somit auch auf die Variablen des OWLPropViz unterhalb OWLPropVizGraphInfo. Hierbei handelt es sich um den Aufbau eines
Events. Die Events werden in diesem Fall in OWLPropViz erstellt und übernehmen
es als Quelle im Konstruktor (Listing 5.9).
public G r a p h C h a n g e E ve n t ( G r a p h C h a n g e E v e n t T y p e type ,
O W L V i z P r o p e r t y V i e w view ) {
this . type = type ;
this . view = view ;
}
1
2
3
4
5
public G r a p h C h a n g e E ve n t ( G r a p h C h a n g e E v e n t T y p e type ,
O W L V i z P r o p e r t y V i e w view ,
String propertyName ) {
this . type = type ;
this . view = view ;
this . propertyName = propertyName ;
}
7
8
9
10
11
12
13
Listing 5.9: Konstruktoren des GraphChangeEvents
Wenn nun ein Event mit der Methode
1
2
EventManager . f i r e G r a p h C h a n g e E v e n t ( G r a p h C h a n g e E v e n t T y p e type ,
O W L V i z P r o p e r t y V i e w view )
Listing 5.10: Informieren der Listeners über ein Event
im OWLPropViz ausgelöst wird, wird die aktuelle OWLVizPropertyView-Instanz dem
Listener übergeben, welcher sich wiederum im OWLPropVizGraphInfo befindet.
Die Typen der Events werden in der Enumeration GraphChangeEventType definiert. Zu diesem Zeitpunkt sind zwei Eventtypen wichtig: INITIALIZE_GRAPH_ITEMS teilt mit, dass der komplette Graph initialisiert werden soll und SHOWN_ITEMS_CHANGED ist eine Benachrichtigung darüber, dass die aktuelle Anzeige im
Graphen geändert ist.
Damit ist das grundlegende Problem der internen Kommunikation beseitigt und
man kann sich auf die Strukturen zum Zählen der Elemente konzentrieren. Zur Speicherung der Gesamtzahl von Graphelementen wird die Klasse DisplayManager benutzt. Dort befinden sich zwei Variablen: allEdges und allNodes, wo alle Knoten
und Kanten einer Ontologie beim automatischen Laden gespeichert werden (Kap.
5.2.3). Gleich nach deren Initialisierung wird ein INITIALIZE_GRAPH_ITEMS
Event rausgeschickt, um mit den in den Variablen vorhandenen Objekten die Gesamtzahl der Elemente in der Ontologie zu bestimmen. Bei allen späteren Aktualisierungen des Graphen, die durch die Hide und Show Befehle stattfinden, wird ein
SHOWN_ITEMS_CHANGED Event ausgelöst und die aktuell angezeigten Elemente, welche direkt aus dem Graphen wie im folgenden Listing 5.11 ausgelesen werden,
werden gezählt.
1
2
3
4
38
view . g e t P r o p e r t y G r a p h C o m p o n e n t ()
. g e t P r o p e r t y G r a p h V i e w (). getGraph (). getEdges ();
view . g e t P r o p e r t y G r a p h C o m p o n e n t ()
. g e t P r o p e r t y G r a p h V i e w (). getGraph (). getNodes ();
5.2. Realisierung der gestellten Aufgaben
Listing 5.11: Methoden des Zugriffs auf die sichtbaren Kanten und Knoten
Die Zählprozesse finden in dem Plugin OWLPropVizGraphInfo statt. OWLPropVizGraphInfoViewComponent ist die Eingangsklasse des Plugins, die in erster Linie
für die Anzeige bereits ausgerechneter Daten zuständig ist. Um einen Zugriff auf
standardisierte Darstellungsmöglichkeiten zu gewährleisten, findet die Visualisierung
in einer HTMLPane statt. Erstellt wird diese durch Protégés ComponentFactory. Des
Weiteren sind in der Klasse Methoden zur Generierung von HTML-Codes aus den
Statistiken vorhanden, sowie ein Listener, der auf alle Arten von Events reagiert, die
eine Änderung der Graphenvisualisierung mit sich bringen. Tritt solch ein Event ein,
werden zunächst die Statistiken intern erneuert und dann wird ein neuer HTML-Code
daraus generiert.
Die Klasse GraphItemsStatistics beinhaltet alle Visualisierungsstatistiken des Graphen. Zum Zählen werden die Hilfsklassen EdgeCounter und NodesCounter benutzt
und innerhalt dieser Klasse gespeichert.
1
2
3
4
5
6
7
8
9
public class G r a p h I t e m s S t a t i s t i c s {
...
private List < EdgeCounter > edgeCounters ;
private NodesCounter nodesCounter ;
...
public void i n i t i a l i z e G r a p h I t e m s () {...}
public void u p d a t e S h o w nI t e m s () {...}
...
}
Listing 5.12: Aufbau der Klasse GraphItemsStatistics
Für jeden einzelnen Kantentyp im Graphen ist ein eigener EdgeCounter zuständig, während die Klasse NodesCounter selbständig alle drei möglichen Knotentypen
durchläuft: normale Klassen, äquivalente Klassen und Individuen. Die beiden damit
arbeitenden Methoden, die den Zählvorgang durchlaufen stehen ebenfalls in dieser
Klasse zur Verfügung: die eine wird bei der Initialisierung des Graphen aufgerufen,
die andere bei jeder Erneuerung des Graphen. Der Unterschied liegt nur darin, dass
die Initialisierungsmethode einmalig alle Objekte des kompletten Graphen durchläuft
und die Erneuerungsmethoden nur die visualisierten. Das Zählprinzip ist aber das
gleiche. Erst werden die Kanten gezählt, indem für jede neue Kante in der Menge
aller Kanten ein neuer EdgeCounter angelegt wird, welcher den Namen und die aktuellen Zahlen enthält. Kommt eine Kante in der Menge mehr als einmal vor, werden
lediglich die Zahlen erhöht. In dem NodesCounter wird nur überprüft um welchen
Knotentyp es sich handelt und die entsprechenden Zahlen werden erhöht.
5.2.6. Wiederherstellung und Erweiterung der Show/Hide
Funktionen
OWLViz besitzt eine Möglichkeit zur Anzeige, welche zwar in OWLPropViz übernommen wurde, aber nicht funktionsfähig ist. Beim Testen dieser Funktionen wurden
zahlreiche Bugs sichtbar. Das Modell der Abfrage von Eltern- und Kinderknoten ist
ebenfalls veraltet und ist nicht in der Lage die Property-Kanten zu traversieren. Diese
39
5. Implementierung
Aspekte waren ausschlaggebend für die Entscheidung, ein neues Modell zu entwerfen.
Zunächst wurden aber Anpassungen an dem Benutzerinterface übernommen.
Die alten Buttons von OWLViz befanden sich in der Toolbar. Es waren also Befehle,
die auf die Auswahl in dem Hierarchiebaum bezogen waren. Dementsprechend wurden auch nur die „is-a“ Kinder und Eltern mit Hilfe des Hierarchiebaums ermittelt.
Das Modell arbeitet mit den Graphenelementen. Das bedeutet, dass der Benutzer innerhalb des Graphen auch die Visualisierungsbefehle aufrufen soll. Diese Möglichkeit
bietet ein Kontextmenü. Wenn auf dem Knoten mit der rechten Maustaste geklickt
wird, erscheint ein Kontextmenü, wo der Benutzer bequem seine Auswahl treffen
kann.
Um ein Kontextmenü zu realisieren, ist zusätzlich die Klasse PropVizPopupMenu
angelegt, die das normale Java-Kontextmenü JPopupMenu erweitert. Hier befindet
sich nich nur der Quelltext zu der graphischen Anzeige des Menüs, sonder auch alle
Funktionalitäten.
Das Modell: Das neue Modell zur Ermittlung und Anzeige von Knoten basiert
auf den in OWLViz bereits vorhandenen Edges. Eine Edge besteht aus dem Startknoten, Zielknoten und dem Namen der Kante. Wenn ein Graph generiert wird, befindet
sich im VisualisedObjectManager eine Liste mit allen aktuell angezeigten Edges. Um
eine vollständige Liste mit allen Elementen, die in der Ontologie vorhanden sind, zu
erhalten, wird noch vor dem automatischen Laden (Kap. 5.2.3) unsichtbar für den
Benutzer eine vollständiger Graph generiert und die Liste mit allen seinen Objekten
im DisplayManager gespeichert. Falls der Benutzer sich später für andere Kanten
entscheiden sollte, tut er das indem er den Befehl „Show Selected Properties“ betätigt. Dabei entsteht ein neuer kompletter Graph mit den Kanten, die der Benutzer
angezeigt haben will. Diese Einstellungen werden beim Ausführen des Befehls abgefangen und die Liste mit den Kanten, an den der Benutzer interessiert ist, wird
ebenfalls im DisplayManager abgespeichert. Zusätzlich speichert der DisplayManager
immer den aktuell angeklickten Knoten.
1
2
3
view . get DispMana ger (). setAllEdges (
view . g e t P r o p e r t y G r a p h C o m p o n e n t (). g e t P r o p e r t y G r a p h V i e w ()
. getGraph (). getEdges ());
Listing 5.13: Speichern der vom Benutzer ausgewählten Kanten
Damit kann jederzeit auf diese Liste zugegriffen werden und die Eltern- und Kinderknoten gemäß definierter Einstellungen ermittelt werden.
Das Kontextmenü: Das Kontextmenü für diese Aufgabe besteht aus zwei Submenüs: Show und Hide. Das folgende Bild zeigt das expandierte Menü „Show“.
Abbildung 5.1.: Kontextmenu Show
40
5.2. Realisierung der gestellten Aufgaben
Der Eintrag „Show > Domain“ soll die Elternknoten anzeigen. An diesem Eintrag
wird beispielhaft gezeigt, wie das neue Modell funktioniert. Alle anderen Menüeinträge sind nur leicht modifiziert, funktionieren aber nach demselben Muster. Jeder
Menüeintrag hat einen ActionListener, wo in der Methode actionPerformed() die
auszuführenden Aktionen stehen. Folgendes Listing zeigt den Quelltext, welcher die
Implementierung der Funktion „Show > Domain“ beinhaltet.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void a ct io nP er f or me d ( ActionEvent e ) {
for ( P r o p e r t y G r a p h C o m p o n e n t graphComp : view . g e t P r o p e r t y G r a p h C o m p o n e n t s ()) {
String selNode = view . g etDispMa nager (). g e t S e l e c t e d N o d e N a m e ();
Set < Object > entObj = new HashSet < Object >();
Edge [] edges = view . get DispMana ger (). getAllEdges ();
for ( Edge edge : edges ) {
if ( edge . getTailNode (). getUserObject (). toString (). equals ( selNode )) {
entObj . add ( edge . getHeadNode (). getUserObject ());
}
}
Object [] entities = entObj . toArray ();
graphComp . g e t V i s u a l i s e d O b j e c t M a n a g e r (). showObjects ( entities );
}
EventManager . getInstance (). f i r e G r a p h C h a n g e E v e n t (
G r a p h C h a n g e E v e n t T y p e . SHOWN_ITEMS_CHANGED , view );
}
Listing 5.14: Implementierung von Show > Domain
Der ausgewählte Knoten wird vermerkt. Es wird eine neue Menge entObj erstellt,
die am Ende die ermittelten Elternknoten beinhaltet. Variable edges beinhaltet alle
Edge-Objekte des zuletzt spezifizierten vollständigen Graphen. Danach wird eine
for-Schleife durchlaufen, in der überprüft wird, ob der aktuell ausgewählte Knoten
in einer Kante als Elternknoten vermerkt ist. Ist das der Fall, wird das Kind in die
Menge entObj hinzugefügt. Da die Methode showObjects() der Klasse VisualisedObjectManager ein Object-Array als Eingabe benötigt, wird die Menge entObj in
ein solches umgewandelt und die Objekte werden im Graphen sichtbar. Anschließend
findet eine Benachrichtigung aller Listeners, die auf die Anzeigeänderungen reagieren,
mit dem entsprechenden Event statt.
Dabei muss angemerkt werden, dass auf den ersten Blick vermutet werden könnte, dass fälschlicherweise die Methode edge.getTailNode() statt edge.getHeadlNode()
benutzt wird. Das ist jedoch richtig, weil es aus Sicht des Benutzers bei dem ausgewählten Knoten zwar um den Elternknoten (Anfangsknoten) handelt, in dem Graphmodell ist es aber der Endknoten, auf welchen der Pfeil zeigt.
Der Eintrag „Show > Domain recursively“ ist dafür zuständig, den restlichen Baum
von dem ausgewählten Knoten aus aufzuspannen. Dies ist eine einfache Erweiterung der vorherigen Funktion. Für jedes Element der Ergebnismenge wird die gleiche
Methode nochmal aufgerufen. Hier ist das noch etwas modifiziert. Es wird erst die
Ergebnismenge berechnet und danach die Methode showObjects() aufgerufen.
Der Eintrag „Show > Range“ zeigt die übergeordneten Knoten. Die Implementierung ist fast identisch zu der von „Show > Domain“, nur dass es hierbei darauf
überprüft wird, ob der ausgewählte Knoten als Kind vermerkt ist.
Das Menü „Hide“ enthält ebenfalls drei Unterpunkte, welche auf dem folgenden
Bild zu sehen sind.
41
5. Implementierung
Abbildung 5.2.: Kontextmenu Hide
Der Unterpunkt „Hide > Domain“ ist für das Verstecken aller Kinderknoten zuständig. Dabei wird dieselbe Methode zur Bestimmung der Ergebnismenge benutzt,
wie bei dem Eintrag „Show > Domain recursively“. Für diesen Zweck wurde diese
Methode in die Klasse DisplayManager unter dem Namen getDomainRecursively ausgelagert. Nach deren Ausführung erfolgt das Verstecken der Objekte mit dem Befehl
hideObjects(...) der Klasse VisualisedObjectManager.
Der Eintrag „Hide > Node“ versteckt den aktuellen Knoten durch Aufruf der
Methode hideObject(...).
„Hide > Range“ ermittelt die übergeordneten Knoten, wie der Eintrag „Show >
Range“, ruft aber am Ende die hideObjects(...) Methode auf.
5.2.7. Hinzufügen und Löschen einzelner Knoten
Diese Funktionen sind ebenfalls in dem Kontextmenü auffindbar. Zwar kann man die
Knöpfe mit denselben Funktionen im gleichen WorkspaceTab finden, intuitiver ist es
aber von dort die Befehle aufzurufen, wo diese auch gebraucht werden - in diesem
Fall direkt am Knoten.
Die Hinzufügeoperationen „Add Subclass“ und „Add Sibling“ sind fast identisch.
Der Unterschied liegt bei der Angabe der Oberklasse. Die im Graphen gemachten
Änderungen müssen entsprechende auch in der Ontologie durchgeführt werden. Implementiert wird dieses genau umgekehrt. Zunächst werden die Änderungen in der
Ontologie vorgenommen. Protégé besitzt bereits die Methoden zum Hinzufügen und
Löschen von Knoten, welche in der Implementierung exakt übernommen werden. Dabei wird ein neues Unterklasseaxiom (OWLSubClassAxiom) wie in der Zeile 5 des
Listings 5.15 erstellt und die Änderungen im OWLModelManager durchgeführt. Jetzt
muss die Visualisierung stattfinden um dem Benutzer ein sofortiges Feedback über
den Erfolg seiner Aktion zu ermöglichen. Das in die Ontologie hinzugefügte Axiom
wird zu der Menge der aktuell im Graphen angezeigten Axiomen hinzugefügt (Zeile
15) und visualisiert.
1
2
3
4
5
6
7
42
// Hinzufügen in die Ontologie . cls = ausgewählte klasse
// creationSet . getOWLEntity () = vom Benutzer eingegebener Name
List < OWLOntologyChange > changes = new ArrayList < OWLOntologyChange >();
OWLD ataFacto ry df = owlEditorKit . g et Mo d el Ma na g er (). g e t O W L D a t a F a c t o r y ();
O W L S u b C l a s s A xi o m ax = df . g e t O W L S u b C l a s s A x i o m ( creationSet . getOWLEntity () , cls );
changes . add ( new AddAxiom ( owlEditorKit . ge tM o de lM an ag e r ()
. g e t A c t i v e O n t o l o g y () , ax ));
5.2. Realisierung der gestellten Aufgaben
8
9
10
11
12
13
14
15
16
17
18
19
owlEditorKit . g et M od el Ma na g er (). applyChanges ( changes );
// Vi sualisie rung
Set < OWLAxiom > axioms = view . getD ispManag er (). g e t C u r r e n t A x i o m s ();
axioms . addAll ( owlEditorKit . g et Mo de l Ma na ge r (). g e t A c t i v e O n t o l o g y (). getAxioms (
creationSet . getOWLEntity ()));
view . a d d A n d C l e a r A x i o m s ( axioms );
HashSet < OWLClass > obj = new HashSet < OWLClass >();
obj . add ( creationSet . getOWLEntity ());
view . g e t P r o p e r t y G r a p h C o m p o n e n t (). g e t V i s u a l i s e d O b j e c t M a n a g e r ()
. showObjects ( obj . toArray ());
EventManager . getInstance (). f i r e G r a p h C h a n g e E v e n t (
G r a p h C h a n g e E v e n t T y p e . CLASS_ADDED , view );
Listing 5.15: Hinzufügen einer Klasse und deren Visualisierung
Zum Schluss findet eine Benachrichtigung von OWLPropVizGraphInfo mit einem
speziell für diesen Zweck definierten Event CLASS_ADDED statt, welche eine Erhöhung der sowohl visualisierten als auch der Zahl der gesamten Klassen vornimmt.
Der Löschvorgang wird ähnlich implementiert, mit einem Unterschied, dass bei dem
Knotentyp unterschieden wird. Die Methoden zum Löschen einer Klasse eigenen sich
nicht zum Löschen eines Individuums. Der zweite Fall wird mit Hilfe der OWL-API
behandelt, da Protégé für diesen Zweck eine etwas veraltete Vorgehensweise benutzt.
Listing 5.16 zeigt, wie ein Individuum aus einer Ontologie gelöscht werden kann. Es
wird ein OWLEntityRemover für die aktive Ontologie erstellt. Das zu löschende Individuum akzeptiert diesen (siehe Kap. 2.5.1) und wird dabei gelöscht. Diese Änderung
wird im ModelManager registriert. Damit ist der Löschvorgang abgeschlossen.
1
2
3
4
5
6
O W L E n t i t y R e mo v e r remover = new O W L E n t i t y R em o v e r (
owlEditorKit . g et M od el Ma na g er (). g e t O W L O n t o l o g y M a n a g e r () ,
owlEditorKit . g et M od el Ma na g er (). getOntologies ());
OWLIndividual indi = ( OWLIndividual ) selectedNode . getUserObject ();
indi . accept ( remover );
owlEditorKit . g et M od el Ma na g er (). applyChanges ( remover . getChanges ());
Listing 5.16: Löschen eines Individuums aus einer Ontologie
Im Anschluß findet eine Benachrichtigung der Listeners mit den entsrepchenden
Eregnissen statt.
5.2.8. Hinzufügen von Kanten
Das schnelle Hinzufügen von Kanten innerhalb eines Graphen ist ein komplett neues
Konzept für Protégé. In Wirklichkeit wird dabei eine Beziehung der Sorte „Subjekt
- Prädikat - Objekt“ erstellt. Um dieses durchführen zu können ist in Protégé eine
etwas mühselige Reihenfolge von Operationen vorgesehen und ist für einen unerfahrenen Benutzer eine Herausforderung sie herauszufinden. Der neue Interaktionsprozess, bereits in dem Architekturkapitel vorgestellt, erfordert dafür wenige Mausklicks
innerhalb des Graphen und kann leicht durch Ausprobieren erlernt werden. Im Folgenden wird erst auf die Implementierung der Änderungen im Graphen und in der
Ontologie eingegangen, während die Umsetzung der Benutzerinteraktion getrennt
betrachtet wird.
43
5. Implementierung
Die entsprechende Funktionalität ist wieder in dem Kontextmenü untergebracht.
Erst muss festgelegt werden von welchem Knoten aus welche Kanten möglich sind.
Es gibt drei in Frage kommende Beziehungen:
1. Class Restriction ist eine Beziehung zwischen zwei Klassen der Form: „Klasse1“ - „property some“ - „Klasse2“, beispielsweise „Lehrer“ - „lehrt some“ „Schulfach“.
2. Class Assertion ist eine Zuweisung eines Individuums an eine Oberklasse,
also eine „instance-of“ Kante: „HerrMustermann“ - „instance-of“ - „Lehrer“.
3. Propery Assertion ist eine Beziehung zwischen zwei Individuen, ähnlich wie
bei einer Class Restriction: „HerrMustermann“ - „lehrt“ - „Mathe“.
Um diese Beziehungen in der Ontologie erstellen zu können werden Methoden
benötigt, die es erlauben, unter Angabe der drei Parameter direkt die gewünschte
Beziehung in die Ontologie einzutragen. Zur Lösung dieser Aufgabe steht die OWLAPI zur Verfügung.
Erstellen einer Class Restriction: in dem folgenden Listing wird gezeigt wie
unter Benutzung von OWL-API das gewünschte Axiom erstellt werden kann. Die Variablen dataFactory und man (ModelManager) sind ebenfalls Konstrukte von OWLAPI, da aber Protégé darauf basiert sind diese hier vorhanden und können ohne
Umwege benutzt werden. subjectNode, propertyName und objectNode speichern die
benutzerdefinierten Angaben.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Erstellen
...
O W L O b j e c t P r o p e r t y property = dataFactory
. g e t O W L O b j e c t P r o p e r t y ( URI . create ( base + " # " + propertyName ));
OWLClass object = ( OWLClass ) objectNode . getUserObject ();
OWLD escripti on desc = dataFactory . g e t O W L O b j e c t S o m e R e s t r i c t i o n (
property , object );
OWLClass subject = ( OWLClass ) subjectNode . getUserObject ();
O W L S u b C l a s s A xi o m ax = dataFactory . g e t O W L S u b C l a s s A x i o m ( subject , desc );
AddAxiom addAx = new AddAxiom ( ont , ax );
man . applyChange ( addAx );
// Visualisieren
...
Set < OWLAxiom > s = extr . g e t P r o p e r t y R e l a t e d A x i o m s ( property , ont );
view . get DispMana ger (). ge t C u r r e n t A x i o m s (). addAll (
extr . g e t O W L O b j e c t P r o p e r t y W r a p p e r A x i o m s ( s ));
...
Listing 5.17: Erstellen und Anzeigen einer Class Restriction
Es wird eine Property erstellt, die Subjekt- und Objektklasse definiert und ein Axiom
daraus erstellt. Durch den Befehl applyChange(...) werden die Änderungen wirksam
und müssen nun auch im Graphen angezeigt werden. Das erstellte Axiom wird dafür
zu der Menge der bereits visualisierten hinzugefügt und der Graph wird neu gezeichnet.
Erstellen einer Class Assertion verläuft ähnlich wie das Erstellen einer Class
Restriction mit dem Unterschied, dass ein anderer Axiomtyp erstellt wird (OWLClassAssertionAxiom). Der Visualisierungsprozess bleibt der gleiche.
44
5.2. Realisierung der gestellten Aufgaben
1
2
3
4
5
6
7
OWLIndividual indi = dataFactory . g e t O W L I n d i v i d ua l ( URI . create ( base
+ " # " + subjectNode . getUserObject (). toString ()));
OWLDe scriptio n desc = ( OWLClass ) objectNode . getUserObject ();
O W L C l a s s A s s e r t i o n A x i o m assertion = dataFactory
. g e t O W L C l a s s A s s e r t i o n A x i o m ( indi , desc );
AddAxiom addAx iomChang e = new AddAxiom ( ont , assertion );
man . applyChange ( addAxi omChange );
Listing 5.18: Erstellen einer Class Assertion
Erstellen einer Property Assertion: auch hier treten keine Besonderheiten auf
und die Assertion wird in der Zeilen 2-3 des folgenden Listings erstellt.
1
2
3
4
...
O W L O b j e c t P r o p e r t y A s s e r t i o n A x i o m assertion = dataFactory
. g e t O W L O b j e c t P r o p e r t y A s s e r t i o n A x i o m ( subject , property , object );
...
Listing 5.19: Erstellen einer Property Assertion
Alle oben beschriebenen Methoden befinden sich in der Klasse EdgeCreator, welche eine Implementierung von CreateEdgeEventListener ist, und werden erst nach
dem Abschluss einer vorgesehenen Benutzerinteraktionskette ausgeführt. Diese fängt
in dem Kontextmenü an. Beim Anklicken des Knotens wird zunächst unterschieden um welchen Knoten es sich handelt, um nur die Menüpunkte für das Erstellen
der dafür in Frage kommenden Kanten anzuzeigen. Der NodeClickedListener befindet sich in der Hauptklasse OWLVizPropertyView und ist für diese Unterscheidung
zuständig. Ist ein Menüeintrag ausgewählt, wird der aktuelle Knoten als Subjekt
gespeichert und eine boolesche Variable isPreparing auf true gesetzt. Das aktiviert
den entsprechenden NodeClickedListener in der OWLPropVizView, welcher ab jetzt
auf seinen Einsatz wartet. Auch der Mauscursor wird zu einem Fadenkreuz geändert, damit der Benutzer weiß, dass er nun einen Zielknoten auswählen soll. Ist ein
neuer Knoten ausgewählt, wird er als Objekt gespeichert und der Cursor in seinen
normalen Zustand gesetzt. Die Klasse EdgeCreator wird mittels eines Events, z.B.
ADD_RESTRICTION, darüber informiert, dass der Benutzer die beiden Knoten,
zwischen welchen eine Kante erstellt werden soll, gewählt hat. Der EdgeCreator startet nun ein Dialog, in welchem entweder eine vorhandene Property ausgewählt oder
eine neue angegeben werden kann. Der Dialog wird mit Hilfe der UIHelper-Klasse
aufgerufen. Das erlaubt eine Unterbrechung beim Ausführen des Programmcodes, bis
der Benutzer seine Auswahl getroffen hat.
1
2
3
UIHelper helper = view . getHelper ();
helper . showDialog ( " Select Object Properties " ,
g e t S e l e c t i o n P a n e l ());
Listing 5.20: Aufruf des UIHelpers
Die Methode getSelectionPanel() gibt ein JPanel zurück, die dem Benutzer die
vorhandenen Properties anzeigt und ein Feld zur Erstellung einer neuen gibt. Der
Quelltext der Methode kann dem Anhang B.21 (Zeile 175) entnommen werden.
Sind diese Aktionen erledigt, kann, wie am Anfang des Abschnitts beschrieben, die
Erstellung der Kanten durchgeführt werden. Zu allerletzt wird OWLVizPropertyGra-
45
5. Implementierung
phInfo darüber informiert, dass eine Kante hinzugefügt wurde und die entsprechenden
Erhöhungen der Zahlen finden statt.
5.3. Realisierung zusätzlicher Aufgaben
5.3.1. Anzeige von versteckten Eltern- und Kinderknoten
In dem OWLViz Plugin gibt es eine für die Benutzerinteraktion sehr wichtige Funktion, welche graphisch eine Auskunft darüber gibt, ob ein Knoten versteckte Kinder
oder Eltern hat. Dieses ist in Form kleiner Pfeile realisiert und ist auf dem folgenden
Bild zu sehen.
Abbildung 5.3.: Die kleinen Pfeile
Das bedeutet, dass die Klasse „Lecture“ gerade sowohl nicht angezeigte übergeordnete als auch untergeordnete Knoten hat. Wenn diese angezeigt werden sollen,
kann der Benutzer die bereits implementierten Kontextmenüeinträge nehmen. Dann
sollen z.B. nach der Aktion „Show > Domain“ die Kinderknoten angezeigt werden
und das rechte Pfeilchen verschwinden. Damit wird signalisiert, dass alle Kinderelemente der Klasse zurzeit in dem Graphen sichtbar sind. Diese Funktionalität ist
im OWLPropViz verloren gegangen und muss daher unbedingt zu Verbesserung der
Benutzerinteraktion wiederhergestellt werden.
Zunächst wurde die Architektur dieser Funktion untersucht. Die Pfeile werden in
der Klasse OWLClsNodeRenderer des OWLViz Plugins gezeichnet, welche im Rahmen der parallelen Bachelorarbeit eine eigene Adapterklasse bekommen hat und für
die Änderungen im OWLPropViz zugänglich ist. Dieser Prozess findet parallel zu
dem Zeichnen der Knoten statt. Folglich können nicht die bisher benutzen Methoden
wiederverwendet werden, da das neue Modell im DisplayManager die visualisierten
und versteckten Knoten erst nach deren Visualisierung abfragen kann. Es muss ein
Prozess zur Markierung der Sichtbarkeit der Knoten noch vor deren Zeichnung stattfinden und wird wie folgt realisiert.
In der Klasse DisplayManager wird eine Map angelegt, in welcher jedem Knoten
der Ontologie ein Sichtbarkeitswert zugewiesen wird. Um mit dem Standard-JavaDatentyp Map arbeiten zu können, wird eine Hilfsklasse HideState erstellt, welche
nur einen booleschen Wert speichert und Methoden zu dessen Setzung und Abfrage
zur Verfügung stellt. Map<Object, HideState> visibilityMap ist nun die benutzte Sichtbarkeitsmap. Bei der Plugininitialisierung werden alle Knoten und jeweils ein
Sichtbarkeitszustand „versteckt“ abgespeichert. Die Methoden zur Abfrage, Erneuerung und Zurücksetzung der visibilityMap, sowie Abfrage der Zahl der versteckten
Kinder- bzw. Elternknoten, sind ebenfalls im DisplayManager definiert. Das ist die
Basis mit der alle beteiligten Klassen nun arbeiten sollen.
46
5.3. Realisierung zusätzlicher Aufgaben
In der Klasse OWLClsNodeRenderer wird die alte getChildrenHiddenCount(node)
Methode durch die neue aus dem DisplayManager ersetzt. Dazu muss erst die vorhandene Struktur erweitert werden. Die aktive Instanz der OWLVizPropertyView,
von welcher der Zugriff auf DisplayManager möglich ist, wird über die Konstruktoren der beteiligten Klassen an den DefaultPropertyController weitergegeben, welcher
wiederum von OWLClsNodeRenderer aufgerufen werden kann.
1
controller . getView (). getDis pManager (). g e t C h i l d r e n H i d d e n C o u n t ( userObject )
Listing 5.21: Zugriff auf die neue getChildrenCount Methode
Die Funktionsweise folgende: über die Menge aller Kanten wird nach Kinderknoten gesucht und deren Sichtbarkeitsstatus in der visibilityMap abgefragt. Befinden
sich darunter versteckte Knoten, werden diese gezählt. Ählich funktioniert auch die
Methode getParentsHiddenCount(node).
Der entscheidende Punkt, der die Korrektheit der zurückgegebenen Werte gewährleistet, ist das rechtzeitige Updaten der Sichtbarkeitsmap. Die Methode zum Zeichnen
der Knoten wird in der Klasse DefaultPropertyGraphView aufgerufen. Folglich muss
direkt davor das Update erfolgen. Mit der Methode getNodes() dieser Klasse kann
über einen Iterator auf die zu zeichnenden Knoten zugegriffen werden. Die visibilityMap wird zurückgesetzt und die zu zeichnenden Knoten werden mit dem sichtbaren
Status vermerkt (Listing 5.22). Damit liefern die danach für jeden Knoten aufgerufenen Methoden getParentsHiddenCount(node) und getChildrenHiddenCount(node) die
richtigen Werte und die Pfeilen werden nur dann angezeigt, wenn auch tatsächlich
versteckte Objekte existieren.
1
2
3
4
5
6
7
8
Iterator nodeIt = getNodes ();
controller . getView (). getDis pManager (). r e s e t V i s i b i l i t y M a p ( nodeIt );
nodeIt = getNodes ();
while ( nodeIt . hasNext ()) {
Node node = ( Node ) nodeIt . next ();
controller . getView (). getDis pManager (). s e t O b j e c t H i d d e n S t a t e (
node . getUserObject () , false );
}
Listing 5.22: Aktualisieren der visibilityMap
5.3.2. Bugfix: DotParser Exception
Durch die zahlreichen Veränderungen der beiden parallel laufenden Bachelorarbeiten
entstand ein Bug, welcher ein sehr störendes Verhalten aufwies. In etwa 30% aller
Fälle, bei denen man Änderungen an dem Graphen großer Ontologien (in unserem
Fall handelt es sich um die pizza.owl) vorgenommen hat, wie z.B. neue Properties
anzeigen oder die Kinderknoten verstecken, kam es zu einem Fehler beim Einlesen der
temporären Datei, die von GraphViz erstellt wurde. Trat dieser Fehler zur Laufzeit
auf, wurde kein neuer Graph generiert, während beim Laden des Plugins der Fehler
zum Absturz der ganzen Applikation führte. Folgender Fehlertext war in der Konsole
zu lesen:
47
5. Implementierung
1
2
3
4
Error logged
java . lang . N u m b e r F o r m a t E x c e p t i o n : For input string : "4110.8"
...
at {...}( D o t G r a p h L a y o u t E n g i n e W r a p p e r . java :134)
Listing 5.23: Konsolenausgabe der Parser Exception
Dabei handelt es sich um folgende Zeile der Klasse DotGraphLayoutEngineWrapper:
DotParser.parse(paramSetter, is);
is ist der Input-Stream der temporären Datei, die von GraphViz generiert wird. Wenn
man sich die Datei genau anschaut, findet man sofort die Zeile, in der die Dimensionen
des Graphen angegeben werden und die Zahl 4110.8 enthalten ist. Diese Zahl ändert
sich bei jeder neuen Ausgabe von GraphViz, also muss man generell das Muster
beseitigen, in diesem Fall muss der Punkt und die danach stehenden Zahlen in der
Zeile
1
graph [ bb ="0 ,0 ,1986 ,4110.8"];
Listing 5.24: Die für den Fehler zuständige Zeile der GraphViz Datei
gelöscht werden. Diese Aufgabe wird durch das Einfügen des folgenden Java Codes
gelöst.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* PREPARSING */
File preparsedFile = File . c reateTemp File ( " OWLVizScratch " , null );
Buff eredRead er in = new Buff eredRead er ( new I n p u t S t r e a m R e a d e r ( is ));
Buff eredWrit er out = new Buff eredWrit er ( new FileWriter ( preparsedFile ));
String s = " " ;
while (( s = in . readLine ())!= null ){
if ( s . contains ( " graph [ bb =\" " ) && s . contains ( " . " )) {
s = s . substring (0 , s . indexOf ( " . " )) + " \"]; " ;
out . write ( s );
} else {
out . write ( s );
out . newLine ();
}
}
in . close ();
out . close ();
is = new F il eI np u tS tr ea m ( preparsedFile );
/* CONTINUE AS USUAL */
D o t P a r a m e t e r S e t t e r paramSetter = new D o t P a r a m e t e r S e t t e r ();
paramSetter . setGraph ( g );
DotParser . parse ( paramSetter , is );
Listing 5.25: Preparsen der GraphViz Datei
Bevor der interne Parser von Protégé die Datei bearbeitet, wird die Datei nach genannten Zeile (siehe Listing 5.24) durchsucht, die die Exception auslöst. Diese wird
muss dann beseitigt werden. Dafür wird eine neue temporäre Datei preparsedFile
erstellt, in welche der Inhalt der ursprünglichen Datei file Zeile für Zeile kopiert
wird. Trifft man auf die Zeile, die mit graph [bb=" Anfängt und den unerwünschten
Punkt enthält, wird der Teil des Strings gelöscht, der Zwischen den Punkt und der
beendenden Charaktersequenz "]; liegt (siehe Zeile 8 des Listings 5.25). Die somit
vorgeparste Datei wird in der Zeile 21 an den DotParser von Protégé gegeben und
das Parsen verursacht entsprechend keine Fehler mehr.
48
6 Evaluation
6.1. Funktionstest
Die neuen Funktionalitäten des Programms wurden eingeführt um die Benutzerinteraktion zu verbessern. In den folgenden Testfällen wird ein Szenario durchgegangen,
bei welchem der Benutzer eine kleine Ontologie öffnet, diese kennenlernt und Änderungen vornimmt.
Voraussetzung für die folgenden Testfälle ist das bereits in Protégé installierte
OWLPropViz-Plugin.
6.1.1. Installation des Plugins
Dieser Testfall wird gesondert betrachtet und dient primär dazu, dem Benutzer die
Installationsschritte bis zum Ausführen des Plugins zu veranschaulichen.
Ausgangszustand: Auf dem Zielrechner müssen eine installierte Distribution von
Protégé, von GraphViz (siehe [Sch09] Kapitel 3.10) und die kompilierte JAR-Datei
des Plugins OWLPropViz vorhanden sein. Diese können der Bachelorarbeit beiliegenden CD entnommen oder aus dem Internet bezogen werden.
Durchzuführende Aktionen: Die JAR-Datei de.tubs.cs.ifis.owlpropviz.jar wird in
das Verzeichnis plugins des Protégé-Ordners kopiert. Protégé wird ausgeführt. Falls
Abbildung 6.1.: Das OWLPropViz-Tab
der OWLPropViz-Tab nicht alle oder keine der drei Komponenten besitzen sollte,
werden diese manuell hinzugefügt. Im Protégé-Menü wird zunächst die View unter dem Menüpunkt „View > Class views > OWLPropViz“ ausgewählt und in den
49
6. Evaluation
OWLPropViz-Tab platziert. Als nächstes wird die „View > Class views > Asserted
class hierarchy“ links in den Tab eingefügt. Zum Schluß wird „View > Ontology views
> OWLPropViz Graphinfo“ unten links in den OWLPropViz-Tab hinzugefügt.
Erwartetes Ergebnis: Der OWLPropViz-Tab sieht wie auf der Abbildung 6.1 aus.
Eingetretenes Ergebnis: Das erwartete Ergebnis ist eingetroffen.
Auswertung: Der eigentliche Installationsvorgag des OWLPropViz besteht dank
der Protégé-Struktur nur darin, die JAR-Datei in den Plugin-Ordner zu kopieren
und ist somit innerhalb kürzester Zeit abgeschlossen.
6.1.2. Automatische Grapherstellung und Informationsanzeige
Ausgangszustand: Protégé wird gestartet und die Datei uni.owl wird ausgewählt.
Der Protégé-Workspace ist offen. Die Funktion zur automatischen Grapherstellung
muss aktiviert sein. Wenn das noch nicht der Fall ist, kann das in dem Tab „AutoDisplay“ des „Options“-Menü (Toolbarbutton) im OWLPropViz-Plugin gemacht
werden.
Durchzuführende Aktionen: Anklicken des WorkspaceTabs „OWLPropViz“.
Erwartetes Ergebnis: Dem Benutzer soll ein Ausschnitt der Ontologie automatisch
vorgestellt werden. Informationen über die weiteren Inhalte, deren Gesamtzahl und
die Zahl der visualisierten Objekte sollen angezeigt werden.
Eingetretenes Ergebnis: Gleich nach der Aktivierung des Plugins erscheint das
gewünschte Ergebnis. (Abbildung 6.2)
Auswertung: Auf Grund der Ontologiegröße dieses Testfalls werden in dieser Vorschau sogar die kompletten Klassen der Ontologie angezeigt. Der Benutzer kann sehen
wie die Wurzel der Ontologie aufgebaut ist. In dem Innenfenster OWLPropViz Graphinfo sind die Informationen über alle vorhandenen Objekte angezeigt. Diesen Informationen kann entnommen werden, dass in der Vorschau derzeit keine Individuen
und keine Properties angezeigt werden. Alle in der Ontologie vorhandenen Properties
und deren Anzahl sind ebenfalls aufgelistet. Dieses soll dem Benutzer einen ersten
Überblick verschafft haben. Zur Ausführung der in diesem Testfall gestellten Aufgabe, ist nach dem Laden des Protégé lediglich ein einziger Mausklick notwendig. Die
für die Verschaffung des ersten Eindrucks über die Ontologie benötigten Informationen werden angezeigt. Es musste keine Zeit verloren gehen um die interne Struktur
des Plugins zu verstehen, bevor man an diese Informationen gelangen kann.
6.1.3. Aktivierung der Toolbarbuttons beim Laden des Plugins
Ausgangszustand: Das Plugin WorkspaceTab ist frisch geöffnet und es wurden
noch keine Aktionen durchgeführt.
Durchzuführende Aktionen: Anklicken des Toolbar-Buttons „Show Selected Properties“.
Erwartetes Ergebnis: Der Toolbarbutton lässt sich anklicken bzw. ist aktiv.
Eingetretenes Ergebnis: Der Toolbarbutton ist aktiv.
Auswertung: Der Benutzer muss nicht erst eine zusätzliche Aktion ausführen, um
50
6.1. Funktionstest
die gestellte Aufgabe durchzuführen. Dieser Aspekt spart zwar kaum Zeit, erhöht
aber die Zufriedenheit des Benutzers mit dem Programm, da er nicht immer nach
dem Laden einer neuen Ontologie erst eine Klasse auswählen muss, um einen Graphen
generieren zu können.
Abbildung 6.2.: Ergebnis der Testfälle 2 und 3
6.1.4. Kontextmenü: Funktionen Hide und Show
Ausgangszustand: Die Ontologie „uni.owl“ ist geladen. Ein Graph zur Anzeige aller Beziehungen ist generiert. Der Knoten „University“ ist ausgewählt.
Durchzuführende Aktionen: Aufruf des Kontextmenü auf einem Knoten. Nacheinander werden alle Menüeinträge der Kategorie Show/Hide getestet. Folgende Reihenfolge ist für diesen Test vorgesehen: Hide > Domain, Show > Domain, Show >
Domain Recursively, Hide > Range, Show > Range und Hide > Class.
Erwartetes Ergebnis: Der Testreihenfolge nach: alle untergeordneten Knoten des
ausgewählten Knotens werden versteckt; nur direkte Kinderknoten werden sichtbar;
alle untergeordneten Knoten werden sichtbar; Elternknoten werden ausgeblendet; Elternknoten werden wieder angezeigt; der ausgewählte Knoten wird versteckt. Für jede
dieser Aktion müssen die entsprechenden Zahlen im OWLPropVizGraphInfo ändern.
Auch die Pfeile, die versteckte Kinder- und Elternknoten graphisch anzeigen, müssen
passend angezeigt bzw. ausgeblendet werden
Eingetretenes Ergebnis: Entspricht dem erwarteten Ergebnis.
Auswertung: Die Funktionalitäten zum Anzeigen und Verstecken von Knoten innerhalb des Graphen sind wiederhergestellt und auf die Anzeige von Properties und
Individuen erweitert worden. Der Benutzer kann je nach Bedürfnis einen beliebigen
Graphenausschnitt für sich sichtbar machen, mit dem er dann weiterhin arbeiten will.
6.1.5. Visualisierung der äquivalenten Klassen
Ausgangszustand: Die Ontologie „uni.owl“ ist geladen. Ein Graph, welcher die darin vorhandenen äquivalenten Klassen anzeigt, wird generiert.
51
6. Evaluation
Durchzuführende Aktionen: der Mauszeiger wird über der äquivalenten Klasse
„Professor“ gehalten
Erwartetes Ergebnis: ein Popup mit der Anzeige der Beschreibung dieser äquivalenten Klasse soll auf dem Bildschirm sichtbar sein.
Eingetretenes Ergebnis: der Popup erscheint.
Auswertung: Der Graph wird nicht durch zusätzliche Elemente zur Anzeige der
Abbildung 6.3.: Anzeige der äquivalenten Klasse „Professor“
äquivalenten Klassen vergrößert. Ein bequemer Tooltip mit der Anzeige der Klassenbeschreibung erscheint und wird sofort ausgeblendet, sobald die Maus nicht mehr
in dem Anzeigebereich der Klasse liegt. Da äquivalente Klassen im Graphen farblich
markiert sind, weiß der Benutzer immer Bescheid, ob es sich bei dem ausgewählten
Knoten um eine äquivalente Klasse handelt und falls er detaillierte Informationen
darüber haben will, muss er nicht mehr das WorkspaceTab von OWLPropViz verlassen.
6.1.6. Doppelklick auf einem Knoten
Ausgangszustand: Ein Graph mit angezeigten Individuen, normalen und äquivalenten Klassen ist angezeigt.
Durchzuführende Aktionen: Ein Doppelklick mit der Maus auf einem Individuum, einer normalen und einer äquivalenten Klasse.
Erwartetes Ergebnis: Der passende WorkspaceTab öffnet sich, das entsprechende
Knotenobjekt ist ausgewählt
Eingetretenes Ergebnis: Sowohl im Falle einer normalen, als auch einer äquivalenten Klasse öffnet sich der WorkspaceTab „Classes“ und die Klasse ist im Baum
ausgewählt. Bei einem Individuum wird zwar der richtige Tab aufgerufen, aber es
erfolgt keine Auswahl.
Auswertung: Ein Doppelklick assoziiert sich oft mit einer detaillierten Anzeige der
Auswahl oder ein Editorfenster wird erwartet. In diesem Fall entspricht das Ergebnis
auch den Erwartungen. Nach einem Doppelklick auf der ausgewählten Klasse kann
der Benutzer in dem sich automatisch öffnenden Tab die einzelnen Details über die
Klasse nachschauen und ggfs. diese auch ändern. Das auftauchende Problem, dass
das angeklickte Individuum nicht in der Hierarchie ausgewählt ist, liegt daran, dass
diese Funktionalität in Protégé nicht vorhanden ist.
52
6.1. Funktionstest
6.1.7. Kontextmenu: Hinzufüge- und Löschoperationen
Ausgangszustand: Ein beliebiger Graph ist generiert mit mindestens einem angezeigten Element.
Durchzuführende Aktionen: Über das Kontextmenü werden eine Unterklasse und
eine Sibling-Klasse hinzugefügt und im Anschluss gelöscht.
Erwartetes Ergebnis: Die hinzuzufügende Klassen sollen in der Ontologie erscheinen und gleich visualisiert werden. Die Informationen sollen sich auch bei jeder Aktion
im OWLPropVizGraphInfo aktualisieren.
Eingetretenes Ergebnis: Das erwartete Ergebnis ist eingetroffen.
Auswertung: Die Hinzufüge- und Löschoperationen für die Knoten wurden zusätzlich in dem Graphenbereich platziert. Die Änderungen werden sofort sichtbar und
die Statistiken werden aktualisiert. Somit ist eine Verbesserung im Bereich der Zeit
und Zugänglichkeit erreicht worden. Die alten Funktionen sind zwar immer noch an
deren alten Stellen platziert und können weiterhin benutzt werden, erfordern aber
eine manuelle Aktualisierung des Graphen nach dem Durchführen der Aktion. Um
ein Individuum in der früheren Version von OWLPropViz zu löschen war sogar ein
zusätzlicher Wechsel des WorkspaceTabs notwendig.
6.1.8. Kontextmenü: Hinzufügen von Kanten
Ausgangszustand: Die Onotlogie uni.owl geladen. Ein Graph mit Darstellung aller
Kanten, außer disjoint-with ist generiert.
Durchzuführende Aktionen: 1. Mit Hilfe des Kontextmenüpunktes „Assert to
class...“ wird das Individuum Prof._Balke der Klasser Professor zugewiesen. 2. Kontextemenupunkt „Add property to object...“ wird auf dem gleichen Individuum aufgerufen. Als Ziel wird das Individuum Wolf-Tilo_Balke ausgewählt. Neue Propertyname wird angegeben: ist. 3. Auf der Klasse Professor wird der Kontextmenüpunkt
„Add restriction to class...“ ausgeführt. Als Ziel wird University gewählt und als Property Name worksAtaus den bestehenden Properties ausgewählt.
Erwartetes Ergebnis: 1. Prof._Balke ist mit der Klasse Professor über die Kante
instance-of verbunden. Im OWLPropViz GraphInfo ändern sich die entrsprechenden Zahlen der Kanten. 2. Prof._Balke ist über die Kante ist mit dem Individuum
Wolf-Tilo_Balke verbunden. Die neue Kante ist erscheint im OWLPropViz GraphInfo, die gesamtzahl der Kanten erhöht sich. 3. Der Knoten ist nun über die Kante
worksAt mit dem Knoten University verbunden. Die entsprechenden Zahlen ändern
sich in dem OWLPropViz GraphInfo. 1-3. Die eingefügten Beziehungen sind in dem
entsprechenden Individuum- bzw. Klasseneditor von Protégé zu finden (Doppelklick
Funktion, Kapitel 5.2.1)
Eingetretenes Ergebnis: Das erwartete Ergebnis ist eingetroffen.
Auswertung: Es ist gelungen mit wenigen Mausklicks Erstellung von Assertions
und Restrictions zu erreichen.
53
6. Evaluation
Abbildung 6.4.: Ergebnis des Testfalls 8
6.2. Auswertung
Die Auswertung der Testfälle hat gezeigt, dass die vorgenommenen Änderungen
an OWLPropViz zu einer deulichen Steigerung der Benutzerfreundlichkeit und Arbeitseffizienz geführt haben. Insbesondere zeigt sich dies in den eleganten und mit
weniger Nutzeraktionen verbundenen Operationen, wie dem Hinzufügen von Kanten und der Anzeige der äquivalenten Klassen. Erreicht wurde diese unter anderem
durch eine intuitive Platzierung von Operationen in Form eines Doppelklicks oder
eines Kontextmenüs. Ausserdem können Änderungen an der Ontologie nun direkt am
Graphen vorgenommen werden, anstatt Änderungen nur graphisch darzurstellen, wie
es in der alten Version des Plugins und in OWLViz der Fall ist.
54
7 Schlussbetrachtungen
7.1. Zusammenfassung
Es wurde eine weitere Version des OWLPropViz-Plugins entwickelt, die dem Benutzer
zusätzliche Interaktionsmöglichkeit bietet.
In dem Kapitel 2 wurden zunächst alle Grundlagen vermittelt, die notwendig sind,
um den Aufgabenbereich und die Architektur von Protégé zu verstehen. Dafür wurde
zunächst das generelle Problem der Semantic Web erläutert, um zu dem Begriff der
Ontologie zu kommen. Es wurde erklärt, wie aus XML die Ontologie-Sprache OWL
entsteht und die für OWLPropViz wichtigen semantischen Konstrukte äquivalente
Klasse und Property Restriction werden an einem Ontologiebeispiel näher erläutert.
Die nächsten Unterkapitel befassen sich mit der Beschreibung von Protégé. Es wurde
auf die für die Plugin-Entwicklung wichtige OSGi Plattform und deren Implementierung Equinox eingegangen, um den Überblick darüber zu verschaffen wie ein Plugin
entwickelt wird. Anschließend wurden die in OWLPropViz benutzten Entwurfsmuster erläutert.
Kapitel 3 beschäftigt sich damit, die Entwicklungsumgebung Eclipse einzustellen.
Schritt für Schritt wurde erklärt, wie man die aktuellen Versionen von Protégé und
von OWLPropViz bezieht und kompiliert. Die Integration der beiden Projekte zu
einem Eclipse-Projekt wird beschrieben, um eine noch bequemere Entwicklung unter
Benutzung des Java-Debuggers zu ermöglichen.
In Kapitel 4 wurde auf die Architekturen des OWLViz- und des neuen OWLPropViz-Plugins eingegangen. Es wurden die einzelnen Funktionen derer Pakete beschrieben, da sie eine Basis für die Weiterentwicklung von OWLPropViz sind. Daraus
wurde ersichtlich, dass die gestellten Aufgaben ohne Einmischung in die vorhandenen
Strukturen realisierbar sind, jedoch neue Konzepte und Modelle benötigen. Die einzelnen Aufgaben wurden genauer betrachtet und deren Architektur diskutiert. Neben
den kleinen Interaktionsverbesserungen wurden neue Konzepte eingeführt: Anzeige
von äquivalenten Klassen als Tooltip, ein weiteres Plugin OWLPropVizGraphInfo
zur Anzeige der gesamten und sichtbaren Ontologieelementen, sowie ein Kontextmenü, welches die oft benutzen Funktionen zugänglich macht und einige Neue zur
Verfügung stellt.
Implementierung der zuvor beschriebenen Konzepte und einzelner Aufgaben, ist im
Kapitel 5 beschrieben. Anhand des Quelltextes werden alle intern ablaufenden Prozesse zur Erreichung der Aufgabenstellungen erklärt. Für die Doppelklickfunktion, die
Add/Delete-Funktion und die automatische Anzeige des Graphen werden bereits in
Protégé vorhandenen Funktionalitäten genommen. Für die weiteren Funktionen wur-
55
7. Schlussbetrachtungen
de ein neues Modell erstellt, genau beschrieben und implementiert. Dieses betrifft die
textbasierte Anzeige der visualisierten und versteckten Elemente des Graphen, das
Hinzufügen von Kanten und die Wiederherstellung der Show/Hide-Funktionalitäten.
Des Weiteren wurde auf die Schwierigkeiten bei der Implementierung eingegangen,
wie z.B. die zwei schwerwiegenden Bugs bei der Tooltip-Anzeige und dem Einlesen
der .dot-Datei.
Die Testfälle des 6. Kapitels dienen dazu, die Funktionsfähigkeit der implementierten Funktionen zu überprüfen. In einzelnen Testfällen wird der Vorgang beschrieben,
welcher die neu implementierten Funktionen aufruft. Es findet ein Vergleich der Sollund Ist-Zustands des Programms, aus welchem hervorgeht, dass die gestellten Aufgaben erfüllt sind.
7.2. Ausblick
Die aktuelle Version von OWLPropViz kann für gewisse Zeitersparnisse beim Umgang mit dem Programm sorgen. Viele Funktionen wurden in das Kontextmenu verlagert und somit zugänglicher gemacht. Die meisten einfachen Aktionen, die auf Ontologien ausgeführt werden, können jetzt direkt am Graphen durchgeführt werden.
Der nächste Schritt wäre noch mehr Funktionen in das Plugin zu integrieren.
Derzeit ist z.B. keine Löschfunktion für die Kanten vorhanden. Der Ansatz diese
Funktionalität zu realisieren ist gegeben, konnte aber aus zeitlichen Gründen, ähnlich
wie bei weiteren Vorschlägen und Ansätzen, nicht weiter verfolgt werden. Die im
Graphen angezeigten Kanten, sollten einzeln auswählbar sein. Damit könnte man
einen weiteren Kontextmenüeintrag realisieren, welcher die ausgewählte Kante gleich
löscht.
Es könnte ein neuer Kontextmenüeintrag realisiert werden, der das Hinzufügen von
Individuen innerhalb des Graphen ermöglicht. Ähnlich wie bei den Klassen, welche
bereits über zwei Menüpunkte „Add sibling...“ und „Add subclass...“ verfügen, könnte
für das Hinzufügen von Individuen ein Eintrag „Add instance...“ stehen, der eine
Instanz der ausgewählten Klasse direkt in den Graphen und Ontologie hinzufügt.
Eine weitere Verbesserung sollte im Bereich der Performanz erreicht werden. Im
Rahmen dieser Bachelorarbeit stand die Wiederherstellung und die Erweiterung der
Funktionalitäten so sehr im Vordergrund, dass der Performanzaspekt teilweise komplett vernachlässigt wurde. Die Kernüberlegung zur Steigerung der Performanz ist,
ein von dem VisualizedObjectManager unabhängiges Modell zur Speicherung von
Knoten und Kanten zu erstellen. Es soll ein Datentyp verwendet werden, der effizient mit Bäumen umgehen und die Baumstruktur direkt aus den Axiomen erstellen
kann. Damit könnte man einen deutlichen Zeitgewinn bei großen Ontologien erreichen.
Insgesamt tendiert das verbesserte OWLPropViz-Plugin dazu, den kompletten
Umgang mit der Ontologie, zumindest auf der einfachen Ebene, visuell zu ermöglichen, was bei den nächsten Entwicklungen unbedingt berücksichtigt und weiterverfolgt werden muss.
56
Literatur
[Con04a] Consortium, World Wide W.: OWL Web Ontology Language Guide.
2004. – http://www.w3.org/TR/owl-guide/ Stand: 10.02.2004 Letzter Aufruf: 01.10.2009
7,
9
[Con04b] Consortium, World Wide W.: RDF Vocabulary Description Language
1.0: RDF Schema. 2004. – http://www.w3.org/TR/rdf-schema/ Stand:
10.02.2004 Letzter Aufruf: 01.10.2009
8
[Daull] Daum, Berthold: Java-Entwicklung mit Eclipse 3.3: Anwendungen, Plugins
und Rich Clients. 5., aktualisierte und erweiterte Auflage. dpunkt.verlag,
2008
11
[Gamll] Gamma, Erich: Entwurfsmuster: Elemente wiederverwendarere objektorientierter Software. 1. Auflage. Addison-Wesley Publishing Company, 1996
12
[Hor09] Horridge, Matthew: OWL-API. 2009. – http://owlapi.sourceforge.net/
Stand: 01.10.2009
27
[PHll]
Pascal Hitzler, Sebastian Rudolph York S. Markus Krötzsch K. Markus Krötzsch: Semantic Web Grundlagen. 1. Auflage. Springer, 2008
6,
7
[Sch09] Schön, Axel: Weiterentwicklung der Softwarearchitektur von Protégé zur
Onthologievisualisierung: Darstellung. 2009. – Parallele Bachelorarbeit 1, 3,
28, 29, 49
[Sta09] Stanford Center for Biomedical Informatics Research at Stanford University School of Medicine: What is Protégé-OWL? 2009.
– http://protege.stanford.edu/overview/protege-owl.html Stand: 01.10.2009
10
[Thi09] Thiem, Sebastian: Implementierung und Evaluation einer Datenbankanbindung an die OSGI-Service-Plattform. 2009. – Bachelorarbeit an dem Institut
für Betriebssysteme und Rechnerverbund, TU Braunschweig
11
[Tig09] Tigris.org:
subclipse: Download and Install.
2009. –
http://subclipse.tigris.org/servlets/ProjectProcess?pageID=p4wYuA
Stand: 01.10.2009
15
57
Literatur
[Vor09] Vorlesung „Knowledge-Based Systems and Deductive Databases“, TU Braunschweig: Uni-Ontologie. 2009. – http://www.ifis.cs.tubs.de/sites/default/files/webfm/slides/ss09_kbs/uni.owl Stand: 01.10.2009
viii, 3
[Wac08] Wachsmann, Lutz: Entwurf und Implementierung eines Modells zur Visualisierung von OWL-Properties als Protégé-PlugIn mit Layoutalgorithmen
aus Graphviz. 2008. – Diplomarbeit, TU Braunschweig
3
58
A Zusätzliche Dateien
A.1. plugin.xml
1
2
<?xml version="1.0" ?>
<plugin>
4
5
6
7
8
9
10
11
12
<!-- Classes view -->
<extension point="org.protege.editor.core.application.WorkspaceTab"
id="OWLPropVizTab">
<label value="OWLPropViz"/>
<class value="org.protege.editor.core.ui.workspace.WorkspaceViewsTab"/>
<editorKitId value="OWLEditorKit"/>
<index value="JJJ"/>
<defaultViewConfigFileName value="viewconfig-owlpropviz.xml"/>
</extension>
14
15
16
17
18
19
20
<extension point="org.protege.editor.core.application.ViewComponent"
id="OWLPropVizView">
<label value="OWLPropertyViz"/>
<class value="de.tubs.cs.ifis.owlpropviz.OWLPropVizView"/>
<headerColor value="@org.protege.classcolor"/>
<category value="@org.protege.classcategory"/>
</extension>
22
23
24
25
26
27
28
29
30
<extension point="org.protege.editor.core.application.ViewComponent"
id="OWLPropVizGraphInfo">
<label
value="OWLPropViz Graphinfo"/>
<class
value="de.tubs.cs.ifis.owlpropvizgraphinfo.
OWLPropVizGraphInfoViewComponent"/>
<headerColor value="@org.protege.ontologycolor"/>
<category value="@org.protege.ontologycategory"/>
</extension>
</plugin>
Listing A.1: plugin.xml
1
2
59
4
5
6
<?xml version = "1.0" encoding = "utf-8"?>
<project name = "owlpropviz" default = "compile">
<!-To run this build file set the environment variable
PROTEGE_HOME to point to a protege distribution and type ant
7
8
9
10
11
install or jar.
-->
<property environment="env"/>
<property name = "protege.home" location="${dist.dir}/equinox"/>
<property file="local.properties"/>
13
14
15
16
17
<property
<property
<property
<property
<property
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<property name = "protege.common" location="${protege.home}/bundles"/>
<property name = "protege.plugins" location="${protege.home}/plugins"/>
<property name = "protege.osgi" location="${protege.home}/
org.eclipse.osgi.jar"/>
<property name="equinox.common" location="${protege.common}/
org.eclipse.equinox.common.jar"/>
<property name="equinox.registry" location="${protege.common}/
org.eclipse.equinox.registry.jar"/>
<property name="protege.lib"
location="${protege.common}/
org.protege.editor.core.application.jar"/>
<property name="common.lib"
location="${protege.common}/org.protege.common.jar"/>
<property name="owl.lib"
location="${protege.plugins}/
org.semanticweb.owl.owlapi.jar"/>
<property name="owl.editor.jar" location="${protege.plugins}/
org.protege.editor.owl.jar"/>
<property name="owlviz.jar"
location="${protege.plugins}/org.coode.owlviz.jar"/>
36
<property name = "genlib"
38
39
40
41
42
43
44
45
46
47
48
49
50
51
53
54
55
56
57
58
59
60
61
62
63
64
65
66
name
name
name
name
name
=
=
=
=
=
"src.dir"
location="${owlpropviz.dir}/src"/>
"build"
location="${owlpropviz.dir}/build"/>
"classes"
location="${build}/classes"/>
"owlpropviz.jar" location="${build}/de.tubs.cs.ifis.owlpropviz.jar"/>
"manifest"
location="${build}/manifest.mf"/>
location = "${build}/lib"/>
<!-- =================================================================== -->
<!-- INIT -->
<!-- =================================================================== -->
<target name = "init">
<echo message="---------------Building the Owlpropviz Plugin -------------"/>
<tstamp/>
<tstamp>
<format property="build.time" pattern="MM/dd/yyyy hh:mm aa"/>
</tstamp>
<mkdir dir = "${build}"/>
<mkdir dir = "${classes}"/>
<mkdir dir = "${classes}/lib"/>
<mkdir dir = "${genlib}"/>
</target>
<target name = "checkProtegeLibs">
<echo message="Using Protege Home = ${protege.home} to find protege jars"/>
<condition property="libs.found">
<and>
<available file="${equinox.common}" type = "file"/>
<available file="${equinox.registry}" type = "file"/>
<available file="${owl.editor.jar}" type = "file"/>
<available file="${owl.lib}" type="file"/>
<available file="${owlviz.jar}" type="file"/>
</and>
</condition>
<path id = "owlpropviz.classpath">
<pathelement location = "${protege.lib}"/>
<pathelement location="${equinox.common}"/>
75
76
77
78
79
80
81
82
83
84
85
87
88
89
90
91
92
93
94
<pathelement location="${equinox.registry}"/>
<pathelement location="${owl.editor.jar}"/>
<pathelement location="${owl.lib}"/>
<pathelement path="${owlviz.jar}"/>
<fileset dir="${genlib}"/>
</path>
</target>
<target name="checkProtegeLibsAndReport" depends="checkProtegeLibs"
unless="libs.found">
<echo message="Missing protege libraries. You need to set "/>
<echo message="the PROTEGE_HOME environment variable to a"/>
<echo message="protege installation directory where the"/>
<echo message="appropriate plugins have been installed."/>
<echo message="Alternatively set the jar libs in local.
properties (protege.lib=...)"/>
<echo message="Use the -v option to ant to see what jars are missing."/>
<fail message = "missing protege libraries"/>
</target>
<target name = "buildlibs" depends="init, checkProtegeLibsAndReport">
<unjar dest="${build}"
src="${common.lib}">
<patternset>
<include name = "**/log4j.jar"/>
</patternset>
</unjar>
</target>
96
97
98
99
100
101
102
103
104
105
<!-- ================================================================ -->
<!-- COMPILE -->
<!-- ================================================================ -->
<target name = "compile" depends = "init,buildlibs">
<javac srcdir = "${src.dir}" destdir = "${classes}"
debug="true"
includeAntRuntime = "false">
<classpath refid = "owlpropviz.classpath"/>
</javac>
</target>
108
109
110
111
<!-- =========================================================== -->
<!-- Bundle - create the distribution JAR file that goes -->
<!-in the "plugins" directory -->
<!-- =========================================================== -->
113
114
115
116
117
118
119
120
121
124
125
126
<target name="build.manifest">
<copy tofile="${manifest}"
file="${owlpropviz.dir}/META-INF/manifest.mf" overwrite="true"/>
<manifest file="${manifest}"
mode = "update">
<attribute name="Built-By" value = "${user.name}"/>
<attribute name="Build-Date" value = "${build.time}"/>
</manifest>
</target>
<target name = "bundle" depends = "compile,build.manifest">
<copy todir="${classes}" file="${owlpropviz.dir}/plugin.xml"/>
<copy todir="${classes}">
127
128
<fileset dir="${owlpropviz.dir}/resources"/>
</copy>
130
131
132
133
<!-- image files -->
<copy todir = "${classes}/de/tubs/cs/ifis/owlpropviz/image">
<fileset dir = "${src.dir}/de/tubs/cs/ifis/owlpropviz/image"/>
</copy>
135
136
137
138
<jar basedir = "${classes}"
destfile = "${owlpropviz.jar}"
manifest = "${manifest}"/>
</target>
140
141
142
143
144
145
<!-- ============================================================== -->
<!-- INSTALL - Install the plugin -->
<!-- ============================================================== -->
<target name = "install" depends = "bundle">
<copy todir="${protege.plugins}" file="${owlpropviz.jar}"/>
</target>
147
148
149
153
<target name = "clean">
<delete dir = "${build}"/>
</target>
</project>
Listing A.2: build.xml
A. Zusätzliche Dateien
60
67
68
69
70
71
72
73
34
B Implementierung:
Quelltext
B.1. de.tubs.cs.ifis.owlpropvizgraphinfo
B.1.1. OWLPropVizGraphInfoViewComponent
1
package de.tubs.cs.ifis.owlpropvizgraphinfo;
3
4
import java.awt.BorderLayout;
...
6
7
8
9
10
11
12
13
14
15
16
/**
*
* @author Sergey Kovalev
* @version 1.0
*
*
This Class extends the AbstractOWLViewComponent and is the entry
*
point for the "OWLPropVizGraphInfo" view.
*
*/
public class OWLPropVizGraphInfoViewComponent
extends AbstractOWLViewComponent {
61
18
19
20
21
22
23
24
25
/** serial */
private static final long serialVersionUID = -7927403467385074253L;
/** Variable to store the html pane */
private JEditorPane htmlPane;
/** Variable to store the impelementation of {@link GraphChangeListener} */
private GraphChangeListener graphChangeListener;
/** Variable to store current ontology displaying statistics */
private GraphItemsStatistics stats;
27
28
29
30
31
32
33
/**
* Implements the superclass’ method intialiseOWLView and initialises the
* view
*/
protected void initialiseOWLView() throws Exception {
setLayout(new BorderLayout());
htmlPane = ComponentFactory.createHTMLPane(new HyperlinkListener() {
public void hyperlinkUpdate(HyperlinkEvent e) {
36
37
38
39
40
41
}
43
44
45
46
47
48
49
50
51
/**
* Initializes the the html panel, where all the inforamations about the
* edges and nodes count will be written
*/
private void initializeHtmlPane() {
htmlPane.setText("" + "<html>" + "<body bgcolor=\"#FFFFFF\">"
+ "View Initialized. " + "Restart Proptégé to start"
+ "showing statistics." + "</body>" + "</hmtl>");
}
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/**
* This method creates an instance {@link GraphChangeListener}, which
* handles all relevant {@link GraphChangeEventType}s
*/
private void createGraphChangeEventListener() {
graphChangeListener = new GraphChangeListener() {
public void handleChange(GraphChangeEvent event) {
if (event.isType(GraphChangeEventType.INITIALIZE_GRAPH_ITEMS)) {
stats = new GraphItemsStatistics(event.getView());
stats.initializeGraphItems();
} else if (event
.isType(GraphChangeEventType.SHOWN_ITEMS_CHANGED)) {
if (stats != null) {
stats.updateShownItems();
updateGraphStats(stats);
}
} else if (event.isType(GraphChangeEventType.EDGE_ADDED)) {
stats.increasePropEdgeCountAndUpdateStats(event
.getPropertyName());
updateGraphStats(stats);
} else if (event.isType(GraphChangeEventType.CLASS_ADDED)) {
stats.increasePropEdgeCountAndUpdateStats("is-a");
stats.getNodesCounter().increaseNormalClsShownCount();
stats.getNodesCounter().increaseNormalClsTotalCount();
updateGraphStats(stats);
} else if (event
.isType(GraphChangeEventType.NORMAL_CLASS_REMOVED)) {
stats.getNodesCounter().decreaseNormalClsShownCount();
stats.getNodesCounter().decreaseNormalClsTotalCount();
updateGraphStats(stats);
} else if (event
.isType(GraphChangeEventType.EQUIVALENT_CLASS_REMOVED)) {
stats.getNodesCounter().decreaseEquivalentlClsShownCount();
stats.getNodesCounter().decreaseEquivalentlClsTotalCount();
updateGraphStats(stats);
} else if (event
.isType(GraphChangeEventType.INDIVIDUAL_REMOVED)) {
stats.getNodesCounter().decreaseIndiesShownCount();
stats.getNodesCounter().decreaseIndiesTotalCount();
updateGraphStats(stats);
}
}
});
add(ComponentFactory.createScrollPane(htmlPane), BorderLayout.CENTER);
initializeHtmlPane();
createGraphChangeEventListener();
}
};
EventManager.getInstance().addGraphChangeListener(graphChangeListener);
}
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/**
* Generates a string from the specified statistics, which is being
* displayed in the html pane
*
* @param stats
*
current statistics
*/
private void updateGraphStats(GraphItemsStatistics stats) {
htmlPane
.setText(""
+ "<html>"
+ "<body bgcolor=\"#FFFFFF\">"
+ "<table><tr><th align=\"left\">Item
</th><th>Shown</th><th>Total</th></tr>"
+ getNodesCount()
+ "<tr><th align=\"left\">Total edges: </th><th>"
+ stats.getEdgesShown() + "</th><th>"
+ stats.getEdgesTotal() + "</th></tr>"
+ getNamedEdgesAndCounts() + "</table></body>"
+ "</hmtl>");
}
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
* invoked by updateGraphStats(), iterates thought all {@link EdgeCounter}s
* and returns their counts
*
* @return
*/
private String getNamedEdgesAndCounts() {
StringBuffer result = new StringBuffer();
for (EdgeCounter counter : stats.getEdgeCounters()) {
result.append("<tr><td>" + counter.getName()
+ "</td><td align=\"center\">" + counter.getShownCount()
+ "</td><td align=\"center\">" + counter.getTotalCount()
+ "</td></tr>");
}
return result.toString();
}
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/**
* invoked by updateGraphStats(), creates a string representation of all
* current nodes count
*
* @return
*/
private String getNodesCount() {
StringBuffer result = new StringBuffer();
result.append("<tr><td>Classes:</td><td align=\"center\">"
+ stats.getNodesCounter().getNormalClsShownCount()
+ "</td><td align=\"center\">"
+ stats.getNodesCounter().getNormalClsTotalCount()
+ "</td></tr>"
+ "<tr><td>Equivalent Classes:</td><td align=\"center\">"
+ stats.getNodesCounter().getEquivalentClsShownCount()
+ "</td><td align=\"center\">"
154
155
156
157
158
159
160
161
}
163
164
165
166
167
168
169
170
171
/**
* Sets specified text in the html pane
*
* @param text
*
to be displayed
*/
protected void setHtmlPaneText(String text) {
htmlPane.setText(text);
}
173
174
175
176
177
/**
* Implements the superclass’ method disposeOWLView() and is called on main
* protege window close
*/
protected void disposeOWLView() {
+ stats.getNodesCounter().getEquivalentClsTotalCount()
+ "</td></tr>"
+ "<tr><td>Individuals:</td><td align=\"center\">"
+ stats.getNodesCounter().getIndisShownCount()
+ "</td><<td align=\"center\">"
+ stats.getNodesCounter().getIndisTotalCount() + "</td></tr>");
return result.toString();
179
181
}
}
Listing B.1: OWLPropVizGraphInfoViewComponent.java
B.1.2. GraphItemsStatistics
1
package de.tubs.cs.ifis.owlpropvizgraphinfo;
3
4
import java.util.ArrayList;
...
6
7
8
9
10
11
12
13
/**
* This class contains all objects and methods needed to generate edges and
* nodes counts
*
* @author skovalev
*
*/
public class GraphItemsStatistics {
15
16
/** Variable to store the propvizview */
private OWLPropVizView view;
18
19
20
21
/** Variable to store the count of all graph edges */
private int edgesTotalCount;
/** Variable to store the count of shown graph edges */
private int edgesShownCount;
B. Implementierung: Quelltext
62
94
95
96
97
/** This HashSet is here to ensure that each named
* edge has only one counter */
private HashSet<String> namedEdges;
/** List to store the counters */
private List<EdgeCounter> edgeCounters;
/** Variable to store the {@link NodesCounter} */
private NodesCounter nodesCounter;
31
32
33
34
35
36
37
38
39
/**
* Default Constructor
*
* @param view
*
active instance of the OWLVizPropVizView
*/
public GraphItemsStatistics(OWLPropVizView view) {
this.view = view;
}
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
* Initializes all edges and nodes, then counts them
*/
public void initializeGraphItems() {
/* count all edges */
Edge[] edgesTotal = view.getDispManager().getAllEdges();
edgesTotalCount = edgesTotal.length;
namedEdges = new HashSet<String>();
edgeCounters = new ArrayList<EdgeCounter>();
String curName = "";
for (Edge edge : edgesTotal) {
/* initialize and count named edges */
curName = edge.getUserObject().toString();
if (!namedEdges.contains(curName)) {
namedEdges.add(curName);
edgeCounters.add(new EdgeCounter(curName));
} else {
for (EdgeCounter counter : edgeCounters) {
if (counter.getName().equals(curName)) {
counter.increaseTotalCount();
}
}
}
}
/* initialize and count classes */
nodesCounter = new NodesCounter();
Node[] nodesTotal = view.getDispManager().getAllNodes();
for (Node node : nodesTotal) {
if (node.getUserObject() instanceof OWLClassImpl) {
OWLClass cls = view.getOWLModelManager().getOWLClass(
node.getUserObject().toString());
Set<OWLDescription> descSet = cls.getEquivalentClasses(view
.getOWLModelManager().getActiveOntology());
if (descSet.toString().equals("[]")) {
nodesCounter.increaseNormalClsTotalCount();
} else {
nodesCounter.increaseEquivalentlClsTotalCount();
}
nodesCounter.increaseNormalClsShownCount();
} else if (node.getUserObject() instanceof OWLIndividualImpl) {
nodesCounter.increaseIndiesTotalCount();
82
83
84
}
}
}
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/**
* Counts currently displayed nodes and edges, updates the counts stored in
* the counters
*/
public void updateShownItems() {
Edge[] edgesShown = view.getPropertyGraphComponent()
.getPropertyGraphView().getGraph().getEdges();
edgesShownCount = edgesShown.length;
for (EdgeCounter edgeCounter : edgeCounters) {
edgeCounter.resetShownCount();
}
String curName = "";
for (Edge edge : edgesShown) {
curName = edge.getUserObject().toString();
for (EdgeCounter counter : edgeCounters) {
if (counter.getName().equals(curName)) {
counter.increaseShownCount();
}
}
}
/* count shown classes */
nodesCounter.resetShownNodesCount();
Node[] nodesShown = view.getPropertyGraphComponent()
.getPropertyGraphView().getGraph().getNodes();
for (Node node : nodesShown) {
if (node.getUserObject() instanceof OWLClassImpl) {
OWLClass cls = view.getOWLModelManager().getOWLClass(
node.getUserObject().toString());
if (cls != null) {
Set<OWLDescription> descSet = cls.getEquivalentClasses(view
.getOWLModelManager().getActiveOntology());
if (descSet.toString().equals("[]")) {
nodesCounter.increaseNormalClsShownCount();
} else {
nodesCounter.increaseEquivalentlClsShownCount();
}
}
} else if (node.getUserObject() instanceof OWLIndividualImpl) {
nodesCounter.increaseIndiesShownCount();
}
}
}
129
130
131
132
133
134
135
136
137
138
139
140
141
/**
* This method finds the {@link EdgeCounter} with the same name as the
* property, where the count has to be increased. If there is no counter
* with specified name, a new one will be created
*
*
* @param counterName
*
is usually the same as the name of the property (edge)
*/
public void increasePropEdgeCountAndUpdateStats(String counterName) {
boolean edgeExists = false;
for (EdgeCounter edgeCounter : getEdgeCounters()) {
if (edgeCounter.getName().equals(counterName)) {
B.1. de.tubs.cs.ifis.owlpropvizgraphinfo
63
23
24
25
26
27
28
29
}
155
156
157
public int getEdgesTotal() {
return edgesTotalCount;
}
159
160
161
public int getEdgesShown() {
return edgesShownCount;
}
163
164
165
166
167
168
169
/**
* increases both shown and total edges count
*/
public void increaseTotalAndShownEdgeCount() {
edgesShownCount++;
edgesTotalCount++;
}
171
172
173
public HashSet<String> getNamedEdges() {
return namedEdges;
}
175
176
177
public List<EdgeCounter> getEdgeCounters() {
return edgeCounters;
}
179
180
181
public NodesCounter getNodesCounter() {
return nodesCounter;
}
183
edgeExists = true;
edgeCounter.increaseTotalCount();
edgeCounter.increaseShownCount();
}
}
if (!edgeExists) {
EdgeCounter counter = new EdgeCounter(counterName);
counter.increaseShownCount();
getEdgeCounters().add(counter);
}
increaseTotalAndShownEdgeCount();
}
Listing B.2: GraphItemsStatistics.java
B.2. de.tubs.cs.ifis.owlpropvizgraphinfo.counters
B.2.1. EdgeCounter
1
3
4
5
6
7
8
9
10
package de.tubs.cs.ifis.owlpropvizgraphinfo.counters;
/**
* This class contains the integer variables to store the counts of normal
* classes, equivalent classes and individuals
*
* @author skovalev
*
*/
public class NodesCounter {
12
13
14
15
/** Variable to store the count of all normal classes in ontology */
private int normalClsTotalCount;
/** Variable to store the count of normal classes shown in graph */
private int normalClsShownCount;
17
18
19
20
/** Variable to store the count of all equivalent classes in ontology */
private int equivalentClsTotalCount;
/** Variable to store the count of equivalent classes shown in graph */
private int equivalentClsShownCount;
22
23
24
25
/** Variable to store the count of all individuals in ontology */
private int indisTotalCount;
/** Variable to store the count of individuals shown in graph */
private int indisShownCount;
27
28
29
30
31
32
33
34
35
36
37
/**
* Default constructor, sets all counts to 0
*/
public NodesCounter() {
normalClsShownCount = 0;
normalClsTotalCount = 0;
equivalentClsShownCount = 0;
equivalentClsTotalCount = 0;
indisShownCount = 0;
indisTotalCount = 0;
}
39
40
41
42
43
44
45
46
/**
* This method resets the count of all shown nodes types
*/
public void resetShownNodesCount() {
resetEquivalentClsShownCount();
resetIndisShownCount();
resetNormalClsShownCount();
}
48
/* Normal Classes */
50
51
52
public int getNormalClsTotalCount() {
return normalClsTotalCount;
}
54
55
56
57
58
59
/**
* resets <code>normalClsTotalCount</code> to 0
*/
public void resetNormalClsTotalCount() {
normalClsTotalCount = 0;
}
B. Implementierung: Quelltext
64
142
143
144
145
146
147
148
149
150
151
152
153
/**
* increases <code>normalClsTotalCount</code>: +1
*/
public void increaseNormalClsTotalCount() {
normalClsTotalCount++;
}
68
69
70
71
72
73
/**
* decreases <code>normalClsTotalCount</code>: -1
*/
public void decreaseNormalClsTotalCount() {
normalClsTotalCount--;
}
75
76
77
public int getNormalClsShownCount() {
return normalClsShownCount;
}
79
80
81
82
83
84
/**
* resets <code>normalClsShownCount</code>
*/
public void resetNormalClsShownCount() {
normalClsShownCount = 0;
}
86
87
88
89
90
91
/**
* increases <code>normalClsShownCount</code>: +1
*/
public void increaseNormalClsShownCount() {
normalClsShownCount++;
}
93
94
95
96
97
98
/**
* decreases <code>normalClsShownCount</code>: -1
*/
public void decreaseNormalClsShownCount() {
normalClsShownCount--;
}
65
100
/* Equivalent Classes */
102
103
104
public int getEquivalentClsTotalCount() {
return equivalentClsTotalCount;
}
106
107
108
109
110
111
/**
* resets <code>equivalentClsTotalCount</code> to 0
*/
public void resetEquivalentClsTotalCount() {
equivalentClsTotalCount = 0;
}
113
114
115
116
117
118
/**
* increases <code>equivalentClsTotalCount</code>: +1
*/
public void increaseEquivalentlClsTotalCount() {
equivalentClsTotalCount++;
}
120
/**
121
122
123
124
125
* decreases <code>equivalentClsTotalCount</code>: -1
*/
public void decreaseEquivalentlClsTotalCount() {
equivalentClsTotalCount--;
}
127
128
129
public int getEquivalentClsShownCount() {
return equivalentClsShownCount;
}
131
132
133
134
135
136
/**
* resets <code>equivalentClsShownCount</code> to 0
*/
public void resetEquivalentClsShownCount() {
equivalentClsShownCount = 0;
}
138
139
140
141
142
143
/**
* increases <code>equivalentClsShownCount</code>: +1
*/
public void increaseEquivalentlClsShownCount() {
equivalentClsShownCount++;
}
145
146
147
148
149
150
/**
* decreases <code>equivalentClsShownCount</code>: -1
*/
public void decreaseEquivalentlClsShownCount() {
equivalentClsShownCount--;
}
152
/* Individuals */
154
155
156
public int getIndisTotalCount() {
return indisTotalCount;
}
158
159
160
161
162
163
/**
* resets <code>indisTotalCount</code> to 0
*/
public void resetIndisTotalCount() {
indisTotalCount = 0;
}
165
166
167
168
169
170
/**
* increases <code>indisTotalCount</code>: +1
*/
public void increaseIndiesTotalCount() {
indisTotalCount++;
}
172
173
174
175
176
177
/**
* decreases <code>indisTotalCount</code>: -1
*/
public void decreaseIndiesTotalCount() {
indisTotalCount--;
}
179
180
public int getIndisShownCount() {
return indisShownCount;
B.2. de.tubs.cs.ifis.owlpropvizgraphinfo.counters
61
62
63
64
65
66
}
183
184
185
186
187
188
/**
* resets <code>indisShownCount</code> to 0
*/
public void resetIndisShownCount() {
indisShownCount = 0;
}
190
191
192
193
194
195
/**
* increases <code>indisShownCount</code>: +1
*/
public void increaseIndiesShownCount() {
indisShownCount++;
}
197
198
199
200
201
202
/**
* decreases <code>indisShownCount</code>: -1
*/
public void decreaseIndiesShownCount() {
indisShownCount--;
}
204
26
27
28
29
30
31
32
*
is-a assertion)
*/
public EdgeCounter(String name) {
this.name = name;
countTotal = 1;
countShown = 0;
}
34
35
36
public String getName() {
return name;
}
38
39
40
public void setName(String name) {
this.name = name;
}
42
43
44
public int getTotalCount() {
return countTotal;
}
46
47
48
public void increaseTotalCount() {
countTotal++;
}
50
51
52
public int getShownCount() {
return countShown;
}
54
55
56
public void resetShownCount() {
countShown = 0;
}
58
59
60
61
62
63
64
65
66
67
68
public void increaseShownCount() {
countShown++;
/*
* following condition can be true, because of owlviz internal bug: if
* there are actually 2 or more edges between 2 nodes, only 1 edge is
* being displayed (counted)
*/
if (countShown > countTotal) {
countTotal = countShown;
}
}
}
Listing B.3: EdgeCounter.java
B.2.2. NodesCounter
1
package de.tubs.cs.ifis.owlpropvizgraphinfo.counters;
3
4
5
6
7
8
9
/**
* A class to store name of the edge and the counts
*
* @author skovalev
*
*/
public class EdgeCounter {
11
12
13
14
15
16
/** Name of the Edge, i.g. "is-a" */
private String name;
/** Count of all edges in ontology */
private int countTotal;
/** Count of edges shown in graph */
private int countShown;
18
19
20
21
22
23
24
25
/**
* Creates an Edge counter. Supposes
* create an instance of this class,
* edges count to 1. Shown edges = 0
* methods to get, increase (+1) and
*
* @param name
*
specify the name of the
that you have at least 1 edge, if you
so this constructor sets the total
for the first. Also contains the
reset (to 0) the counts
edge, for example ("is-a" edge for
70
}
Listing B.4: NodesCounter.java
B.3. de.tubs.cs.ifis.owlpropviz
B.3.1. OWLPropVizView
B. Implementierung: Quelltext
66
181
1
package de.tubs.cs.ifis.owlpropviz;
3
4
import java.awt.BorderLayout;
...
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* This class is the entry point for the plugin-architectrue. Therefore it
* extends the AbstractOWLClassViewComponent and implements the three abstract
* methods. This class implements the interface OWLVizViewI and implements its
* three methods. <br>
* This class shows the user interface and holds the graph model.
*
* @author wachsmann, skovalev, Axel Schön
*
*/
@SuppressWarnings("deprecation")
public class OWLPropVizView extends AbstractOWLClassViewComponent implements
OWLPropVizViewI, OWLVizViewI, DropTargetListener {
public static DefaultPropVizController getController() {
return c;
}
24
private static final Logger logger = Logger.getLogger(OWLPropVizView.class);
26
27
28
29
30
31
private static final long serialVersionUID = -3409019505422972058L;
private PropVizGraphComponent propVizGraphComponent;
private OWLVizWrapperAxiomGraphModel propertyModel;
protected OWLVizSelectionModel selectionModel;
private PropertyTreeSelectionPanel propertyTree;
private UIHelper helper;
33
34
35
36
37
/**
* stores the PropViz specific contextMenu, shown on right click on nodes in
* graph
*/
private PropVizPopupMenu popupMenu;
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
59
private
private
private
private
66
67
68
69
70
71
/**
* Implements the method getSelectionModel of interface OWLVizViewI.
*/
public OWLVizSelectionModel getSelectionModel() {
return selectionModel;
}
73
74
75
76
77
78
79
/**
* Implements the method getPropertyGraphComponents of interface
* OWLPropVizViewI.
*/
public Collection<PropVizGraphComponent> getPropertyGraphComponents() {
return getAllPropertyGraphComponents();
}
81
82
83
84
85
86
87
88
89
/**
* Implements the method getAllPropertyGraphComponents of interface
* OWLPropVizViewI.
*/
public Collection<PropVizGraphComponent> getAllPropertyGraphComponents() {
ArrayList<PropVizGraphComponent> list = new ArrayList<PropVizGraphComponent>();
list.add(propVizGraphComponent);
return list;
}
91
92
93
public PropVizGraphComponent getPropertyGraphComponent() {
return propVizGraphComponent;
}
HideUnboundedEntitiesCommand huec;
ShowUnboundedEntitiesCommand suec;
HideEdgeLabelsCommand helc;
ShowEdgeLabelsCommand selc;
/**
* indicates the status, the user is currently selecting the second node to
* make a new edge
*/
private boolean isPreparing;
/**
* stores the {@link HtmlHightlighter}, used by the tool tip to show the
* description rendering of equivalent classes
*/
private HtmlHighlighter highlighter;
/**
* contains the instance of {@link DisplayManager}, needed for the new
* hide/show model and autoload feature
*/
private DisplayManager dispManager;
/** tooltip instance where quivalent classes description is beeing showed */
private final JToolTip tt = new JToolTip();
/** used by tool tip to know if it has to be displayed or not */
public boolean validNode = false;
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/**
* Helper-method to create the user interface. It gets an instance of the
* class OWLVizWrapperAxiomGraphModel which holds the graph model.
*/
protected void createOWLPropertyVizUI() {
Set<OWLAxiom> axioms = new HashSet<OWLAxiom>();
propertyModel = new OWLVizWrapperAxiomGraphModel(getOWLModelManager()
.getOWLEntityRenderer(), axioms, this);
OWLPropVizGraphPanel propertyPanel = null;
propertyPanel = new OWLPropVizGraphPanel("Object Properties", this,
getOWLEditorKit(), propertyModel);
propVizGraphComponent = propertyPanel.getGraphComponent();
this.createToolBar(propVizGraphComponent.getController());
setLayout(new BorderLayout());
add(propertyPanel);
popupMenu = new PropVizPopupMenu(this);
setupListeners(getPropertyGraphComponent());
highlighter = new HtmlHighlighter(getOWLEditorKit());
dispManager = new DisplayManager(this);
dispManager.initializeCompleteOntology();
if (dispManager.getPropVizOptions().isAutodisplayEnabled) {
dispManager.autoDisplayPastRadius(3);
}
}
protected static DefaultPropVizController c;
120
/**
B.3. de.tubs.cs.ifis.owlpropviz
67
20
21
22
61
62
63
64
* This method calls the method with the same name in the super-class. The
* set of axioms as parameter "axioms" is given to the super-method.
*
* @param axioms
*
Set of OWLAxioms which will be visualized.
*/
public void addAndClearAxioms(Set<OWLAxiom> axioms) {
propertyModel.addAndClearAxioms(axioms);
getDispManager().setCurrentAxioms(axioms);
}
132
133
134
135
136
137
138
139
140
/**
* This method create the toolbar.
*
* @param propC
*
This parameter is of type Controller. Some action-commands
*
will need it.
* @return The toolbar with its commands.
*/
public JToolBar createToolBar(DefaultPropVizController propC) {
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
}
176
177
178
public HideEdgeLabelsCommand getHelc() {
return helc;
}
180
public void setHelc(HideEdgeLabelsCommand helc) {
OWLModelManager mm = getOWLModelManager();
OWLPropertyAxiomExtractorImpl extr = new OWLPropertyAxiomExtractorImpl(
mm);
JToolBar toolBar = new JToolBar();
ShowSelectedPropertiesCommand sspc = new ShowSelectedPropertiesCommand(
this, mm, propertyTree, helper, extr, propC);
addAction(sspc, "A", "A");
setHuec(huec);
setSuec(suec);
addAction(huec, "A", "B");
addAction(suec, "A", "C");
setHelc(helc);
setSelc(selc);
addAction(helc, "A", "D");
addAction(selc, "A", "E");
addAction(new ShowClassCommandWrapper(this), "B", "A");
// addAction(new ShowSubclassesCommandWrapper(this), "B", "B");
// addAction(new ShowSuperclassesCommandWrapper(this), "B", "C");
// addAction(new ShowSubclassesCommandWrapper(this), "B", "B");
// addAction(new ShowSuperclassesCommandWrapper(this), "B", "C");
addAction(new ShowAllClassesCommandWrapper(this, mm, propC), "B", "D");
addAction(new ShowAllIndividualsPropVizCommand(this, mm), "B", "E");
// addAction(new HideClassCommand(this), "B", "E");
// addAction(new HideSubclassesCommandWrapper(this), "B", "F");
addAction(new HideAllPropVizCommand(this), "B", "G");
// addAction(new HideClassesPastRadiusCommandWrapper(propC), "B", "H");
addAction(new ZoomOutCommandWrapper(this), "C", "A");
addAction(new ZoomInCommandWrapper(this), "C", "A");
addAction(new SetOptionsPropVizCommand(this, setupOptionsDialog()),
"D", "A");
addAction(new ExportPropVizCommand(this, propC, mm), "E", "A");
return toolBar;
181
182
183
}
185
186
187
public ShowEdgeLabelsCommand getSelc() {
return selc;
}
189
190
191
192
public void setSelc(ShowEdgeLabelsCommand selc) {
selc = new ShowEdgeLabelsCommand(this);
this.selc = selc;
}
194
195
196
public HideUnboundedEntitiesCommand getHuec() {
return huec;
}
198
199
200
201
202
public void setHuec(HideUnboundedEntitiesCommand huec) {
huec = new HideUnboundedEntitiesCommand(this, propVizGraphComponent
.getController());
this.huec = huec;
}
204
205
206
public ShowUnboundedEntitiesCommand getSuec() {
return suec;
}
208
209
210
211
public void setSuec(ShowUnboundedEntitiesCommand suec) {
suec = new ShowUnboundedEntitiesCommand(this);
this.suec = suec;
}
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/**
* This method creates the options dialog and returns it.
*
* @return The options dialog
*/
protected OptionsDialog setupOptionsDialog() {
DefaultPropVizController c = propVizGraphComponent.getController();
PropVizDotGraphLayoutEngine lE = null;
lE = (PropVizDotGraphLayoutEngine) c.getGraphLayoutEngine();
String s = "Layout Options";
Frame frame = null;
frame = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, this);
OptionsDialog optionDiag = new OptionsDialog(frame);
optionDiag.addOptionsPage(new DotProcessPathPage(), s);
optionDiag.addOptionsPage(new PropVizLayoutDirectionOptionsPage(c), s);
optionDiag.addOptionsPage(new PropVizLayoutSpacingOptionsPage(), s);
// optionDiag.addOptionsPage(new DisplayOptionsPage(), "Display
// Options");
// optionDiag.addOptionsPage(new UIOptionsPage(), "UI Options");
optionDiag.addOptionsPage(new AutoDisplayOptionsPage(), "AutoDisplay");
return optionDiag;
}
236
237
238
239
240
/**
* This method initializes the export formats. Here they are PNG, JPEG and
* DOT.
*/
protected void setupExportFormats() {
helc = new HideEdgeLabelsCommand(this);
this.helc = helc;
B. Implementierung: Quelltext
68
121
122
123
124
125
126
127
128
129
130
}
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
/**
* This little helper only adds the given ExportFormat only if it not
* already in the List the ExportManager holds. This is needed because of
* multiple entries of the same ExportFormats in the UI-Options list.<br>
* In the case that the user selects the OWLViz-PlugIn first and then the
* OWLPropViz it works fine. In the case that at first the OWLPropViz-PlugIn
* is selected, there will be duplicates in the list. :(
*
* @param format
*
The ExportFormat to be added
* @param formats
*
All formats that are already added to the ExportFormatManager
* @return true, if the ExportFormat is added to the ExportFormatManager,
*
false, if it already added.
*/
protected boolean addExportFormat(ExportFormat format,
ExportFormat[] formats) {
boolean retVal = false;
for (int i = 0; i < formats.length && !retVal; i++) {
if (formats[i].getClass() == format.getClass()) {
retVal = true;
} else {
retVal = false;
}
}
if (!retVal) {
ExportFormatManager.addExportFormat(format);
retVal = true;
}
return retVal;
}
280
281
282
283
284
285
286
287
288
289
/**
* This method calls all repaint-methods of all graph components.
*/
protected void forceRepaint() {
Iterator<PropVizGraphComponent> it = getPropertyGraphComponents()
.iterator();
while (it.hasNext()) {
it.next().getPropertyGraphView().repaint();
}
}
291
292
293
294
295
/**
* Sets up the main listeners that are required by the tab, including
* selection listeners, and knowledgebase listeners.
*/
protected void setupListeners(final PropVizGraphComponent gC) {
297
298
299
300
/*
* Listen to node clicks so that if there is a double click we can
* display the Protege Info View
*/
ExportFormat[] formats = ExportFormatManager.getExportFormats();
addExportFormat(new PNGExportFormat(), formats);
addExportFormat(new JPEGExportFormat(), formats);
// addExportFormat(new SVGExportFormat(), formats);
addExportFormat(new DotExportFormat(getOWLModelManager()), formats);
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
gC.addNodeClickedListener(new NodeClickedListener() {
public void nodeClicked(NodeClickedEvent evt) {
dispManager.setSelectedNode(evt.getNode());
if (evt.getNode().getUserObject() instanceof OWLClass) {
selectionModel.setSelectedClass((OWLClass) evt.getNode()
.getUserObject());
}
if (evt.getMouseEvent().getClickCount() == 2) {
if (evt.getNode().getUserObject() instanceof OWLIndividualImpl) {
getOWLWorkspace()
.setSelectedTab(
getOWLWorkspace()
.getWorkspaceTab(
"org.protege.editor.owl.OWLIndividualsTab"));
} else if (((OWLClass) evt.getNode().getUserObject())
.isOWLClass()) {
getOWLWorkspace()
.setSelectedTab(
getOWLWorkspace()
.getWorkspaceTab(
"org.protege.editor.owl.OWLClassesTab"));
}
}
}
});
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/* this listener deals with edge creation */
isPreparing = false;
gC.addNodeClickedListener(new NodeClickedListener() {
public void nodeClicked(NodeClickedEvent evt) {
if (isPreparing) {
popupMenu.setObject(evt.getNode());
isPreparing = false;
popupMenu.setDefaultCursor();
if (popupMenu.isPrepareingClassAssertion()) {
EventManager.getInstance().fireCreateEdgeEvent(
CreateEdgeEventType.CREATE_CLASS_ASSERTION,
getOWLVizPropertyView());
} else if (popupMenu.isPrepareingPropertyAssertion()) {
EventManager.getInstance().fireCreateEdgeEvent(
CreateEdgeEventType.CREATE_PROPERTY_ASSERTION,
getOWLVizPropertyView());
} else if (popupMenu.isPrepareingRestriction()) {
EventManager.getInstance().fireCreateEdgeEvent(
CreateEdgeEventType.ADD_RESTRICTION,
getOWLVizPropertyView());
}
}
}
});
352
353
354
355
356
357
358
359
360
/* this listener deals with contextmenu */
gC.addNodeClickedListener(new NodeClickedListener() {
public void nodeClicked(NodeClickedEvent e) {
dispManager.setSelectedNode(e.getNode());
if (e.getMouseEvent().getButton() == 3) {
if (e.getNode().getUserObject() instanceof OWLClass) {
popupMenu.getAddRestrictionToClass().setVisible(true);
popupMenu.getAssertToClass().setVisible(false);
popupMenu.getAssertPropertyTo().setVisible(false);
B.3. de.tubs.cs.ifis.owlpropviz
69
241
242
243
244
245
246
}
});
373
374
375
376
377
378
379
380
381
382
/* this listener selects the right class in asserted hierarchy view */
gC.addGraphSelectionModelListener(new GraphSelectionModelListener() {
public void selectionChanged(GraphSelectionModelEvent event) {
Object selObj = event.getSource().getSelectedObject();
if (selObj instanceof OWLClass) {
selectionModel.setSelectedClass((OWLClass) selObj);
setSelectedEntity((OWLClass) selObj);
}
}
});
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
/*
* tooltip being added here, switch next 2 commands or simply remove
* layout to reproduce the bug.
*/
getPropertyGraphComponent().getPropertyGraphView().add(tt);
getPropertyGraphComponent().getPropertyGraphView().setLayout(
new BorderLayout());
gC.getPropertyGraphView().addMouseMotionListener(
new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
// We might have to show a tooltip
// if the mouse is over a node.
Point graphPt = getPropertyGraphComponent()
.getPropertyGraphView().pointFromZoomedPoint(
e.getPoint());
// Iterate through the nodes, and find out if
// a node is located at the specified point
Iterator nodeIt = getPropertyGraphComponent()
.getController().getGraphGenerator().getGraph()
.getNodeIterator();
validNode = false;
while (nodeIt.hasNext()) {
final Node node = (Node) nodeIt.next();
OWLClass cls = null;
try {
cls = getOWLModelManager().getOWLClass(
node.getUserObject().toString());
} catch (NullPointerException e1) {
System.out.println(e1);
}
if (node.getShape().contains(graphPt)
&& cls != null) {
/* get the equivalent-description */
Set<OWLDescription> descSet = cls
.getEquivalentClasses(getOWLModelManager()
.getActiveOntology());
StringBuilder sb = new StringBuilder();
} else if (e.getNode().getUserObject() instanceof OWLIndividual) {
popupMenu.getAddRestrictionToClass().setVisible(false);
popupMenu.getAssertToClass().setVisible(true);
popupMenu.getAssertPropertyTo().setVisible(true);
}
popupMenu.show(getPropertyGraphComponent()
.getPropertyGraphView(), e.getMouseEvent()
.getPoint().x, e.getMouseEvent().getPoint().y);
}
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
}
449
450
451
452
453
454
455
456
/**
* Implements the abstract method disposeView of class
* AbstractOWLClassViewComponent.
*/
public void disposeView() {
propertyModel.dispose();
EventManager.getInstance().removeAllListeners();
}
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
/**
* Implements the abstract method initialiseClassView of class
* AbstractOWLClassViewComponent.
*/
public void initialiseClassView() throws Exception {
selectionModel = new OWLVizSelectionModel();
/*
* following 2 lines make the OWLClass Thing to be selected in both
* OWLSelectionModel and AssertedClassView tab
*/
selectionModel.setSelectedClass(getOWLModelManager().getOWLClass(
"Thing"));
setSelectedEntity(getOWLModelManager().getOWLClass("Thing"));
propertyTree = new PropertyTreeSelectionPanel(getOWLEditorKit());
helper = new UIHelper(getOWLEditorKit());
this.setupExportFormats();
this.createOWLPropertyVizUI();
}
477
478
479
480
/**
* Implements the abstract method updateView of class
* AbstractOWLClassViewComponent.
*/
sb.append("<html>");
for (OWLDescription c : descSet) {
sb.append(getOWLEditorKit()
.getModelManager().getRendering(c));
}
sb.append("</html>");
if (!sb.toString().equals("<html></html>")) {
validNode = true;
}
/* set rendered description as tool tip text */
tt
.setTipText(highlighter
.performHtmlHighlighting(sb
.toString()));
tt.setBounds(e.getPoint().x + 8,
e.getPoint().y + 10, sb.toString()
.length() * 2, 50);
}
}
if (validNode) {
tt.setVisible(true);
} else {
tt.setVisible(false);
}
}
});
B. Implementierung: Quelltext
70
361
362
363
364
365
366
367
368
369
370
371
protected OWLClass updateView(OWLClass selectedClass) {
selectionModel.setSelectedClass(selectedClass);
if (getHuec() != null) {
if (!getHuec().isHideState()) {
getSuec().setShowUnboundedState(true);
getHuec().setHideUnboundedState(false);
}
}
if (getHelc() != null) {
if (!getHelc().isState()) {
getSelc().setShowLabelsState(true);
getHelc().setHideLabelsState(false);
}
}
return selectedClass;
}
498
499
public void dragEnter(DropTargetDragEvent dtde) {
}
501
502
public void dragOver(DropTargetDragEvent dtde) {
}
504
505
public void dropActionChanged(DropTargetDragEvent dtde) {
}
507
508
public void dragExit(DropTargetEvent dte) {
}
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
@SuppressWarnings("unchecked")
public void drop(DropTargetDropEvent dtde) {
logger.info("Drop: " + dtde);
if (dtde
.isDataFlavorSupported(OWLObjectDataFlavor.OWL_OBJECT_DATA_FLAVOR)) {
try {
List<OWLObject> objects = null;
objects = (List<OWLObject>) dtde.getTransferable()
.getTransferData(
OWLObjectDataFlavor.OWL_OBJECT_DATA_FLAVOR);
List<OWLClass> clses = new ArrayList<OWLClass>();
for (OWLObject obj : objects) {
if (obj instanceof OWLClass) {
clses.add((OWLClass) obj);
}
}
if (!clses.isEmpty()) {
getPropertyGraphComponent().getVisualisedObjectManager()
.showObjects(clses.toArray());
dtde.dropComplete(true);
}
} catch (UnsupportedFlavorException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
71
539
540
@Override
public Collection<GraphComponent> getAllGraphComponents() {
541
542
}
544
545
546
547
@Override
public Collection<GraphComponent> getGraphComponents() {
return null;
}
549
550
551
public DisplayManager getDispManager() {
return dispManager;
}
553
554
555
private OWLPropVizView getOWLVizPropertyView() {
return this;
}
557
558
559
public boolean isPreparing() {
return isPreparing;
}
561
562
563
public void setPreparing(boolean isPreparing) {
this.isPreparing = isPreparing;
}
565
566
567
public PropVizPopupMenu getPopupMenu() {
return popupMenu;
}
569
570
571
public UIHelper getHelper() {
return helper;
}
573
return null;
}
Listing B.5: OWLPropVizView.java
B.4. de.tubs.cs.ifis.owlpropviz.command
B.4.1. ShowAllIndividualsPropVizCommand.java
1
package de.tubs.cs.ifis.owlpropviz.command;
3
4
import java.awt.event.ActionEvent;
...
6
/**
B.4. de.tubs.cs.ifis.owlpropviz.command
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
* This class exteds the {@link OWLSelectionViewAction} and is responsible for
* displaying individuals
*
* @author skovalev, Axel Schön
*
*/
public class ShowAllIndividualsPropVizCommand extends OWLSelectionViewAction {
Listing B.6: ShowAllIndividualsPropVizCommand
15
16
17
private static final long serialVersionUID = -9135679821616798446L;
protected OWLPropVizView view;
protected OWLModelManager model;
B.4.2. ShowClassCommandWrapper.java
19
20
21
22
23
24
25
26
public ShowAllIndividualsPropVizCommand(OWLPropVizView view,
OWLModelManager model) {
super("Show individuals", new ImageIcon(OWLPropVizView.class
.getResource("image/individuals.png")));
this.putValue(AbstractAction.SHORT_DESCRIPTION, "Show individuals");
this.setEnabled(true);
this.view = view;
this.model = model;
1
package de.tubs.cs.ifis.owlpropviz.command;
3
4
import java.awt.event.ActionEvent;
...
6
public class ShowClassCommandWrapper extends OWLSelectionViewAction {
28
8
9
private static final long serialVersionUID = -2367980535729113687L;
private OWLPropVizView view;
}
30
31
32
33
34
35
36
37
38
39
40
41
public void actionPerformed(ActionEvent e) {
Object[] clses = model.getActiveOntology().getReferencedClasses()
.toArray();
Object[] indis = model.getActiveOntology().getReferencedIndividuals()
.toArray();
repaint(clses, indis);
view.getDispManager().setAllEdges(
view.getPropertyGraphComponent().getPropertyGraphView()
.getGraph().getEdges());
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.SHOWN_ITEMS_CHANGED, view);
}
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
* A dirty repaint, needs to be improved!!! (hides all displayed nodes an
* brings them up again).
*
* @param clses
*
The elements to be displayed.
*/
private void repaint(Object[] clses, Object[] indis) {
for (PropVizGraphComponent graphComp : view
.getPropertyGraphComponents()) {
graphComp.getVisualisedObjectManager().hideObjects(clses);
graphComp.getVisualisedObjectManager().showObjects(indis);
}
}
58
59
60
public void updateState() {
setEnabled(true);
}
62
63
64
public void dispose() {
// TODO Auto-generated method stub
}
66
}
11
12
13
14
15
public ShowClassCommandWrapper(OWLPropVizView view) {
super("Show class", OWLVizIcons.getIcon(OWLVizIcons.SHOW_CLASS_ICON));
this.putValue(AbstractAction.SHORT_DESCRIPTION, "Show class");
this.view = view;
}
17
18
19
public void updateState() {
setEnabled(true);
}
21
22
public void dispose() {
}
24
25
26
27
28
29
30
31
32
33
/**
* Invoked when an action occurs.
*/
public void actionPerformed(ActionEvent e) {
view.getPropertyGraphComponent().getVisualisedObjectManager()
.showObject(view.getSelectionModel().getSelectedClass());
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.SHOWN_ITEMS_CHANGED, view);
}
}
Listing B.7: ShowClassCommandWrapper.java
B.4.3. ShowSubclassesCommandWrapper.java
1
package de.tubs.cs.ifis.owlpropviz.command;
3
4
import java.awt.event.ActionEvent;
...
B. Implementierung: Quelltext
72
7
8
9
10
11
12
13
6
public class ShowSubclassesCommandWrapper extends ShowSubclassesCommand {
8
9
private static final long serialVersionUID = 1L;
private OWLPropVizView view;
public ShowSubclassesCommandWrapper(OWLPropVizView view) {
super(view);
this.putValue(AbstractAction.SHORT_DESCRIPTION, "Show children");
this.view = view;
}
17
18
19
public void updateState() {
setEnabled(true);
}
21
22
public void dispose() {
}
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* Invoked when an action occurs.
*/
public void actionPerformed(ActionEvent e) {
for (PropVizGraphComponent graphComp : view
.getPropertyGraphComponents()) {
String selNode = view.getDispManager().getSelectedNodeName();
Set<Object> entObj = new HashSet<Object>();
Edge[] edges = view.getDispManager().getAllEdges();
for (Edge edge : edges) {
if (edge.getTailNode().getUserObject().toString().equals(
selNode)) {
entObj.add(edge.getHeadNode().getUserObject());
}
}
Object[] entities = entObj.toArray();
41
42
43
44
45
graphComp.getVisualisedObjectManager().showObjects(entities);
}
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.SHOWN_ITEMS_CHANGED, view);
47
public class ShowSuperclassesCommandWrapper extends ShowSuperclassesCommand {
8
9
private static final long serialVersionUID = -7652956811953090623L;
private OWLPropVizView view;
11
12
13
14
public ShowSuperclassesCommandWrapper(OWLPropVizView view) {
super(view);
this.view = view;
}
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public void actionPerformed(ActionEvent e) {
for (PropVizGraphComponent graphComp : view
.getPropertyGraphComponents()) {
String selNode = view.getDispManager().getSelectedNodeName();
Set<Object> entObj = new HashSet<Object>();
Edge[] edges = view.getDispManager().getAllEdges();
for (Edge edge : edges) {
if (edge.getHeadNode().getUserObject().toString().equals(
selNode)) {
entObj.add(edge.getHeadNode().getUserObject());
entObj.add(edge.getTailNode().getUserObject());
}
}
Object[] entities = entObj.toArray();
Object[] curShownObjects = graphComp.getVisualisedObjectManager()
.getVisualisedObjects();
33
34
35
36
37
38
39
41
graphComp.getVisualisedObjectManager().hideAll();
graphComp.getVisualisedObjectManager().showObjects(entities);
graphComp.getVisualisedObjectManager().showObjects(curShownObjects);
}
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.SHOWN_ITEMS_CHANGED, view);
}
}
Listing B.9: ShowSuperclassesCommandWrapper.java
}
}
Listing B.8: ShowSubclassesCommandWrapper.java
B.5. de.tubs.cs.ifis.owlpropviz.ui.options
B.4.4. ShowSuperclassesCommandWrapper.jaB.5.1. AutoDisplayOptionsPage.java
va
73
1
package de.tubs.cs.ifis.owlpropviz.command;
1
package de.tubs.cs.ifis.owlpropviz.ui.options;
3
4
import java.awt.event.ActionEvent;
...
3
4
import java.awt.BorderLayout;
...
B.5. de.tubs.cs.ifis.owlpropviz.ui.options
11
12
13
14
15
6
/**
* Displayed options page
*
* @author skovalev
*
*/
public class AutoDisplayOptionsPage extends OptionsPage {
private static final long serialVersionUID = 2057417172456308065L;
public static boolean isEnabled;
private JCheckBox cbEnable;
private OWLPropVizOptions propVizOptions;
18
19
20
21
22
public AutoDisplayOptionsPage() {
propVizOptions = OptionsFileIO.loadOptions();
setLayout(new BorderLayout(14, 14));
add(createUI());
}
24
25
26
27
28
29
30
31
32
protected JComponent createUI() {
Box box = new Box(BoxLayout.Y_AXIS);
JPanel panelEnable = new JPanel(new FlowLayout());
JLabel labelEnable = new JLabel("Enable autodisplay on load ");
panelEnable.add(labelEnable);
cbEnable = new JCheckBox();
panelEnable.add(cbEnable);
box.add(panelEnable);
box.setBorder(BorderFactory.createTitledBorder("Autodisplay Options"));
34
35
}
37
38
39
40
41
42
@Override
public void applyOptions() {
propVizOptions.setAutodisplayEnabled(cbEnable.isSelected());
OptionsFileIO.saveOptions(propVizOptions);
// System.out.println("DisplayOptionsPage.applyOptions()");
}
44
45
46
@Override
public void updateInterface() {
// TODO Auto-generated method stub
48
}
50
51
52
@Override
public void validateOptions() {
// TODO Auto-generated method stub
return box;
54
56
}
}
Listing B.10: AutoDisplayOptionsPage.java
1
package de.tubs.cs.ifis.owlpropviz.ui.options;
3
4
import java.io.File;
...
6
7
8
9
10
11
12
/**
* This class contains static methods to save and load options files
*
* @author skovalev
*
*/
public class OptionsFileIO {
14
15
16
17
18
19
/** User directory */
public static String userDir = System.getProperty("user.dir");
/** File separator*/
public static String fs = System.getProperty("file.separator");
/** File name of the options file*/
public static String optionsFileName = "PropVizOptions.dat";
21
22
public OptionsFileIO() {
}
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* loads the optionsfile
* @return
*/
public static OWLPropVizOptions loadOptions() {
File optionsFile = new File(userDir + fs + optionsFileName);
OWLPropVizOptions propvizOptions;
try {
FileInputStream fis = new FileInputStream(optionsFile);
ObjectInputStream ois = new ObjectInputStream(fis);
propvizOptions = (OWLPropVizOptions) ois.readObject();
return propvizOptions;
} catch (FileNotFoundException e) {
System.err
.println("PropViz options file not found, new file will be created"
+ " when you apply new options to the PropViz-Plugin");
return new OWLPropVizOptions();
} catch (IOException e) {
System.err
.println("PropViz options file not found, new file will be created"
+ " when you apply new options to the PropViz-Plugin");
return new OWLPropVizOptions();
} catch (ClassNotFoundException e) {
e.printStackTrace();
return new OWLPropVizOptions();
}
51
}
53
54
55
56
57
/**
* saves into an option file
* @param propvizOptions
*/
public static void saveOptions(OWLPropVizOptions propvizOptions) {
B. Implementierung: Quelltext
74
B.5.2. OptionsFileIO.java
6
7
8
9
10
11
12
13
14
15
16
58
59
60
61
62
63
64
65
66
67
68
69
70
try {
File optionsFile = new File(userDir + fs + optionsFileName);
FileOutputStream fos = new FileOutputStream(optionsFile);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(propvizOptions);
oos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Listing B.11: OptionsFileIO.java
B.5.3. OWLPropVizOptions.java
1
package de.tubs.cs.ifis.owlpropviz.ui.options;
3
import java.io.Serializable;
/**
* This class contains variables to store the visualizing options
*
* @author skovalev
*
*/
public class OWLPropVizOptions implements Serializable {
private static final long serialVersionUID = -6991270596640765233L;
/* options */
public boolean isAutodisplayEnabled;
public int countProperties;
public int countIsA;
public int countDisjointWith;
public int countEquivalentTo;
75
20
21
22
23
24
25
26
public OWLPropVizOptions() {
isAutodisplayEnabled = true;
countProperties = 40;
countIsA = 40;
countDisjointWith = 10;
countEquivalentTo = 10;
}
28
29
30
public boolean isAutodisplayEnabled() {
return isAutodisplayEnabled;
}
32
33
34
public void setAutodisplayEnabled(boolean isAutodisplayEnabled) {
this.isAutodisplayEnabled = isAutodisplayEnabled;
}
36
public int getCountProperties() {
}
40
41
42
public void setCountProperties(int countProperties) {
this.countProperties = countProperties;
}
44
45
46
public int getCountIsA() {
return countIsA;
}
48
49
50
public void setCountIsA(int countIsA) {
this.countIsA = countIsA;
}
52
53
54
public int getCountDisjointWith() {
return countDisjointWith;
}
56
57
58
public void setCountDisjointWith(int countDisjointWith) {
this.countDisjointWith = countDisjointWith;
}
60
61
62
public int getCountEquivalentTo() {
return countEquivalentTo;
}
64
65
66
public void setCountEquivalentTo(int countEquivalentTo) {
this.countEquivalentTo = countEquivalentTo;
}
68
return countProperties;
}
Listing B.12: OWLPropVizOptions.java
B.6. de.tubs.cs.ifis.owlpropviz.util
B.6.1. DisplayManager.java
1
package de.tubs.cs.ifis.owlpropviz.util;
3
4
import java.util.Collection;
...
6
7
8
9
10
11
/**
*
* @author skovalev
*
*/
public class DisplayManager {
B.6. de.tubs.cs.ifis.owlpropviz.util
5
6
7
8
9
10
11
12
13
14
15
16
17
18
37
38
protected OWLPropVizView view;
15
16
17
18
private
private
private
private
20
private OWLPropVizOptions propVizOptions;
22
23
private Set<OWLAxiom> axioms;
private Map<Object, HideState> visibilityMap;
25
26
27
public void setCurrentAxioms(Set<OWLAxiom> axioms) {
this.axioms = axioms;
}
29
30
31
public Set<OWLAxiom> getCurrentAxioms() {
return axioms;
}
33
34
35
36
37
public DisplayManager(OWLPropVizView view) {
this.view = view;
EventManager.getInstance().addCreateEdgeListener(new EdgeCreator());
visibilityMap = new HashMap<Object, HideState>();
}
39
40
41
42
43
44
45
46
47
48
/**
* Visualizes the class hiararchy of given radius
*
* @param radius
*/
public void autoDisplayPastRadius(int radius) {
HashSet<OWLPropertyAxiom> props = new HashSet<OWLPropertyAxiom>();
props.clear();
props.addAll(view.getOWLEditorKit().getModelManager()
.getActiveOntology().getObjectPropertyAxioms());
Edge[] allEdges;
Node[] allNodes;
Node selectedNode;
Object[] unboundedObjs;
50
51
52
53
axioms = new HashSet<OWLAxiom>();
OWLOntology o = view.getOWLModelManager().getActiveOntology();
OWLPropertyAxiomExtractor extr = new OWLPropertyAxiomExtractorImpl(view
.getOWLModelManager());
55
56
57
58
59
60
61
62
axioms.addAll(extr.getSubClassAxioms(o));
// axioms.addAll(extr.getDisjointClassesAxioms(o)); todo disabale if >
// 40
axioms.addAll(extr.getEquivalentClassesAxioms(o));
axioms.addAll(view.getOWLModelManager().getActiveOntology()
.getIndividualAxioms());
axioms.addAll(view.getOWLModelManager().getActiveOntology()
.getObjectPropertyAxioms());
64
65
66
67
68
69
70
71
for (OWLPropertyAxiom prop : props) {
Set<OWLObjectProperty> propsInSig = prop
.getObjectPropertiesInSignature();
for (OWLObjectProperty pro : propsInSig) {
Set<OWLAxiom> s = extr.getPropertyRelatedAxioms(pro, o);
// if (s.size() <= 40) add esle not
axioms.addAll(extr.getOWLObjectPropertyWrapperAxioms(s));
}
73
75
76
}
//
78
79
Collection<OWLClass> clses = new HashSet<OWLClass>();
Collection<OWLIndividual> indis = null;
clses.add(view.getOWLModelManager().getOWLClass("Thing"));
clses.addAll((getClassesPastRadius(clses, radius)));
81
82
//
//
84
85
86
//
indis = view.getOWLModelManager().getActiveOntology()
.getReferencedIndividuals();
LinkedList<OWLEntity> clsesIndisList = new LinkedList<OWLEntity>();
clsesIndisList.addAll(clses);
clsesIndisList.addAll(indis); indis disable now
88
Object[] clsesIndis = clsesIndisList.toArray();
90
91
92
93
94
95
96
97
view.addAndClearAxioms(axioms);
for (PropVizGraphComponent graphComp : view
.getPropertyGraphComponents()) {
graphComp.getVisualisedObjectManager().hideAll();
graphComp.getVisualisedObjectManager().showObjects(clsesIndis);
}
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.SHOWN_ITEMS_CHANGED, view);
99
101
102
103
104
105
106
107
108
}
/**
* initializes the onlogy
*/
public void initializeCompleteOntology() {
HashSet<OWLPropertyAxiom> props = new HashSet<OWLPropertyAxiom>();
props.clear();
props.addAll(view.getOWLEditorKit().getModelManager()
.getActiveOntology().getObjectPropertyAxioms());
110
111
112
113
114
115
116
117
118
119
120
121
Set<OWLAxiom> axioms = new HashSet<OWLAxiom>();
OWLOntology o = view.getOWLModelManager().getActiveOntology();
OWLPropertyAxiomExtractor extr = new OWLPropertyAxiomExtractorImpl(view
.getOWLModelManager());
//
axioms.addAll(extr.getSubClassAxioms(o));
// axioms.addAll(extr.getDisjointClassesAxioms(o));
axioms.addAll(extr.getEquivalentClassesAxioms(o));
axioms.addAll(view.getOWLModelManager().getActiveOntology()
.getIndividualAxioms());
axioms.addAll(view.getOWLModelManager().getActiveOntology()
.getObjectPropertyAxioms());
123
124
125
126
127
128
129
130
for (OWLPropertyAxiom prop : props) {
Set<OWLObjectProperty> propsInSig = prop
.getObjectPropertiesInSignature();
for (OWLObjectProperty pro : propsInSig) {
Set<OWLAxiom> s = extr.getPropertyRelatedAxioms(pro, o);
// if (s.size() <= 40) add esle not
axioms.addAll(extr.getOWLObjectPropertyWrapperAxioms(s));
}
132
}
B. Implementierung: Quelltext
76
13
134
135
136
137
138
139
140
Collection<OWLClass> clses = new HashSet<OWLClass>();
Collection<OWLIndividual> indis = null;
clses = view.getOWLModelManager().getActiveOntology()
.getReferencedClasses();
clses.add(view.getOWLModelManager().getOWLClass("Thing"));
indis = view.getOWLModelManager().getActiveOntology()
.getReferencedIndividuals();
LinkedList<OWLEntity> clsesIndisList = new LinkedList<OWLEntity>();
clsesIndisList.addAll(clses);
clsesIndisList.addAll(indis);
146
Object[] clsesIndis = clsesIndisList.toArray();
148
149
150
151
152
153
view.addAndClearAxioms(axioms);
for (PropVizGraphComponent graphComp : view
.getPropertyGraphComponents()) {
graphComp.getVisualisedObjectManager().hideAll();
graphComp.getVisualisedObjectManager().showObjects(clsesIndis);
}
155
156
157
158
allEdges = view.getPropertyGraphComponent().getPropertyGraphView()
.getGraph().getEdges();
allNodes = view.getPropertyGraphComponent().getPropertyGraphView()
.getGraph().getNodes();
160
161
162
163
164
165
166
167
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.INITIALIZE_GRAPH_ITEMS, view);
// create visibility map
Node[] nodes = view.getPropertyGraphComponent().getPropertyGraphView()
.getGraph().getNodes();
for (Node node : nodes) {
visibilityMap.put(node.getUserObject(), new HideState());
}
169
170
171
172
173
174
175
for (PropVizGraphComponent graphComp : view
.getPropertyGraphComponents()) {
graphComp.getVisualisedObjectManager().hideAll();
}
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.SHOWN_ITEMS_CHANGED, view);
}
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/**
* gets classes past given radius. using class hierarchy for it
*
* @param collection
* @param radius
* @return
*/
public Collection<OWLClass> getClassesPastRadius(
Collection<OWLClass> collection, int radius) {
Collection<OWLClass> result = new HashSet<OWLClass>();
if (radius >= 0) {
radius--;
for (OWLClass sub : collection) {
result.add(sub);
result.addAll(getClassesPastRadius(view.getOWLModelManager()
.getOWLHierarchyManager()
.getOWLClassHierarchyProvider().getChildren(sub),
radius));
196
197
198
199
}
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/**
* gets all children nodes recursively
*
* @param selNode
*
current node
* @param edges
*
the edge map
* @return
*/
public HashSet<Object> getDomainRecursively(String selNode, Edge[] edges) {
HashSet<Object> result = new HashSet<Object>();
for (Edge edge : view.getDispManager().getAllEdges()) {
if (edge.getTailNode().getUserObject().toString().equals(
selNode.toString())) {
result.add(edge.getHeadNode().getUserObject());
result.addAll(getDomainRecursively(edge.getHeadNode()
.getUserObject().toString(), edges));
}
}
return result;
}
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/**
* counts hidden children
*
* @param userObject
*
current node
* @return
*/
public int getChildrenHiddenCount(Object userObject) {
int count = 0;
for (Edge edge : view.getDispManager().getAllEdges()) {
if (edge.getTailNode().getUserObject().equals(userObject)) {
Object childObj = edge.getHeadNode().getUserObject();
if (visibilityMap.get(childObj).isHidden()) {
count++;
}
}
}
return count;
}
243
244
245
246
247
248
249
250
251
252
/**
* counts hidden parents
*
* @param userObject
*
current node
* @return
*/
public int getParentsHiddenCount(Object userObject) {
int count = 0;
for (Edge edge : view.getDispManager().getAllEdges()) {
}
}
return result;
B.6. de.tubs.cs.ifis.owlpropviz.util
77
142
143
144
193
194
}
263
264
265
266
267
268
269
270
271
272
273
/**
* resets the visibilitymap
*
* @param nodeIt
*/
public void resetVisibilityMap(Iterator nodeIt) {
Set<Object> allObj = visibilityMap.keySet();
for (Object obj : allObj) {
visibilityMap.put(obj, new HideState());
}
}
275
276
277
278
279
280
281
public void setObjectHiddenState(Object obj, boolean state) {
if (!visibilityMap.containsKey(obj)) { // = true, if we have just added
// a new node
visibilityMap.put(obj, new HideState(false));
}
visibilityMap.get(obj).setHidden(state);
}
283
284
285
public Edge[] getAllEdges() {
return allEdges;
}
287
288
289
public void setAllEdges(Edge[] allEdges) {
this.allEdges = allEdges;
}
291
292
293
public Node[] getAllNodes() {
return allNodes;
}
295
296
297
public String getSelectedNodeName() {
return selectedNode.getUserObject().toString();
}
299
300
301
public Node getSelectedNode() {
return selectedNode;
}
303
304
305
public void setSelectedNode(Node selectedNode) {
this.selectedNode = selectedNode;
}
307
308
309
public Object[] getUnboundedObjs() {
return unboundedObjs;
}
311
312
public void setUnboundedObjs(Object[] curObjs) {
this.unboundedObjs = curObjs;
if (edge.getHeadNode().getUserObject().equals(userObject)) {
Object childObj = edge.getTailNode().getUserObject();
if (visibilityMap.get(childObj).isHidden()) {
count++;
}
}
}
return count;
313
}
315
316
317
public Map<Object, HideState> getVisibilityMap() {
return visibilityMap;
}
319
320
321
322
323
324
public OWLPropVizOptions getPropVizOptions() {
if (propVizOptions == null) {
propVizOptions = OptionsFileIO.loadOptions();
}
return propVizOptions;
}
326
}
Listing B.13: DisplayManager.java
B.6.2. HideState.java
1
package de.tubs.cs.ifis.owlpropviz.util;
3
4
5
6
7
8
9
/**
* Support Class for creating Visibility map. Stores hidden state
*
* @author skovalev
*
*/
public class HideState {
11
private boolean isHidden;
13
14
15
public HideState() {
isHidden = true;
}
17
18
19
public HideState(boolean state) {
isHidden = state;
}
21
22
23
public void setHidden(boolean isHidden) {
this.isHidden = isHidden;
}
25
26
27
public boolean isHidden() {
return isHidden;
}
29
}
Listing B.14: HideState.java
B. Implementierung: Quelltext
78
253
254
255
256
257
258
259
260
261
B.6.3. HtmlHighlighter.java
1
package de.tubs.cs.ifis.owlpropviz.util;
3
4
import java.awt.Color;
import java.util.StringTokenizer;
6
import org.protege.editor.owl.OWLEditorKit;
8
9
10
11
12
13
14
15
16
/**
* This class creates html code to display the right color for owl keywords
* (and, or, some...). Generated strings are being displayed in the
* PropVizGraphInfo html pane
*
* @author skovalev
*
*/
public class HtmlHighlighter {
/** current owl editor kit */
private OWLEditorKit owlEditorKit;
/** tokenizer */
private StringTokenizer tokenizer;
23
24
25
26
/** constructor, sets the owl editor kit */
public HtmlHighlighter(OWLEditorKit owlEditorKit) {
this.owlEditorKit = owlEditorKit;
}
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* Tokenizes input text and scans for keywords. each keyword gets it’s color
* and invokes the <code>setColor()</code> method
*
* @param htmlText
*
text to be tokenized and colored later on
* @return colored text (html format)
*/
public String performHtmlHighlighting(String htmlText) {
String result = "";
tokenizer = new StringTokenizer(htmlText);
while (tokenizer.hasMoreTokens()) {
String curToken = tokenizer.nextToken();
Color color = owlEditorKit.getWorkspace().getKeyWordColorMap().get(
curToken);
if (color == null) {
color = Color.BLACK;
}
result = result + setColor(curToken, color) + " ";
}
return result;
}
51
52
53
54
55
56
57
/**
* Creates <-font color="RRGGBB"> <-/font> tag around the word (without -)
*
* @param text
*
to be colored
* @param color
*
java’s representation of color, stored in
74
*
{@link org.protege.editor.owl.ui.renderer.KeywordColourMap}
* @return <-font color="RRGGBB">text<-/font></code>
*/
private String setColor(String text, Color color) {
String result;
if (text.contains("html>") || color.equals(Color.BLACK)) {
result = text;
} else {
result = "<font color=\"" + Integer.toHexString(color.getRed())
+ Integer.toHexString(color.getGreen())
+ Integer.toHexString(color.getBlue()) + "\">" + text
+ "</font>";
}
return result;
}
}
Listing B.15: HtmlHighlighter.java
B.6.4. PropVizPopupMenu.java
1
3
4
5
6
7
8
9
10
11
12
13
14
15
package de.tubs.cs.ifis.owlpropviz.util;
import java.awt.Cursor;
...
/**
* This class creates the popup-menu in OWLPropViz.
*
* Whole menu structure is base on get*Name*() methods, which initialize menu
* items on load and return the item on demand, so for example you can
* modificate the item’s visibility
*
* @author skovalev
*
*/
public class PropVizPopupMenu extends JPopupMenu {
17
private static final long serialVersionUID = 7119117593848321645L;
19
20
21
22
23
24
25
26
27
28
29
30
/** current view */
private OWLPropVizView view;
/** stores current editor kit */
private OWLEditorKit owlEditorKit;
/** stores curent owl model manager */
private OWLModelManager owlModelManager;
/** stores current selection model */
private OWLVizSelectionModel selectionModel;
/** stores graph components */
private Collection<PropVizGraphComponent> graphComponents;
/** stores cursor */
private Cursor cursor;
32
/** Menu: Show */
B.6. de.tubs.cs.ifis.owlpropviz.util
79
18
19
20
21
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
private JMenu show;
/** Submenu: Show -> Domain */
private JMenuItem showDomain;
/** Submenu: Show -> Range */
private JMenuItem showRange;
/** Submenu: Show -> Domain Recursively */
private JMenuItem showDomainRecursively;
41
42
43
44
45
46
47
48
/** Menu: Hide */
private JMenu hide;
/** Submenu: Hide -> Class */
private JMenuItem hideClass;
/** Submenu: Hide -> Domain */
private JMenuItem hideDomain;
/** Submenu: Hide -> Range */
private JMenuItem hideRange;
50
51
52
53
54
55
/** Menu: Add subclass */
private JMenuItem addSubclass;
/** Menu: Add sibling */
private JMenuItem addSibling;
/** Menu: Delete class */
private JMenuItem deleteClass;
57
58
59
60
/** Menu: Add restriction to... */
private JMenuItem addRestrictionToClass;
/** Indicates the status whether the user is currently preparing restriction */
private boolean prepareingRestriction;
62
63
64
65
/** Menu: Assert to class... */
private JMenuItem assertToClass;
/** Indicates whether the user is currently preparing assertion */
private boolean prepareingClassAssertion;
67
68
69
70
/** Menu: Assert property to... */
private JMenuItem assertPropertyTo;
/** Indicates whether the user is currently preparing property assertion */
private boolean prepareingPropertyAssertion;
72
73
74
75
/** Object node (subject - predicate - objects) */
private Node object;
/** Subject node (subject - predicate - objects) */
private Node subject;
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
* Constructor. Initializes a cursor and creates the popupmenu
*
* @param view
*/
public PropVizPopupMenu(OWLPropVizView view) {
super();
this.view = view;
this.owlEditorKit = view.getOWLEditorKit();
this.owlModelManager = view.getOWLModelManager();
this.selectionModel = view.getSelectionModel();
this.graphComponents = view.getAllPropertyGraphComponents();
cursor = new Cursor(Cursor.CROSSHAIR_CURSOR);
createPopupMenu();
}
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/**
* creates the popupmenu
*/
public void createPopupMenu() {
/* menu structure here */
add(getShow());
add(getHide());
addSeparator();
add(getAddSubclass());
add(getAddSibling());
add(getDeleteClass());
addSeparator();
add(getAddRestrictionToClass());
add(getAssertToClass());
add(getAssertPropertyTo());
}
110
111
112
113
114
115
116
117
118
public JMenu getShow() {
if (show == null) {
show = new JMenu("Show");
show.add(getShowDomain());
show.add(getShowDomainRecursively());
show.add(getShowRange());
}
return show;
}
120
121
122
123
124
125
126
127
public JMenuItem getShowDomain() {
if (showDomain == null) {
showDomain = new JMenuItem("Domain");
showDomain
.addActionListener(new ShowSubclassesCommandWrapper(view));
}
return showDomain;
}
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
public JMenuItem getShowDomainRecursively() {
if (showDomainRecursively == null) {
showDomainRecursively = new JMenuItem("Domain recursively");
showDomainRecursively.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String selNode = view.getDispManager()
.getSelectedNodeName();
HashSet<Object> domain = view.getDispManager()
.getDomainRecursively(selNode,
view.getDispManager().getAllEdges());
view.getPropertyGraphComponent()
.getVisualisedObjectManager().showObjects(
domain.toArray());
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.SHOWN_ITEMS_CHANGED, view);
}
});
}
return showDomainRecursively;
}
150
151
152
public JMenuItem getShowRange() {
if (showRange == null) {
showRange = new JMenuItem("Range");
B. Implementierung: Quelltext
80
33
34
35
36
37
38
39
}
159
160
161
162
163
164
165
166
167
public JMenu getHide() {
if (hide == null) {
hide = new JMenu("Hide");
hide.add(getHideDomain());
hide.add(getHideNode());
hide.add(getHideRange());
}
return hide;
}
169
170
171
172
173
174
175
176
public JMenuItem getHideDomain() {
if (hideDomain == null) {
hideDomain = new JMenuItem("Domain");
hideDomain.addActionListener(new HideSubclassesCommandWrapper(
(OWLPropVizView) view));
}
return hideDomain;
}
178
179
180
181
182
183
184
185
186
187
188
189
190
public JMenuItem getHideNode() {
if (hideClass == null) {
hideClass = new JMenuItem("Node");
hideClass.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
view.getPropertyGraphComponent()
.getVisualisedObjectManager().hideObject(
view.getDispManager().getSelectedNode()
.getUserObject());
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.SHOWN_ITEMS_CHANGED, view);
}
});
showRange
.addActionListener(new ShowSuperclassesCommandWrapper(view));
}
return showRange;
81
192
193
194
}
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
public JMenuItem getHideRange() {
if (hideRange == null) {
hideRange = new JMenuItem("Range");
hideRange.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (PropVizGraphComponent graphComp : view
.getPropertyGraphComponents()) {
String selNode = view.getDispManager()
.getSelectedNodeName();
Set<Object> entObj = new HashSet<Object>();
Edge[] edges = view.getDispManager().getAllEdges();
for (Edge edge : edges) {
if (edge.getHeadNode().getUserObject().toString()
.equals(selNode)) {
entObj.add(edge.getTailNode().getUserObject());
}
}
}
return hideClass;
213
214
215
216
217
218
219
220
221
222
223
}
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
public JMenuItem getAddSubclass() {
if (addSubclass == null) {
addSubclass = new JMenuItem("Add Subclass");
addSubclass.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
OWLDescription cls = selectionModel.getSelectedClass();
if (cls == null) {
return;
}
OWLEntityCreationSet<OWLClass> creationSet = owlEditorKit
.getWorkspace().createOWLClass();
if (creationSet == null) {
return;
}
List<OWLOntologyChange> changes = new ArrayList<OWLOntologyChange>();
changes.addAll(creationSet.getOntologyChanges());
OWLDataFactory df = owlEditorKit.getModelManager()
.getOWLDataFactory();
if (!df.getOWLThing().equals(cls)) {
OWLSubClassAxiom ax = df.getOWLSubClassAxiom(
creationSet.getOWLEntity(), cls);
changes.add(new AddAxiom(owlEditorKit.getModelManager()
.getActiveOntology(), ax));
// OWLPropertyAxiom ax1 = df.getOWL
// changes.add(new
// AddAxiom(owlEditorKit.getModelManager().getActiveOntology(),
// ax1));
}
owlEditorKit.getModelManager().applyChanges(changes);
selectionModel.setSelectedClass(creationSet.getOWLEntity());
/* VISUALISE NEW CLASS */
Set<OWLAxiom> axioms = view.getDispManager()
.getCurrentAxioms();
axioms.addAll(owlEditorKit.getModelManager()
.getActiveOntology().getAxioms(
creationSet.getOWLEntity()));
view.addAndClearAxioms(axioms);
263
264
265
266
267
268
269
270
271
272
Object[] entities = entObj.toArray();
graphComp.getVisualisedObjectManager().hideObjects(
entities);
}
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.SHOWN_ITEMS_CHANGED, view);
}
});
}
return hideRange;
HashSet<OWLClass> obj = new HashSet<OWLClass>();
obj.add(creationSet.getOWLEntity());
view.getPropertyGraphComponent()
.getVisualisedObjectManager().showObjects(
obj.toArray());
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.CLASS_ADDED, view);
}
});
}
B.6. de.tubs.cs.ifis.owlpropviz.util
153
154
155
156
157
}
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
public JMenuItem getAddSibling() {
if (addSibling == null) {
addSibling = new JMenuItem("Add Sibling");
addSibling.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
OWLClass cls = selectionModel.getSelectedClass();
if (cls == null) {
// Shouldn’t really get here, because the
// action should be disabled
return;
}
// We need to apply the changes in the active ontology
OWLEntityCreationSet<OWLClass> creationSet = owlEditorKit
.getWorkspace().createOWLClass();
if (creationSet == null) {
return;
}
return addSubclass;
294
295
OWLModelManager mngr = owlEditorKit.getModelManager();
OWLDataFactory df = mngr.getOWLDataFactory();
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
// Combine the changes that are required to create the
// OWLClass, with the
// changes that are required to make it a sibling class.
List<OWLOntologyChange> changes = new ArrayList<OWLOntologyChange>();
changes.addAll(creationSet.getOntologyChanges());
for (OWLDescription par : mngr.getOWLHierarchyManager()
.getOWLClassHierarchyProvider().getParents(cls)) {
OWLAxiom ax = df.getOWLSubClassAxiom(creationSet
.getOWLEntity(), par);
changes.add(new AddAxiom(mngr.getActiveOntology(), ax));
}
mngr.applyChanges(changes);
// Select the new class
selectionModel.setSelectedClass(creationSet.getOWLEntity());
/* VISUALISE NEW CLASS */
Set<OWLAxiom> axioms = view.getDispManager()
.getCurrentAxioms();
axioms.addAll(owlEditorKit.getModelManager()
.getActiveOntology().getAxioms(
creationSet.getOWLEntity()));
view.addAndClearAxioms(axioms);
319
320
321
322
323
324
325
326
327
328
329
330
HashSet<OWLClass> obj = new HashSet<OWLClass>();
obj.add(creationSet.getOWLEntity());
view.getPropertyGraphComponent()
.getVisualisedObjectManager().showObjects(
obj.toArray());
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.CLASS_ADDED, view);
}
332
public JMenuItem getDeleteClass() {
}
});
}
return addSibling;
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
if (deleteClass == null) {
deleteClass = new JMenuItem("Delete Node");
deleteClass.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Node selectedNode = view.getDispManager().getSelectedNode();
if (selectedNode.getUserObject() instanceof OWLClass) {
Set<OWLDescription> descSet = ((OWLClass) selectedNode
.getUserObject()).getEquivalentClasses(view
.getOWLModelManager().getActiveOntology());
boolean equivalentClass = true;
if (descSet.isEmpty()) {
equivalentClass = false;
}
final OWLObjectHierarchyDeleter<OWLClass> hierarchyDeleter
= new OWLObjectHierarchyDeleter<OWLClass>(
owlEditorKit, owlEditorKit.getOWLModelManager()
.getOWLHierarchyManager()
.getOWLClassHierarchyProvider(),
new OWLEntitySetProvider<OWLClass>() {
public Set<OWLClass> getEntities() {
HashSet<OWLClass> set = new HashSet<OWLClass>();
set.add(selectionModel
.getSelectedClass());
return set;
}
}, "classes");
hierarchyDeleter.performDeletion();
if (equivalentClass) {
EventManager
.getInstance()
.fireGraphChangeEvent(
GraphChangeEventType.EQUIVALENT_CLASS_REMOVED,
view);
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.SHOWN_ITEMS_CHANGED,
view);
} else {
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.NORMAL_CLASS_REMOVED,
view);
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.SHOWN_ITEMS_CHANGED,
view);
}
Collection<OWLClass> clses = null;
clses = owlModelManager.getActiveOntology()
.getReferencedClasses();
clses.add(view.getOWLModelManager()
.getOWLClass("Thing")); // THING NOW DISPLAYED
for (PropVizGraphComponent graphComp : graphComponents) {
graphComp.getVisualisedObjectManager().hideAll();
graphComp.getVisualisedObjectManager().showObjects(
clses.toArray());
}
view.getSelectionModel().setSelectedClass(
view.getOWLModelManager().getOWLClass("Thing"));
} else if (selectedNode.getUserObject() instanceof OWLIndividual) {
OWLEntityRemover remover = new OWLEntityRemover(
owlEditorKit.getModelManager()
.getOWLOntologyManager(), owlEditorKit
B. Implementierung: Quelltext
82
273
274
393
394
395
396
397
398
399
400
401
402
403
404
405
.getModelManager().getOntologies());
OWLIndividual indi = (OWLIndividual) selectedNode
.getUserObject();
indi.accept(remover);
owlEditorKit.getModelManager().applyChanges(
remover.getChanges());
view.getPropertyGraphComponent()
.getVisualisedObjectManager().hideObject(indi);
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.INDIVIDUAL_REMOVED, view);
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.SHOWN_ITEMS_CHANGED, view);
}
}
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
public JMenuItem getAddRestrictionToClass() {
if (addRestrictionToClass == null) {
addRestrictionToClass = new JMenuItem("Add restriction to class...");
addRestrictionToClass.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Node subjectNode = view.getDispManager().getSelectedNode();
setSubject(subjectNode);
setPrepareingRestriction(true);
setPrepareingClassAssertion(false);
setPrepareingPropertyAssertion(false);
setCrosshairCursor();
view.setPreparing(true);
}
});
}
return addRestrictionToClass;
}
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
public JMenuItem getAssertToClass() {
if (assertToClass == null) {
assertToClass = new JMenuItem("Assert to class...");
assertToClass.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Node subjectNode = view.getDispManager().getSelectedNode();
setSubject(subjectNode);
setPrepareingRestriction(false);
setPrepareingClassAssertion(true);
setPrepareingPropertyAssertion(false);
setCrosshairCursor();
view.setPreparing(true);
}
});
}
return assertToClass;
}
449
450
451
452
public JMenuItem getAssertPropertyTo() {
if (assertPropertyTo == null) {
assertPropertyTo = new JMenuItem("Add property to object...");
assertPropertyTo.addActionListener(new ActionListener() {
}
});
}
return deleteClass;
}
467
468
469
public void setObject(Node object) {
this.object = object;
}
471
472
473
public Node getObject() {
return object;
}
475
476
477
public void setSubject(Node subject) {
this.subject = subject;
}
479
480
481
public Node getSubject() {
return subject;
}
483
484
485
public void setPrepareingRestriction(boolean prepareingRestriction) {
this.prepareingRestriction = prepareingRestriction;
}
487
488
489
public boolean isPrepareingRestriction() {
return prepareingRestriction;
}
491
492
493
public void setPrepareingClassAssertion(boolean prepareingClassAssertion) {
this.prepareingClassAssertion = prepareingClassAssertion;
}
495
496
497
public boolean isPrepareingClassAssertion() {
return prepareingClassAssertion;
}
499
500
501
502
public void setPrepareingPropertyAssertion(
boolean prepareingPropertyAssertion) {
this.prepareingPropertyAssertion = prepareingPropertyAssertion;
}
504
505
506
public boolean isPrepareingPropertyAssertion() {
return prepareingPropertyAssertion;
}
508
509
510
511
public void setCrosshairCursor() {
cursor = new Cursor(Cursor.CROSSHAIR_CURSOR);
view.setCursor(cursor);
}
public void actionPerformed(ActionEvent e) {
Node subjectNode = view.getDispManager().getSelectedNode();
setSubject(subjectNode);
setPrepareingRestriction(false);
setPrepareingClassAssertion(false);
setPrepareingPropertyAssertion(true);
setCrosshairCursor();
view.setPreparing(true);
}
});
}
return assertPropertyTo;
B.6. de.tubs.cs.ifis.owlpropviz.util
83
407
408
409
410
411
453
454
455
456
457
458
459
460
461
462
463
464
465
518
public void setDefaultCursor() {
cursor = new Cursor(Cursor.DEFAULT_CURSOR);
view.setCursor(cursor);
}
}
Listing B.16: PropVizPopupMenu.java
B.7. de.tubs.cs.ifis.owlpropviz.util.event
B.7.1. EventManager.java
1
2
3
4
/**
*
*/
package de.tubs.cs.ifis.owlpropviz.util.event;
6
7
import java.util.ArrayList;
...
9
10
11
12
13
14
15
/**
* This Class serves as communication interface between the plugins
*
* @author skovalev
*
*/
public class EventManager {
17
18
19
static EventManager instance = null;
public static List<GraphChangeListener> graphChangelisteners;
public static List<CreateEdgeListener> createEdgeListeners;
21
22
private EventManager() {
}
24
25
26
27
28
29
30
31
32
33
34
/**
* @return the only instance of the class
*/
public static EventManager getInstance() {
if (instance == null) {
instance = new EventManager();
graphChangelisteners = new ArrayList<GraphChangeListener>();
createEdgeListeners = new ArrayList<CreateEdgeListener>();
}
return instance;
}
36
37
38
39
40
41
42
/* Graph Change */
/**
* Adds a GraphChangeListener
*/
public void addGraphChangeListener(GraphChangeListener listener) {
graphChangelisteners.add(listener);
}
44
45
46
47
48
49
50
51
52
53
54
55
/**
* Fires a GraphChangeEvent
* @param type event type
* @param view current OWLPropVizView
*/
public void fireGraphChangeEvent(GraphChangeEventType type,
OWLPropVizView view) {
GraphChangeEvent event = new GraphChangeEvent(type, view);
for (GraphChangeListener listener : graphChangelisteners) {
listener.handleChange(event);
}
}
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
* Fires a GraphChangeEvent
* @param type event type
* @param view current OWLPropVizView
* @param propertyName property added or deleted
*/
public void fireGraphChangeEvent(GraphChangeEventType type,
OWLPropVizView view, String propertyName) {
GraphChangeEvent event = new GraphChangeEvent(type, view, propertyName);
for (GraphChangeListener listener : graphChangelisteners) {
listener.handleChange(event);
}
}
71
72
73
public void removeGraphChangeListeners() {
graphChangelisteners.clear();
}
75
76
77
78
79
80
81
/* Add Edge */
/**
* adds CreateEdgeListener
*/
public void addCreateEdgeListener(CreateEdgeListener listener) {
createEdgeListeners.add(listener);
}
83
84
85
86
87
88
89
90
91
92
93
94
/**
* Fires CreateEdgeEvent
* @param type type of edge
* @param view current OWLPropVizView
*/
public void fireCreateEdgeEvent(CreateEdgeEventType type,
OWLPropVizView view) {
CreateEdgeEvent event = new CreateEdgeEvent(type, view);
for (CreateEdgeListener listener : createEdgeListeners) {
listener.handleChange(event);
}
}
B. Implementierung: Quelltext
84
513
514
515
516
96
97
98
public void removeCreateEdgeListeners() {
createEdgeListeners.clear();
}
100
101
102
103
104
105
106
107
109
/* global */
/**
* removes all registered listeners
*/
public void removeAllListeners() {
removeCreateEdgeListeners();
removeGraphChangeListeners();
}
}
31
32
33
public boolean isType(CreateEdgeEventType type) {
return this.type.equals(type);
}
35
return view;
}
Listing B.18: CreateEdgeEvent.java
}
Listing B.17: EventManager.java
B.8.1. CreateEdgeEvent.java
1
package de.tubs.cs.ifis.owlpropviz.util.event.createedge;
3
import de.tubs.cs.ifis.owlpropviz.OWLPropVizView;
/**
* Event for edge creation
*
* @author skovalev
*
*/
public class CreateEdgeEvent {
B.8.2. CreateEdgeEventType.java
1
package de.tubs.cs.ifis.owlpropviz.util.event.createedge;
3
4
5
6
7
8
9
/**
* define edge creation types here
*
* @author skovalev
*
*/
public enum CreateEdgeEventType {
11
ADD_RESTRICTION,
13
CREATE_CLASS_ASSERTION,
15
CREATE_PROPERTY_ASSERTION
17
}
Listing B.19: CreateEdgeEventType.java
B.8.3. CreateEdgeListener.java
13
14
15
16
/** Type of event */
private CreateEdgeEventType type;
/** current OWLPropVizView */
private OWLPropVizView view;
1
package de.tubs.cs.ifis.owlpropviz.util.event.createedge;
18
19
20
21
public CreateEdgeEvent(CreateEdgeEventType type, OWLPropVizView view) {
this.type = type;
this.view = view;
}
3
4
5
6
7
8
/**
*
* @author skovalev
*
*/
public interface CreateEdgeListener {
23
24
25
public CreateEdgeEventType getType() {
return type;
}
10
85
12
27
public OWLPropVizView getView() {
public void handleChange(CreateEdgeEvent event);
}
B.8. de.tubs.cs.ifis.owlpropviz.util.event.createedge
B.8. de.tubs.cs.ifis.owlpropviz.util.event.createedge
5
6
7
8
9
10
11
28
29
B.8.4. EdgeCreator.java
1
package de.tubs.cs.ifis.owlpropviz.util.event.createedge;
3
4
import java.awt.BorderLayout;
...
6
7
8
9
10
11
12
/**
* Creates specified edges after fired event
*
* @author skovalev
*
*/
public class EdgeCreator implements CreateEdgeListener {
14
15
16
17
private
private
private
private
OWLPropVizView view;
JPanel selectionPanel;
OWLModelManagerTree<OWLObjectProperty> tree;
JTextField newNameField;
19
20
21
22
23
private
private
private
private
private
OWLModelManager man;
OWLDataFactory dataFactory;
OWLOntology ont;
String base;
UIHelper helper;
25
26
public EdgeCreator() {
}
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@Override
public void handleChange(CreateEdgeEvent event) {
view = event.getView();
man = view.getOWLEditorKit().getModelManager();
dataFactory = man.getOWLDataFactory();
ont = man.getActiveOntology();
base = man.getActiveOntology().getURI().toString();
helper = view.getHelper();
Node subjectNode = view.getPopupMenu().getSubject();
Node objectNode = view.getPopupMenu().getObject();
if (subjectNode.equals(objectNode)) {
return;
}
if (event.isType(CreateEdgeEventType.ADD_RESTRICTION)) {
if ((objectNode.getUserObject() instanceof OWLIndividual)
|| (subjectNode.getUserObject() instanceof OWLIndividual)) {
return;
}
int retVal = helper.showDialog("Select Object Properties",
getSelectionPanel());
if (retVal == JOptionPane.OK_OPTION) {
// create axiom
String propertyName;
if (tree.getSelectedOWLObject() != null) {
52
53
54
55
56
57
58
propertyName = tree.getSelectedOWLObject().toString();
} else {
propertyName = getNewNameField().getText();
}
OWLObjectProperty property = dataFactory
.getOWLObjectProperty(URI.create(base + "#"
+ propertyName));
60
61
62
63
OWLClass object = (OWLClass) objectNode.getUserObject();
OWLDescription desc = dataFactory.getOWLObjectSomeRestriction(
property, object);
OWLClass subject = (OWLClass) subjectNode.getUserObject();
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
OWLSubClassAxiom ax = dataFactory.getOWLSubClassAxiom(subject,
desc);
AddAxiom addAx = new AddAxiom(ont, ax);
man.applyChange(addAx);
System.out.println("Restriction Added");
// visualize
Object[] obj = view.getPropertyGraphComponent()
.getVisualisedObjectManager().getVisualisedObjects();
view.getPropertyGraphComponent().getVisualisedObjectManager()
.hideAll();
OWLPropertyAxiomExtractorImpl extr = new OWLPropertyAxiomExtractorImpl(
view.getOWLModelManager());
view.getPropertyGraphComponent().getVisualisedObjectManager()
.hideAll(); // muss alle hiden, weil wenn man nur 2 obj
// mit neuen kante anzeigt, verschwinden die
// anderen
Set<OWLAxiom> s = extr.getPropertyRelatedAxioms(property, ont);
view.getDispManager().getCurrentAxioms().addAll(
extr.getOWLObjectPropertyWrapperAxioms(s));
view
.addAndClearAxioms(view.getDispManager()
.getCurrentAxioms());
view.getPropertyGraphComponent().getVisualisedObjectManager()
.showObjects(obj);
// notify graphinfo
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.EDGE_ADDED, view, propertyName);
}
getTree().dispose();
} else if (event.isType(CreateEdgeEventType.CREATE_CLASS_ASSERTION)) {
if (objectNode.getUserObject() instanceof OWLIndividual) {
return;
}
// create axiom
OWLIndividual indi = dataFactory.getOWLIndividual(URI.create(base
+ "#" + subjectNode.getUserObject().toString()));
OWLDescription desc = (OWLClass) objectNode.getUserObject();
OWLClassAssertionAxiom assertion = dataFactory
.getOWLClassAssertionAxiom(indi, desc);
AddAxiom addAxiomChange = new AddAxiom(ont, assertion);
man.applyChange(addAxiomChange);
// visualize
Object[] obj = view.getPropertyGraphComponent()
.getVisualisedObjectManager().getVisualisedObjects(); // speichern
// damit
// nur
// 1
B. Implementierung: Quelltext
86
Listing B.20: CreateEdgeListener.java
// dazu
// kommt
view.getDispManager().getCurrentAxioms().add(assertion); // neue
// axiom
// wird
// hinzugefuegt
view.addAndClearAxioms(view.getDispManager().getCurrentAxioms());
view.getPropertyGraphComponent().getVisualisedObjectManager()
.hideAll();
view.getPropertyGraphComponent().getVisualisedObjectManager()
.showObjects(obj);
// notify graphinfo
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.EDGE_ADDED, view, "instance-of");
} else if (event.isType(CreateEdgeEventType.CREATE_PROPERTY_ASSERTION)) {
if ((objectNode.getUserObject() instanceof OWLClass)
|| (subjectNode.getUserObject() instanceof OWLClass)) {
return;
}
int retVal = helper.showDialog("Select Object Properties",
getSelectionPanel());
if (retVal == JOptionPane.OK_OPTION) {
// create axiom
String propertyName;
if (tree.getSelectedOWLObject() != null) {
propertyName = tree.getSelectedOWLObject().toString();
} else {
propertyName = getNewNameField().getText();
}
OWLObjectProperty property = dataFactory
.getOWLObjectProperty(URI.create(base + "#"
+ propertyName));
OWLIndividual subject = (OWLIndividual) subjectNode
.getUserObject();
OWLIndividual object = (OWLIndividual) objectNode
.getUserObject();
OWLObjectPropertyAssertionAxiom assertion = dataFactory
.getOWLObjectPropertyAssertionAxiom(subject, property,
object);
AddAxiom addAxiomChange = new AddAxiom(ont, assertion);
view.getOWLEditorKit().getModelManager().applyChange(
addAxiomChange);
// visualize
Object[] obj = view.getPropertyGraphComponent()
.getVisualisedObjectManager().getVisualisedObjects();
view.getPropertyGraphComponent().getVisualisedObjectManager()
.hideAll(); // muss alle hiden, weil wenn man nur 2 obj
// mit neuen kante anzeigt, verschwinden die
// anderen
view.getDispManager().getCurrentAxioms().add(assertion);
view
.addAndClearAxioms(view.getDispManager()
.getCurrentAxioms());
view.getPropertyGraphComponent().getVisualisedObjectManager()
.showObjects(obj);
// notify graphinfo
EventManager.getInstance().fireGraphChangeEvent(
GraphChangeEventType.EDGE_ADDED, view, propertyName);
}
getTree().dispose();
172
173
}
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
private JPanel getSelectionPanel() {
selectionPanel = new JPanel();
selectionPanel.setLayout(new BorderLayout());
selectionPanel.add(new JLabel("Select existing"), BorderLayout.NORTH);
tree = new OWLModelManagerTree<OWLObjectProperty>(view
.getOWLEditorKit(), view.getOWLEditorKit().getModelManager()
.getOWLHierarchyManager()
.getOWLObjectPropertyHierarchyProvider());
tree.setDragEnabled(false);
tree.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION); // credits an Andreas
// :)
JScrollPane scrollPane = new JScrollPane(tree);
selectionPanel.add(scrollPane, BorderLayout.CENTER);
JPanel createPanel = new JPanel();
createPanel.setLayout(new BorderLayout());
createPanel.add(new JLabel("or create a new one:"), BorderLayout.NORTH);
newNameField = new JTextField();
createPanel.add(newNameField, BorderLayout.CENTER);
selectionPanel.add(createPanel, BorderLayout.SOUTH);
return selectionPanel;
}
198
199
200
public OWLModelManagerTree<OWLObjectProperty> getTree() {
return tree;
}
202
203
204
public JTextField getNewNameField() {
return newNameField;
}
206
}
}
Listing B.21: EdgeCreator.java
B.9. de.tubs.cs.ifis.owlpropviz.util.event.graphchange
B.9.1. GraphChangeEvent.java
1
package de.tubs.cs.ifis.owlpropviz.util.event.graphchange;
3
import de.tubs.cs.ifis.owlpropviz.OWLPropVizView;
5
6
/**
* Event indicating a graph change
B.9. de.tubs.cs.ifis.owlpropviz.util.event.graphchange
87
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
*
* @author skovalev
*
*/
public class GraphChangeEvent {
B.9.2. GraphChangeEventType.java
1
package de.tubs.cs.ifis.owlpropviz.util.event.graphchange;
3
4
5
6
7
8
9
/**
* Specify type of graph changes here
*
* @author skovalev
*
*/
public enum GraphChangeEventType {
13
14
15
private GraphChangeEventType type;
private OWLPropVizView view;
private String propertyName;
17
18
19
20
public GraphChangeEvent(GraphChangeEventType type, OWLPropVizView view) {
this.type = type;
this.view = view;
}
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
*
* @param type
*
type of event
* @param view
*
curren OWLPropVizView
* @param propertyName
*
name of new property added
*/
public GraphChangeEvent(GraphChangeEventType type, OWLPropVizView view,
String propertyName) {
this.type = type;
this.view = view;
this.propertyName = propertyName;
}
38
39
40
public GraphChangeEventType getType() {
return type;
}
42
43
44
public OWLPropVizView getView() {
return view;
}
B.9.3. GraphChangeListener.java
46
47
48
public boolean isType(GraphChangeEventType type) {
return this.type.equals(type);
}
1
package de.tubs.cs.ifis.owlpropviz.util.event.graphchange;
50
51
52
public String getPropertyName() {
return propertyName;
}
3
4
5
6
7
8
9
/**
* Interface of GraphChange listeners
*
* @author skovalev
*
*/
public interface GraphChangeListener {
54
}
Listing B.22: GraphChangeEvent.java
11
SHOWN_ITEMS_CHANGED,
13
EDGE_ADDED,
15
CLASS_ADDED,
17
NORMAL_CLASS_REMOVED,
19
EQUIVALENT_CLASS_REMOVED,
21
INDIVIDUAL_REMOVED,
23
INITIALIZE_GRAPH_ITEMS
25
}
Listing B.23: GraphChangeEventType.java
11
13
public void handleChange(GraphChangeEvent event);
}
Listing B.24: GraphChangeListener.java
B. Implementierung: Quelltext
88
7
8
9
10
11
C Inhalt der CD-ROM
Der gedruckten Fassung dieser Bachelorarbeit ist eine CD-ROM beigelegt, deren
Inhalte im Folgenden aufgelistet werden. Die PDF-Version der Arbeit liegt in dem
Hauptverzeichnis dieser CD.
C.1. Ordner Quellenangaben_Seiten
In diesem Verzeichnis befinden sich die in dieser Bachelorarbeit referenzierten Internetquellen. Sowohl PDF-Dateien als auch HTML-Seiten sind entsprechend derer
Abkürzungen im Literaturverzeichnis abgespeichert.
C.2. Ordner Ontologien
Die bei der Evalution benutzte Ontologie uni.owl sowie eine etwas größere für die
Testzwecke benutzte pizza.owl sind in diesem Verzeichnis untergebracht.
C.3. Ordner Protege_Distribution
In diesem Ordner ist eine ausführbare Version der Protégé-Software mit dem OWLPropViz-Plugin enhalten. Diese Distribution ist plattformunabhängig und kann mit
Hilfe der entsprechenden ausführbaren Datei gestartet werden.
C.4. Ordner Workspace_Eclipse
Hier befindet sich der Eclipse-Workspace, in welchem die Plugin-Entwicklung stattfand. Darin sind die Quelltexte von Protégé und von OWLPropViz sowie EclipseProjekteinstellungen gespeichert. In einem getrennen Verzeichnis befindet sich die
Java-Dokumentation für das Plugin.
89
Herunterladen