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