FernUniversität in Hagen Fakultät für Mathematik und Informatik Lehrgebiet Programmiersysteme Prof. Dr. Friedrich Steimann Ein Eclipse-Plug-in zur statistischen Analyse der Verwendung von Typen in Java-Programmen Master-Arbeit im Studiengang Master of Computer Science vorgelegt von Gerd Eichhorn Marco-Polo-Weg 30 70439 Stuttgart Matrikelnummer 7117795 [email protected] Betreuer: Prof. Dr. F. Steimann 26. Februar 2008 Danksagungen Mein besonderer Dank gilt Herrn Prof. Dr. Friedrich Steimann für die Übernahme und Betreuung dieser Arbeit und die zahlreichen weiterführenden Hinweise. Außerdem geht mein Dank an Herrn Dr. Eugen Grycko vom Fachbereich Mathematik für die großzügige Unterstützung bei der statistischen Analyse und Auswertung. Danke auch an Frau Dr. Ursula Scheben und Frau Dr. Daniela Keller für die Durchsichten, Korrekturen und Verbesserungsvorschläge dieser Arbeit. Inhaltsverzeichnis 1 Inhaltsangabe 1 2 Einleitung 2.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Beitrag der Arbeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3 Aufbau der Arbeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 2 3 3 Grundlagen 3.1 Einleitung . . . . . . . . . . . . . . . . . 3.2 Metrik - Number of Declarations (NOD) 3.3 Abstract Syntax Tree . . . . . . . . . . . 3.4 Besucher-Entwurfsmuster . . . . . . . . . . . . . . . . . . . . . . . . . 4 Statistik 4.1 Motivation . . . . . . . . . . . . . . . . . . . . . 4.2 Grundlagen der Bayes-Statistik . . . . . . . . . 4.3 Grundbegriffe der Wahrscheinlichkeitsrechnung: lungsfunktion und Dichtefunktion . . . . . . . . 4.4 Wahl der Verteilungsfunktion . . . . . . . . . . 4.4.1 Zipf/Estoup-Verteilung . . . . . . . . . . 4.4.2 Yule-Verteilung . . . . . . . . . . . . . . 4.5 Statistisches Analyse-Programm . . . . . . . . . 4.6 Rekursionsalgorithmus . . . . . . . . . . . . . . 4.7 Untersuchung der Beispielprogramme . . . . . . 4.7.1 Beispielprogramme . . . . . . . . . . . . 4.7.2 Untersuchung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zufallsvariable, . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vertei. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 4 4 7 10 12 12 12 14 16 16 17 18 20 22 22 23 5 Ergebnisse 5.1 Verteilungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2 Abweichungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3 Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 25 29 29 6 Typ-Analyse-Plug-in (TAP) 6.1 Anforderungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.1.1 Type Diagram View für die grafische Ausgabe der NOD- und NOIMetrik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 31 i 33 Inhaltsverzeichnis 6.1.2 6.2 6.3 6.4 6.5 6.6 6.7 6.8 Type List View für die tabellarische Ausgabe der NOD- und NOIMetrik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.1.3 Detail Diagram View für die grafische Ausgabe der Abhängigkeiten eines bestimmten Typs . . . . . . . . . . . . . . . . . . . . . . . . 6.1.4 Detail List View für die tabellarische Ausgabe der Abhängigkeiten eines bestimmten Typs . . . . . . . . . . . . . . . . . . . . . . . . Praktische Umsetzung . . . . . . . . . . . . . . . . . . . . . . . . . . . . Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Material und Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . Eclipse-IDE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . JFreeChart-Bibliothek . . . . . . . . . . . . . . . . . . . . . . . . . . . . JCommon-Bibliothek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Launch4j-Applikationswrapper . . . . . . . . . . . . . . . . . . . . . . . . 7 Schlußbetrachtungen 7.1 Zusammenfassung . . . . 7.2 Einsatzmöglichkeiten von 7.3 Ausblick . . . . . . . . . 7.4 Fazit . . . . . . . . . . . . . . . TAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 36 37 38 40 40 40 41 41 41 42 42 42 42 43 Abbildungsverzeichnis 44 Tabellenverzeichnis 45 Listingverzeichnis 46 Literaturverzeichnis 47 Inhalt der beiliegenden DVD 48 A Anhang A.1 Metrik - Number of Instantiations (NOI) . . . . . . . . A.2 Ermittlung der Typ-Instanziierungen . . . . . . . . . . A.3 Typ-Analyse-Plug-in (TAP) . . . . . . . . . . . . . . . A.3.1 Installation des Plug-ins . . . . . . . . . . . . . A.3.2 Anwendung des Typ-Analyse-Plug-ins . . . . . A.3.3 File List View für die Ausgabe aller analysierten A.3.4 Paketstruktur des Plug-ins . . . . . . . . . . . . A.3.5 Erstellen der JAR-Datei . . . . . . . . . . . . . A.3.6 Hinzufügen der JFreeChart-Bibliotheken . . . . A.3.7 Ausgaben des Typ-Analyse-Plug-ins . . . . . . . A.3.8 Start der Typ-Analyse . . . . . . . . . . . . . . A.4 Statistisches Auswerteprogramm . . . . . . . . . . . . . A.4.1 Listing des Programms . . . . . . . . . . . . . . A.4.2 Erstellen des Eclipse-Projekts . . . . . . . . . . ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Java-Quellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 49 50 52 52 52 53 54 55 57 58 59 60 60 70 Inhaltsverzeichnis A.4.3 Erstellen der JAR-Datei . . . . . . . . . . . . . . . . . . . . . . . A.4.4 Erstellen der EXE-Datei . . . . . . . . . . . . . . . . . . . . . . . Erklärung 70 74 78 iii 1 Inhaltsangabe Die Qualität von Softwareprodukten gewinnt immer mehr an Bedeutung. Doch wie erhält man ein qualitativ hochwertiges Produkt und vor allem, wie kann man die Qualität messen? Um die Softwarequalität zu quantifizieren, werden in der Softwaretechnik Metriken eingesetzt. Eine spezielle Metrik, die in dieser Arbeit vorgestellt wird, zählt z.B. für jeden in einem Programm vorkommenden Typ, die Häufigkeit seiner Verwendung. Diese Quantifizierung kann z.B. dazu benutzt werden, um häufig verwendete Typen zu optimieren bzw. wenig verwendete Type zu eliminieren. Für den Programmierer ist es jedoch schwer, bei großen Programmen den Überblick über die verwendeten Typen zu behalten. Deshalb soll eine Visualisierung der Verteilung der Verwendung entwickelt werden, um den Programmierer bei der Analyse zu unterstützen. Im Rahmen dieser Arbeit wurde dazu ein Typ-Analyse-Plug-in (TAP) entwickelt. Es stellt die Verwendung der verschiedenen Typen in Java-Programmen dar. Die Ausgabe erfolgt in Tabellen und Histogrammen. Außerdem kann man mit dem TAP-Plug-in zählen, wie oft innerhalb eines Java-Programms Instanzen von einem bestimmten Typ erzeugt werden. Ein weiterer Teil der Arbeit beschäftigt sich mit der statistischen Analyse der zuvor gewonnenen Ergebnisse. Es soll geklärt werden, ob sich die Verteilung der in JavaProgrammen benutzten Typen an die, aus der Statistik bereits bekannten, Verteilungsfunktionen anlehnt. Hierzu wird auf die Methoden der sogenannten Bayesschen Stati” stik“ zurückgegriffen. Als Entwicklungsplattform wurde Eclipse, eine Open-Source-Entwicklungsumgebung, ausgewählt, die vor allem einen guten Erweiterungsmechanismus basierend auf Plugins bietet. Das statistische Analyseprogramm wurde ebenfalls mit Hilfe von Eclipse als Erweiterung des JDT’s als eigenständige Java-Applikation realisiert. 1 2 Einleitung 2.1 Motivation Bei der Programmierung kann es am Ende des Entwicklungszyklus hilfreich sein, sich einen Überblick über die verwendeten Typen zu verschaffen. Dies kann aus zwei Gründen sinnvoll sein. Es können Typen identifiziert werden, die sehr häufig bzw. sehr selten im Programm vorkommen. Daraus kann man Optimierungen für das Programm ableiten, indem man häufig verwendete Typen optimiert, z.B. die Methoden einer Klasse bezüglich Laufzeit verbessert. Andererseits können auch z.B. einfache, selten benutzte, selbstgeschriebene Typen durch Typen aus Bibliotheken ersetzt werden, wenn diese die benötigte Funktionalität bereitstellen. Als Beispiel sei hier die aus der Programmiersprache C++ bekannte Standard Template Library“ (STL) genannt, die z.B. fertige Klassen für Li” sten, Vektoren, Stacks usw. enthält. Alternativ sei hier die für die Programmiersprache Java bekannte Chart-Bibliothek JFreeChart“ genannt, mit deren Hilfe man vorhandene ” Messdaten recht einfach zur grafischen Ausgabe, z.B. in Kuchencharts aufbereiten kann. Durch den extensiven Einsatz sind diese Bibliotheken meist frei von Fehlern und zudem auf die Laufzeit hin optimiert. Diese Standardtypen“ können so Fehler vermeiden und ” auch die Lesbarkeit von Programmen verbessern. 2.2 Beitrag der Arbeit Die vorliegende Arbeit hat zum einen das Ziel, bestehende Java-Programme auf die verwendeten Typen hin zu untersuchen. Zu diesem Zweck wird eine spezielle Metrik eingeführt, die für jeden in einem Java-Programm vorkommenden Typ die Häufigkeit seiner Verwendung mißt. Zum anderen soll mit einer allgemeinen statistischen Auswertung der vorangegangenen Analyse-Ergebnisse eine Aussage über die Verteilung der Typen gemacht werden. Hierzu sollen die Ergebnisse aus den vorangegangenen Untersuchungen von Java-Projekten weiterverwendet werden. Die hierbei gewonnenen Daten können auch als Eingabe für weitere Programme/Plugins dienen. So könnten z.B. automatisierte Refactorings auf Java-Quellen durchgeführt werden, die als Eingabe die Daten der Typ-Analyse benötigen. Aufbauend auf diesen beiden Anforderungen wurde für den ersten Teil ein Plug-in entwickelt, welches bestehende Java-Programme untersucht. Die Ergebnisse werden sowohl tabellarisch als auch grafisch dargestellt. Als Nebenprodukt wird zusätzlich gezählt, wie oft innerhalb eines Java-Programms Instanzen von einem bestimmten Typ erzeugt werden. Dieser Schritt wurde für zukünftige Arbeiten mit implementiert, da es nur einen geringen Zusatzaufwand bedeutet. 2 KAPITEL 2. EINLEITUNG Für den zweiten, statistischen Teil wurde ein Java-Programm entwickelt, welches eine Aussage über die, aus der Literatur bekannten Verteilungen erlaubt. Hierzu wird auf die sog. Bayessche Statistik“ zurückgegriffen, die eine hinreichend genaue Interpretation der ” Ergebnisse erlaubt. 2.3 Aufbau der Arbeit Das Kapitel 3 beschreibt die Metrik, mit der die Häufigkeit der Verwendung von Typen gemessen wird und die im weiteren in dem Typ-Analyse-Plug-in umgesetzt wird. Das Typ-Analyse-Plug-in wird in Kapitel 6 behandelt. Anschließend erfolgt die Beschreibung des statistischen Teils der Arbeit in Kapitel 4. Die Untersuchung der einzelnen Beispielprogramme und deren Ergebnisse fließen dann in das Kapitel 5 ein. Dort wird außerdem die Eingruppierung der Typ-Analyse-Ergebnisse der untersuchten Programme dargestellt. Die Ergebnisse, in Form von Häufigkeitszählungen von Typen in JavaProgrammen, bilden jeweils eine bestimmte Typ-Verteilung. Diese Typ-Verteilungen werden mit den aus der Mathematik bekannten Verteilungen verglichen bzw. bewertet. Die beiden Kapitel Diskussion“ und Schlußbetrachtungen“ schließen die Arbeit ab. ” ” 3 3 Grundlagen 3.1 Einleitung Das vorliegende Typ-Analyse-Plug-in sucht in einer Menge von Java-Quellen nach den in ihnen deklarierten Typen sowie nach anderen Deklarationselementen, in denen Typen verwendet werden. Um deklarierte Typen zu finden, werden die Java-Quellen nach Schnittstellen- und Klassendeklarationen durchsucht sowie nach Deklarationen von Aufzählungstypen. Ein Typ wird als verwendet betrachtet, wenn er in Parameterdeklarationen von Konstruktoren oder Methoden, als Rückgabetyp von Methoden oder in Variablendeklarationen vorkommt. Unter dem Begriff Variablendeklaration werden Attributdeklarationen und lokal in Methoden vorkommende Variablendeklarationen zusammengefasst. Neben der Suche nach Deklarationselementen, in denen ein Typ A verwendet wird, wird auch nach Programmstellen gesucht, in denen Instanzen von A erzeugt werden. Vom Analyse-Plug-in werden zwei Metriken implementiert, eine, die für einen Typ A die Anzahl seiner Verwendungen angibt und eine weitere, die die Anzahl der Instanziierungen von A mißt. Die erste Metik wird im Folgenden vorgestellt, die zweite im Anhang A.1. 3.2 Metrik - Number of Declarations (NOD) Die Metrik, die die Verwendung eines Typs zählt, basiert auf einer statischen TypAnalyse von Java-Programmen und kann folgendermaßen angegeben werden: N OD(A) = | { d | d ∈ D ∧ A ∈ d } | wobei A ein Typ ist und D die Menge aller betrachteten Deklarationselemente. Zu D gehören in Konstruktoren und Methodenden vorkommende Parameterdeklarationen, Deklarationen des Rückgabetyps von Methoden sowie Variablendeklarationen wie in Kapitel 3.1 eingeführt. • Falls d eine Parameter- oder Variablendeklaration repräsentiert, bedeutet A ∈ d, daß A in d als der statische Typ des Parameters bzw. der Variablen festgelegt wird. • Falls d die Deklaration des Rückgabetyps einer Methode repräsentiert, bedeutet A ∈ d, daß A als Rückgabetyp angegeben ist. 4 KAPITEL 3. GRUNDLAGEN Der folgende Algorithmus in Form von Pseudocode beschreibt, wie die Anzahl der Verwendungen innerhalb des Analyse-Plug-ins bestimmt wird: 1 2 3 4 f or each declarationElement of actualProject do { Type := getTypeOfDeclarationElement ; CountingArray [ Type ]++; } Listing 3.1: Algorithmus zur Berechnung der Metrik NOD Unter die bei der Analyse erfassten Typen fallen nicht nur die im untersuchten Quelltext deklarierten Schnittstellen-, Klassen- und Aufzählungstypen, sondern sämtliche, im Quelltext i.o. Sinne verwendeten Typen, also auch Basisdatentypen wie z.B. int und float etc. oder vordefinierte Typen wie java.lang.String etc. Die im untersuchten Quelltext deklarierten Typen besitzen nur insofern eine Sonderstellung, als daß Sie später bei der Auswertung der Analyseergebnisse auch dann aufgeführt werden, wenn sie im untersuchten Quelltext an keiner Stelle verwendet werden. Ihre Verwendung wird dann mit 0 angegeben. Der Sinn dieser Sonderstellung wird weiter unten erläutert. Die folgende Aufzählung gibt noch einmal alle Typen an, die in die Analyse eingehen: • Rückgabetypen von Methoden • Typen von Methoden- und Konstruktorparametern • Typen von lokal in Methoden deklarierten Variablen • Typen in Attributdeklarationen • Klassentypen, die im untersuchten Quelltext deklariert werden • Schnittstellentypen, die im untersuchten Quelltext deklariert werden • Aufzählungstypen, die im untersuchten Quelltext deklariert werden. Um diese Typen zu bestimmen, müssen bei der Analyse also folgende Javakonstrukte untersucht werden: • Konstruktordeklarationen • Methodendeklarationen • Deklarationen von Variablen innerhalb von Methodenrümpfen • Attributdeklarationen • Klassendeklarationen • Schnittstellendeklarationen 5 KAPITEL 3. GRUNDLAGEN • Deklarationen von Aufzählungstypen Das folgende Beispiel dient zur Verdeutlichung des Verfahrens: 1 2 3 public c l a s s SquareRoot { private double squareRoot ; public double getSquareRoot ( ) { return squareRoot ; } 4 public void calcSquareRoot ( double d ) { String s ; squareRoot = Math . sqrt ( d ) ; s = "Die Quadratwurzel von <" + d + "> ist: " + squareRoot ; System . out . println ( s . toString ( ) ) ; } 5 6 7 8 9 10 11 } Listing 3.2: Beispielcode mit relevanten Deklarationselementen Das Beispiel hat das folgende Ergebnis: Typ Anzahl der Verwendungen des Typs void 1 double 3 java.lang.String 1 SquareRoot 0 Tabelle 3.1: Auswertung des Beispielcodes Eine Besonderheit ist hier anzumerken: Der letzte Typ der Tabelle, in Form der Klasse SquareRoot“, wird hier aufgelistet, hat aber eine Anzahl von null Verwendungen, da ” der Typ zwar deklariert, nicht aber in anderen Deklarationselementen verwendet wird. Dies könnte bei einer späteren Untersuchung eines Programms ein Hinweis auf eine nicht benötigte Klasse sein. 6 KAPITEL 3. GRUNDLAGEN 3.3 Abstract Syntax Tree Für die Untersuchung von Java-Quelldateien wird von Eclipse der sogenannte AST bereitgestellt. Der AST ist mit dem Document Object Model“ (DOM) einer XML-Datei ” vergleichbar. Ähnlich dem DOM erlaubt der AST den Baum zu modifizieren. Die Änderungen in einem AST stellen dann anschließend die Modifikationen im Quellcode dar. Der AST stellt dem Programmierer also Informationen über eine Java-Quelle zur Verfügung. Beim Aufbau eines ASTs wird ein Quelltext in seine logischen Bestandteile zerlegt. Das bedeutet, daß jeder Knoten im AST ein bestimmtes Element der Sprache Java repräsentiert. Dies können z.B. Methodendeklarationen, Typdeklarationen, Variablendeklarationen usw. sein. Ein Anwenderprogramm kann nun diesen Syntaxbaum rekursiv durchlaufen, um die Informationen, an denen es interessiert ist, zu erhalten. So kann z.B. bei der Untersuchung einer Methodendeklaration der Rückgabetyp und der bzw. die Typ(en) der Methodenparameter festgestellt werden. Der AST erlaubt außerdem die Modifikation von Quellcode. Dies geschieht durch das Zurückschreiben des modifizierten ASTs. Im Quellcode ist dieses Feature beispielsweise für automatische Refactorings und das Eclipse-Werkzeug Quick Fix“ von großem Nutzen. Für eine praktische Ein” führung bietet sich der Artikel AST [5] aus dem Artikelbereich der Eclipse-Community www.eclipse.org/articles an. Die Abbildung 3.1 zeigt den Workflow“ des ASTs. ” Abbildung 3.1: AST-Workflow • 1. Zu parsende Java-Quelle • 2. Aufruf des Parsers mit Hilfe der Klasse org.eclipse.jdt.core.dom.ASTParser“ ” • 3. Der Abstract Syntax Tree“ als Resultat von Schritt 2 ” 7 KAPITEL 3. GRUNDLAGEN • 4a. Manipulation des ASTs entweder direkt, z.B. Ändern von Knoteneigenschaften • 4b. Modifikationen in einem separaten sogenannten Protokoll“, d.h. mit dem Auf” ruf von ASTRewrite“ wird eine Kopie des ASTs erzeugt, wo anschließend die ” Modifikationen durchgeführt werden • 5. Zurückschreiben der Änderungen • 6. IDocument, ist ein sogenannter Wrapper“ für den Quellcode und wird bei Punkt ” 1. und 5. verwendet Als weiteres Hilfsmittel wird ein sogenannter AST-View“ von der Eclipse-Community ” zur Verfügung gestellt. Mit dessen Hilfe kann eine Java-Quelle geladen und anschließend direkt untersucht werden. Als Ergebnis wird der resultierende AST grafisch dargestellt und kann auf bestimmte Typen/Konstrukte hin durchsucht werden. Dies ist auch eine gute Möglichkeit seine eigenen Programme, die den AST von Java-Quellen benutzen oder modifizieren, auf Korrektheit hin zu überprüfen. Der oben stehende Beispielcode aus Listing 3.2 führt zu dem AST in Abbildung 3.2. Einige beispielhaft aufgeklappte Knoten zeigen die zugehörigen Details. Z.B. zeigt die TypeDeclaration“ den Name des Typs in der untersuchten Quelle, in diesem Fall ” der Name der Klasse SquareRoot“ im Knoten SimpleName“. Ein sogenannter Sim” ” ” pleName“ ist irgendein String einer Java-Quelle, die kein Schlüsselwort, ein Boolesches Literal (true oder false) oder das null“-Literal ist. In den weiteren Knoten innerhalb ” des Rumpfes BODY DECLARATIONS“ folgen die FieldDeclaration“ der Memberva” ” riable squareRoot“. Anschließend folgt die Funktion getSquareRoot“ innerhalb einer ” ” MethodDeclaration“. Hier ist der Modifier“ in Form von public“ zu erkennen, der ” ” ” Rückgabetyp double“ in einem Knotentyp namens PrimitiveType“. Primitive Typen ” ” sind alle Basistypen, die die Sprache Java zur Verfügung stellt, z.B. int, long, double, usw. 8 KAPITEL 3. GRUNDLAGEN Abbildung 3.2: AST-View 9 KAPITEL 3. GRUNDLAGEN 3.4 Besucher-Entwurfsmuster Um Informationen von einer Java-Quelle, wie z.B. den Rückgabetyp einer Methode, zu erhalten, muß der sogenannte Abstract Syntax Tree“ (AST), siehe hierzu Kapitel 3.3, der ” Java-Quelle analysiert werden. Dazu muß zunächst der AST erzeugt werden, um anschließend durchlaufen zu werden. Das aus der Literatur bekannte Besucher-Entwurfsmuster (engl.: Visitor-Designpattern) ist im Zusammenhang mit dem Durchlaufen eines ASTs von Bedeutung. Einen guten Einstieg bietet der englischsprachige Artikel von Wikipedia unter der URL http://en.wikipedia.org/wiki/Visitor_pattern an oder das Standardwerk Design Pattern“ von E. Gamma et al. [15]. Sollen die vom Eclipse-Framework ” angebotenen Funktionen für die Untersuchung eines ASTs verwendet werden, so müssen für das Durchlaufen eines ASTs vom Anwender sogenannte Besucherfunktionen, unter Verwendung der Besucherklasse ASTVisitor“ realisiert werden. Es müssen dann die ” Funktionen zum Auffinden von Informationen überschrieben werden, an denen der Anwender interessiert ist. Das folgende Beispiel zeigt den grundsätzlichen Aufbau für das Aufsammeln von Daten, an denen ein Anwenderprogramm interessiert ist. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public c l a s s TypeDeclarationVisitor extends ASTVisitor { ... public boolean visit ( VariableDeclarationFragment node ) { ... } // Ueberschreiben der Besucherfunktion mit dem Parameter // " MethodDeclaration ", um an Infos zu diesem Typ zu gelangen public boolean visit ( MethodDeclaration node ) { // Resolve binding IMethodBinding methodBinding = node . resolveBinding ( ) ; // Retrieve parameter types ITypeBinding [ ] paramTypes = methodBinding . getParameterTypes ( ) ; ... // Retrieve return type ITypeBinding returnType = methodBinding . getReturnType ( ) ; ... return super . visit ( node ) ; } public boolean visit ( EnhancedForStatement node ) { ... } public boolean visit ( TypeDeclaration node ) { ... } } Listing 3.3: Beispiel für Besucherfunktionen zum Auswerten von AST-Daten 10 KAPITEL 3. GRUNDLAGEN Der vorhergehende Beipielcode ist auch Teil des Typ-Analyse-Plug-ins. Dort werden die von den überschriebenen Funktionen gelieferten Daten ermittelt und anschließend in Tabellen aufgesammelt. So liefert z.B. die Besuchermethode mit dem Knotentyp Me” thodDeclaration“ als Parameter sowohl den Typ des Rückgabewertes als auch sämtliche Typen der übergebenen Aufrufparameter, wie im Beispiel funktional grob angedeutet wird. 11 4 Statistik In diesem Kapitel wird die Untersuchung der mit dem Typ-Analyse-Plug-in gewonnenen Daten mit Hilfe der Bayesschen Statistik gemacht. Es werden zwei Verteilungen angegeben und untersucht, ob die gewonnenen Daten diesen Verteilungen gehorchen. 4.1 Motivation Wie eingangs bereits erwähnt, bildet die Bayes-Statistik“ die Grundlage für die nun ” folgende statistische Untersuchung von Typhäufigkeiten in Java-Programmen. Für eine Einführung sei hier [7], Fahrmeir [14] Kapitel 4.7, Koch [8] und Moeschlin/Steinert [13] empfohlen. Auf Koch geht auch der gleich folgende Abschnitt über die Bayes-Statistik zurück. Mit Hilfe der Bayes-Statistik können willkürlich erhobene Daten, z.B. aus Häufigkeitszählungen von Typen in Java-Programmen, statistisch untersucht werden. Insbesondere ist mit der Bayesschen Methodik das Konzept der sogenannten A-Priori“-Informationen ” verbunden. Das heißt, daß in die statistische Datenanalyse Informationen mit einbezogen werden, die bereits vor der Erhebung der Daten vorliegen. Das Ziel der Bayesschen Methodik ist die Kombination der beiden Informationsquellen, d.h. der A-PrioriInformationen, in Form von Erfahrungen oder Expertenwissen, und der Daten selbst. Im Mittelpunkt der Bayes-Statistik steht das sogenannte Bayes-Theorem“, mit dem ” sich unbekannte Parameter der zugrunde liegenden und vermuteten Verteilungsfunktion schätzen lassen. Als Ergebnis einer solchen Analyse ergibt sich die sogenannte A” Posteriori-Verteilung“. Wie der Name bereits sagt, enthält die A-Posteriori-Verteilung die durch die Beobachtung von Stichproben der zu analysierenden Daten gewonnene Information über den oder die unbekannten Parameter. Dadurch kann eine qualitative Aussage über die Wahrscheinlichkeit gemacht werden, ob die analysierten Daten einer bestimmten Verteilungsfunktion gehorchen oder auch nicht. 4.2 Grundlagen der Bayes-Statistik In der Bayes-Statistik wird die Wahrscheinlichkeit von Aussagen definiert, wobei die Wahrscheinlichkeit ein Maß für die Plausibilität der Aussage liefert. Die Bayes-Statistik beruht auf dem sogenannten Bayes-Theorem“. Mit seiner Hilfe lassen sich unbekannte ” Parameter schätzen, Konfidenzregionen für die unbekannten Parameter festlegen und die Prüfung von Hypothesen für diese Parameter ableiten. Eine Aussage hängt im allgemeinen davon ab, ob eine weitere Aussage wahr ist. Deshalb arbeitet die Bayes-Statistik ausschließlich mit bedingten Wahrscheinlichkeiten: 12 KAPITEL 4. STATISTIK In der Wahrscheinlichkeitsrechnung ist P(A|B) die Wahrscheinlichkeit für ein Ereignis A unter der Bedingung, daß B eintritt. In der Bayes-Statistik gibt diese bedingte Wahrscheinlichkeit ein Maß für die Plausibilität der Aussage A|B an, d.h. daß die Aussage A richtig ist, wenn B gilt. Das Bayes-Theorem der Wahrscheinlichkeitsrechnung hat die folgende Gestalt: Sei Ω die Menge aller möglichen Ergebnisse (Ergebnisraum), E eine Familie von Teilmengen von Ω, die eine ρ -Algrbra bildet (Ereignisalgebra) und P : E → R+ ein Wahrscheinlichkeitsmaß. Sei A ∈ E mit P(A) > 0 und {B , ..., Bn |Bj ∈ E} eine disjunkte Zerlegung von Ω mit P (Bi ) > 0 für i = 1, ..., n. Dann gilt: P (A|Bk ) · P (Bk ) P (Bk |A) = P n P (A|Ei ) · P (Ei ) (4.1) i=1 Eine typische Anwendung wie zum Beispiel in der Medizin ergibt sich, wenn man die Wahrscheinlichkeit P(Ursache|Symptom) bestimmen will, aber viel leichter etwas über P(Symptom|Ursache) herausfinden kann. Praktisches Beispiel: Das nun folgende Beispiel wurde aus Hartung [7] entnommen. Durch einen zu spät erkannten Fabrikationsfehler sind in einer Automobilproduktion genau 20 defekte Lenkgetriebe eingebaut worden. In einer Rückrufaktion wurden 200000 Wagen dieser Serie überprüft und alle als fehlerhaft identifizierten Lenkgetriebe wurden ausgetauscht. Dabei wird die Überprüfung mit 99%-iger Sicherheit zu einem korrekten Ergebnis führen. Wie groß ist die Wahrscheinlichkeit, daß ein ausgewechseltes Lenkgetriebe auch defekt war? Es werden die folgenden Bezeichnungen verwendet: B1 sei das Ereignis eines defekten Lenkgetriebes. B2 sei das Ereignis eines nicht defekten Lenkgetriebes. A sei das Ereignis eines ausgewechselten Lenkgetriebes. Die folgenden Informationen sind gegeben: 20 P (B1 ) = = 0.0001 P (A|B1 ) = 0.99 P (A|B2 ) = 0.01 200000 Gesucht ist die Wahrscheinlichkeit P (Bk |A) = P (Lenkgetriebe def ekt|Lenkgetriebe ausgewechselt) Mit dem Theorem von Bayes folgt nun: P (B1 |A) = P (A|B1 ) · P (B1 ) 2 P P (A|Ei ) · P (Ei ) i=1 = 0.99 · 0.0001 ≈ 0.0098 0.99 · 0.0001 + 0.01 · 0.9999 13 KAPITEL 4. STATISTIK Fast alle ausgewechselten Lenkgetriebe waren demnach nicht defekt. In der Bayes-Statistik wird diese Formel noch durch eine Aussage über zusätzliches Wissen ergänzt. Der Einfachheit halber schreiben wir diese Formel nur für die Ergebnisse A,B und C auf: P (A|BC) = P (A|C) · P (B|AC) P (B|C) (4.2) Dabei bedeutet: A die Aussage über ein unbekanntes Phänomen B die Aussage, die Information über das unbekannte Phänomen enthält C eine Aussage über zusätzliches Wissen Man bezeichnet: P(A|C) als Priori-Wahrscheinlichkeit P(A|BC) als Posteriori-Wahrscheinlichkeit und P(B|AC) als Likelihood Beispiel: Laplace schätzte die Masse des Saturns anhand vorhandener astronomischer Beobachtungen seiner Umlaufbahn. Diese Daten aus dem 18.Jahrhundert ließen eine Schätzung der Masse und eine Vorhersage auf ein Prozent Genauigkeit zu. Dieser Wert wurde bis heute nur um 0.63 Prozent korrigiert. In Formel 4.2 ist dabei: A: Die Masse des Saturn MS liegt in einem bestimmten Intervall B: Daten von Observatorien über gegenseitige Störungen von Jupiter und Saturn C: common sense“, daß die Masse des Saturn weder so klein sein kann, daß er seine ” Ringe verliert, noch so groß, daß er das Sonnensystem zerstört 4.3 Grundbegriffe der Wahrscheinlichkeitsrechnung: Zufallsvariable, Verteilungsfunktion und Dichtefunktion In vielen Fällen benötigt man quantitative Aussagen über das Auftreten bestimmter Zahlenwerte als Ergebnis eines Zufallsversuchs. Das Ereignis“ besteht in diesem Fall ” darin, daß eine zufällige“ Größe X einen vorher fixierten Wert annimmt oder (etwas ” allgemeiner formuliert) einen Wert in einem festgelegten Intervall liefert. Dies führt auf den Begriff der Zufallsvariablen: 14 KAPITEL 4. STATISTIK Eine Funktion X : Ω → R heißt Zufallsvariable, wenn für jedes c ∈ R die Menge Ec := {ω ∈ Ω | X(ω) ≤ c} (4.3) ein Ereignis in dem betrachteten Wahrscheinlichkeitsraum ist. Um beschreiben zu können, welche Werte eine Zufallsvariable X mit einer bestimmten Wahrscheinlichkeit annimmt, macht man folgende Begriffsbildung: Sei X eine Zufallsvariable. Dann heißt die durch FX (x) = P ({ω ∈ Ω | X(ω) ≤ x}), x ∈ R (4.4) gegebene Funktion FX die Verteilungsfunktion der Zufallsvariablen X. Für b > a gilt: FX (b) − FX (a) = P (a < X ≤ b) (4.5) Eine Zufallsvariable heißt kontinuierlich (oder stetig), falls eine Darstellung der Form Zx FX (x) = f (t) dt (4.6) −∞ mit einer nichtnegativen integrierbaren Funktion f möglich ist. f nennt man die Dichtefunktion (oder Dichte) der Zufallsvariablen X. Wenn eine Zufallsvariable X eine Dichtefunktion fX besitzt, dann gilt für a < b: Zb P (a < X ≤ b) = fX (t) dt. (4.7) a Verteilungsfunktion und Dichtefunktion enthalten die gleichen Informationen über eine kontinuierliche Zufallsvariable. Das Bild einer Dichtefunktion ist aber sehr viel aussagekräftiger. Man beachte aber, daß die Dichtefunktion selbst keine direkte Interpretation als Wahrscheinlichkeit erlaubt, erst die Integration führt zu Wahrscheinlichkeiten. 15 KAPITEL 4. STATISTIK 4.4 Wahl der Verteilungsfunktion Die Frage nach den Worthäufigkeiten in beliebigen Texten beschäftigte viele Mathematiker. So fand J.B. Estoup 1916 ein Gesetz über die Häufigkeit der Benutzung von Wörtern, das 1935 von G.K. Zipf formuliert und von G.U. Yule und B. Mandelbrot und anderen weiterentwickelt wurde. Siehe hierzu auch Artikel Bibliometrie“ [4] im WWW bei Wiki” pedia - Die freie Enzyklopädie. Die Zipf/Estoup-Verteilung und die Yule-Verteilung sind im Zusammenhang mit Worthäufigkeiten, siehe auch Johnson [6], aus dem Bereich der Linguistik bekannt. Dort erklären sie die Anzahl des Auftretens von Wörtern in einem beliebigen Text. Von daher liegt es nahe, diese beiden Verteilungen auszuwählen, um zu testen, ob die gemessenen Daten (Typhäufigkeiten in Java-Programmen) einer dieser beiden Verteilungen gehorchen. Sei P[x=r] die Wahrscheinlichkeit, daß ein Wort x in einem Text r-mal auftritt. 4.4.1 Zipf/Estoup-Verteilung Die Zipf/Estoup-Verteilung, in der Literatur teilweise auch Zeta“-Verteilung genannt, ” ergibt sich aus der Näherungsformel, wie sie in Johnson [6] beschrieben ist: nr ∝ r−(ρ+1) (r = 1, 2, ...; ρ > 0) (4.8) wobei nr die Anzahl der Worte ist, die r mal auftreten: P [x = r] = cr−(ρ+1) (r = 1, 2, ...) (4.9) mit c := [ζ(ρ + 1)]−1 (4.10) wobei ζ(...) die Zeta-Funktion ζ(x) = ∞ X 1 1 1 1 = 1 + + + + ··· x x x x n 2 3 4 n=1 beschreibt. Diese Verteilungsfunktion wird auch Zeta-Verteilung genannt. 16 (4.11) KAPITEL 4. STATISTIK 4.4.2 Yule-Verteilung Die Yule-Verteilung ist ebenfalls in Johnson [6] beschrieben und lautet wie folgt: P [x = r] = Aρ B(r, ρ + 1) (r = 1, 2, ...) (4.12) mit " Aρ = ∞ X #−1 B(r, ρ + 1) (4.13) Γ(α) · Γ(β) Γ(α + β) (4.14) r=1 und B(α, β) = als Beta-Funktion, wobei die Gammafunktion durch Γ(t) = t · eγt · ∞ Y k=1 t 1+ k !−1 − kt ·e mit γ = 0.577216... (4.15) gegeben ist. Der unbekannte Parameter ρ stellt hier im konkreten Fall den zu bestimmenden Parameter in der zugrunde liegenden Verteilungsfunktion dar. Dies ist der Wert auf der x-Achse, wo der zugehörige y-Wert sein Maximum erreicht. Dieser Wert stellt dann für die untersuchten Daten das optimale Ergebnis dar. Sprich, mit diesem Wert des optimalen Parameters ρ können die Daten am besten erklärt werden. 17 KAPITEL 4. STATISTIK 4.5 Statistisches Analyse-Programm Grundlage des Analyseprogramms ist das Bayes-Theorem. Es wird damit eine Wahrscheinlichkeitsschätzung durchgeführt, um eine Aussage über die beiden vorab vermuteten Verteilungen zuzulassen. Dazu wird der in Abschnitt 4.6 beschriebene Rekursionsalgorithmus benötigt. Als Eingabe dienen jeweils die von den Java-Testprogrammen ermittelten Typ-Verteilungsdaten in Form der TXT-Datei, die mit dem Typ-Analyse-Plug-in erzeugt werden. Diese Datei wird beim Start eingelesen und anschließend weiterverarbeitet bzw. interpretiert. Für die A-Priori-Daten wird eine Gleichverteilung angenommen, da vorab keine Ausage über die Verteilung der Eingabedaten vorliegt. Das Ergebnis stellt sich in Form einer Wahrscheinlichkeitsdichtefunktion dar, die angibt, wie wahrscheinlich die Messdaten sich einer empirisch ermittelten Yule- bzw. Zipf/Estoup-Verteilung annähern. Ist eine solche Dichtefunktion vorhanden, so stellt das Maximum der Funktion das optimale Ergebnis (die größte Wahrscheinlichkeit) bezogen auf die zu analysierenden Daten dar. In Abbildung 4.1 ist beispielhaft die (Wahrscheinlichkeits-)Dichtefunktion für die Yule-Verteilung dargestellt. Die Grundlage für dieses Beispiel sind die Ergebnisse der Typ-Analyse des Programms ”JUnit 3.8.2”. Abbildung 4.1: Statistisches Analyse-Programm Je ausgeprägter, d.h. je höher und schmaler dieser Peak ist, desto besser passen die getroffenen Annahmen über die Verteilungen auf die zu analysierenden Daten. In diesem Fall gibt es genau einen Parameterwert ρ mit maximalem Gewicht. Im Sinne der BayesStatistik wird dieser Parameterwert als optimaler Parameter“ zur Beschreibung der ” Stichprobe herangezogen. Damit hat man einen guten Hinweis, daß die vorab getroffenen 18 KAPITEL 4. STATISTIK Annahmen korrekt sind. Ergibt sich kein solcher Peak, so kann man daraus schließen, daß die zuvor getroffenen Annahmen nicht zutreffend sind und die Daten keiner der beiden Verteilungen entsprechen. Im obigen Beispiel ergibt sich nur“ für die Yule-Verteilung ein Peak. Bei kleinen Pro” grammen kann es vorkommen, daß sich für beide Verteilungen ein Peak ergibt. Dies kann damit erklärt werden, daß sich die beiden Verteilungen recht ähnlich sind und es erst bei einer größeren Datenmenge zu einem eindeutigen Ergebnis kommt. Der ausgeprägtere der beiden Peaks stellt dann einen statistischen Hinweis dar, daß die zugehörige Verteilung die Daten besser erklärt als die andere Verteilung. Dieses Merkmal ist auch bei den anderen untersuchten Programmen der Fall, wie später bei den Ergebnissen zu sehen sein wird. Es ergibt sich immer eine eindeutige Zuordnung zu einer der beiden Verteilungen. Falls sich kein solcher Peak einstellen würde, so muß man annehmen, daß die vorliegenden Daten auf keine der beiden Verteilungen passen. Die Folge daraus wäre dann, daß die Daten einer anderer Verteilung gehorchen, die die Daten besser erklärt. Es könnte aber auch sein, daß es sich um eine neue, unbekannte Verteilungsfunktion handelt. Auf der rechten Seite ist das Diagramm der Verteilungen zu sehen. Hierbei wird die Verteilung der Typ-Analysedaten in Form der blauen Kurve aufgetragen. Zum Vergleich werden zusätzlich die beiden bekannten Verteilungen Yule (rote Kurve) und Zipf/Estoup (magenta Kurve) in Form einer optimalen Verteilung zum Vergleich dargestellt. Man kann an diesem Bespiel gut erkennen, wie gut sich die Typ-Analysedaten an die beiden bekannten Verteilungen annähern. Den Ausschlag zu einer der beiden bekannten Verteilungen gibt dann letztlich die Abweichung. Diese Abweichung wird berechnet und unterhalb der Diagrammbeschriftung ausgegeben. Der kleinere Wert sagt dabei aus, zu welcher der beiden bekannten Verteilungen die Daten besser passen. Dies wird außerdem durch die Dichtefunktion auf der linken Seite des Diagramms bestätigt. Das komplette Listing des statistischen Analyseprogramms befindet sich im Anhang A.4.1. 19 KAPITEL 4. STATISTIK 4.6 Rekursionsalgorithmus Hier folgt der in Moeschlin et al. [13] dargestellte Rekursionsalgorithmus. Grundlage für das Folgende ist die Kollektion W := {P (γ; .) | γ ∈ G} (4.16) möglicher Stichprobenverteilungen, wobei im diskreten Fall P [., .] durch eine (r × m)Matrix p[., .] gegeben ist. Nach der Grundannahme eines statistischen Experiments existiert in der vorgegebenen Kollektion W der möglichen Stichprobenverteilungen genau eine Verteilung p[w, .], die wahr ist, d.h. die Stichprobenwerte sind unabhängige Replikanten eben dieser Verteilung. Für die Zug um Zug anfallende Stichprobenrealisation setzt man aus Gründen zweckmäßiger Schreibweise das Symbol k“ anstelle von x“. Zunächst wird ein gemäß p[w, .] ” ” verteilter Stichprobenwert k ∈ H bestimmt, durch k in der folgenden Matrix p[., .] Stichproben q Parameter 1 2 ... k ... q[1] 1 p[1,1] p[1,2] . . . p[1,k] . . . q[2] 2 p[2,1] p[2,2] . . . p[2,k] . . . .. .. .. .. .. .. .. . . . . . . . q[r] r p[r,1] p[r,2] . . . p[r,k] . . . Γ m H p[1,m] p[2,m] .. . p[r,m] eine Spalte p[., k], als Kandidat für die Posteriori-Verteilung h[., k] (an der Stelle k) festgelegt. Zur Bestimmung von h[., k] wird jedes Element p[i, k] des Spaltenvektors p[., k] mit dem entsprechenden Element q[i] des Vekors q (Priori-Verteilung) multipliziert. Normiert man den so entstehenden Spaltenvektor derart, daß dessen Spaltensumme gleich 1 ist, so ist mit dem so bestimmten Vektor h[1, k] .. h[., k] := . h[r, k] (4.17) die Posteriori-Verteilung an der Stelle k gefunden. Schließlich wird im Sinne des Iterationsprinzips der Vektor q (bisherige Priori-Verteilung) durch den die Posteriori-Verteilung an der Stelle k beschreibenden Vektor h[., k] überschrieben: q[.] := h[., k] (4.18) 20 KAPITEL 4. STATISTIK h[.,k] übernimmt nun die Funktion der Priori-Verteilung. Dieser Algorihmus ist, wie in Abschnitt 4.5 bereits erwähnt, in dem statistischen Analyseprogramm umgesetzt. 21 KAPITEL 4. STATISTIK 4.7 Untersuchung der Beispielprogramme Für die weitere Datenanalyse wurden einige repräsentative Programme bzw. deren Quellcode verwendet. Die Quellcodes wurden teilweise aus größeren firmeninternen Programmen der Entwicklungsabteilung CC/ESM der Robert Bosch GmbH in Stuttgart (Bosch) oder aus frei verfügbaren Quellen im Internet gewonnen. Dazu gehören http://sourceforge.net und http://freshmeat.net. Anzumerken ist, daß insbesondere der Quellcode der firmeninternen Programme der Robert Bosch GmbH nicht nach außen getragen wurde, sondern nur die Ergebnisse der analysierten Daten weiterverwendet wurden. 4.7.1 Beispielprogramme Für die Datenanalyse wurden folgende Java-Programme verwendet, die sich auch auf der beiliegenden DVD befinden (ausgenommen die Programme der Robert Bosch GmbH): Programm Beschreibung JUnit 3.8.2 Test-Framework für Java-Programme JHotDraw 6.0b1 GUI framework for Graphics J2SDK 1.4.2 Java Software Development Kit Sinaxe2 Code Generator Apache Ant 1.7.0 Java-based build tool Apache James 2.3.1 Plattformunabhängiger E-Mail-Server Apache Tomcat 6.0.10 Web-Server CoBrake Tool zur Bremsenauslegung (Bosch) Frinika Music workstation software JCommon 1.0.12 Basis von JFreeChart/JFreeReport JFreeChart 1.0.6 Grafik-Bibliothek JFreeReport 0.8.7 Reporting library for embedded solutions Orson 0.5.0 Collection of charting components JToolChain API zur Fernsteuerung von Simulink SimServ Simulationsdatenbank-Server (Bosch) SWTSwing-3-2-0004 Widget-Bibliothek TAP 1.0.0 Typ-Analyse-Plug-in Java Chess 0.1.0 Schachprogramm Kaffe 1.1.7 Clean room impl. of the Java VM Marathon 1.0.4 GUI development tool Tabelle 4.1: Repräsentative Beispielprogramme 22 KAPITEL 4. STATISTIK 4.7.2 Untersuchung Die Programme werden dazu innerhalb der Eclipse-IDE geladen, auf Fehlerfreiheit bezüglich der Syntax geprüft und anschließend der Typ-Analyse unterzogen. Die Quellen von Bosch wurden firmenintern der Typ-Analyse unterzogen und nur die Ergebnisse hier weiterverwendet. Dabei entsteht bei jeder Typ-Analyse eine Tabelle der nachfolgenden beispielhaften Form: Anzahl der Deklarationen Typ 11 java.lang.String 6 java.util.List<java.io.File> 4 void 3 java.io.File 3 int 3 java.lang.String[] 2 int[] 1 FileFinder 1 char[] 1 java.lang.Integer 1 java.util.Stack<java.io.File> 1 boolean 0 java.util.ArrayList<java.io.File> 0 IFileFinder 0 StartFileFinder 0 StartFileFinder.LocalClass Tabelle 4.2: Beispieltabelle eines kleinen Beispielprogramms Die vorhergehenden Daten stammen aus einem kleinen Testprogramm, welches sämtliche Java-Quelldateien eines Eclipse-Projekts ermittelt. Dieses Testprojekt wurde auch dazu benutzt, um das Typ-Analyse-Plug-in auf die Korrektheit der Ausgaben zu prüfen. Hierzu wurden sämtliche möglichen Typ-Konstrukte implementiert und anschließend auf korrekte Erkennung der Typen hin analysiert. In der ersten Zeile steht der Datentyp mit dem häufigsten Vorkommen (Deklarationen), in der zweiten Zeile der Datentyp mit dem zweithäufigsten Vorkommen usw. Am Ende der Liste kann es noch zu Einträgen mit der Anzahl von 0 Deklarationen geben. Diese deuten auf Datentypen im Programm hin, die dort zwar definiert werden, aber nie als Deklarationselement verwendet werden. In Java-Programmen kann dies z.B. die Klasse mit dem Einsprungspunkt zum Hauptprogramm main()“ sein. Diese Klassen ha” ben keinen Einfluß auf die weitere Analyse, sollen aber bei der Quellcodeanalyse nicht unberücksichtigt bleiben. Evtl. können auch hier weitere Optimierungen des Programms erfolgen, indem solche Klassen einfach weggelassen werden können. Für die weitere Analyse wird pro zu analysierendem Programm eine Text-Datei erzeugt, die nur die Anzahl 23 KAPITEL 4. STATISTIK der aufgetretenen Typen auflistet. Die Namen der einzelnen Typen sind hier nicht von Bedeutung und wurden in den erzeugten TXT-Dateien einfach weggelassen. Der Inhalt der TXT-Datei ist nach der Anzahl der Deklarationen in absteigender Reihenfolge sortiert. Diese Datei kann grafisch als Histogramm in folgender Form dargestellt werden: Abbildung 4.2: Histogramm Die Daten des Histogramms (TXT-Datei) dienen im Weiteren als Eingabedaten für das statistische Auswerte-Programm. 24 5 Ergebnisse 5.1 Verteilungen Die vorliegenden Beispielprogramme aus der Tabelle 4.1 wurden mit dem statistischen Auswerteprogramm untersucht. In sämtlichen Fällen ergibt sich ein eindeutiges Ergebnis zugunsten einer der beiden bekannten Verteilungen. Das heißt, die Daten der untersuchten Beispielprogramme fallen eindeutig in eine Klasse der beiden bekannten Verteilungen. Als Beispiel sei hier die Abbildung 5.1 des Programms JUnit 3.8.2“ gegeben. ” Abbildung 5.1: Posteriori-Verteilung (JUnit 3.8.2) Damit lassen sich die Typ-Häufigkeiten des Programms JUnit 3.8.2“ eindeutig der ” Yule-Verteilung zuordnen, wie man an dem roten Peak erkennen kann. 25 KAPITEL 5. ERGEBNISSE Abbildung 5.2: Posteriori-Verteilung (JFreeChart 1.0.6) In diesem Beispiel der Grafikbibliothek JFreeChart 1.0.6“ hat sich die Zipf/Estoup” Verteilung durchgesetzt, wie man an dem magenta Peak erkennen kann. 26 KAPITEL 5. ERGEBNISSE Abbildung 5.3: Posteriori-Verteilung (TAP 1.0.0) In diesem Beispiel von TAP 1.0.0“ hat sich in der Tendenz die Yule-Verteilung durch” gesetzt, wie man an dem größeren, ausgeprägteren der beiden Peaks erkennen kann. 27 KAPITEL 5. ERGEBNISSE Für die weiteren Beispielprogramme ergeben sich die in Tabelle 5.1 dargestellten Klassifizierungen. Ein X“ bedeutet hier, daß sich ein eindeutiges Ergebnis in Form eines ” Peaks bei der Posteriori-Verteilung ergibt. Damit ist das untersuchte Programm eindeutig der jeweiligen Verteilung zuzuordnen. Programm Yule JUnit 3.8.2 X JHotDraw 6.0b1 J2SDK 1.4.2 Sinaxe2 X Apache Ant 1.7.0 X Apache James 2.3.1 X Apache Tomcat 6.0.10 CoBrake Frinika JCommon 1.0.12 X JFreeChart 1.0.6 JFreeReport 0.8.7 X Orson 0.5.0 X JToolChain X SimServ X SWTSwing-3-2-0004 TAP 1.0.0 X Java Chess 0.1.0 X Kaffe 1.1.7 Marathon 1.0.4 X Zipf/Estoup X X X X X X X x X Tabelle 5.1: Klassifizierung der Verteilungen Wie man sehen kann, gibt es ein Programm, nämlich das Typ-Analyse-Plug-in TAP 1.0.0“ selbst, mit dem die statistischen Daten gewonnen wurden, welches die Merkmale beider Verteilungen aufweist. Details sind in der Grafik 5.3 weiter unten zu sehen. Durch das große X“ soll aber wiederum der Vorzug für die Yule-Verteilung dargestellt werden. ” Dieser resultiert von dem ausgeprägteren Peak. Dieses Ergebnis kann damit erklärt werden, daß die beiden bekannten Verteilugen recht ähnlich sind und das Programm TAP, im Vergleich zu den anderen Programmen, als klein angesehen werden kann. Es besteht aus nur 14 Java-Quellen mit insgesamt 129 verschiedenen Typen. Größere Programme bestehen aus über 600 Java-Quellen mit über 1000 Typen, wobei sich statistisch gesehen ein eindeutigeres und aussagekräftigeres Ergebnis erzielen läßt. 28 KAPITEL 5. ERGEBNISSE 5.2 Abweichungen In der Tabelle 5.2 sind die Abweichungen der einzelnen Programme von der empirischen Verteilung aufgelistet. Bei den untersuchten Testprogrammen konnten die folgenden Abweichungen von der empirischen Verteilung festgestellt werden. Diese Abweichungen bestätigen außerdem die in der Tabelle 5.1 festgestellten Klassifizierungen durch die Posteriori-Verteilungen (Peaks). Das heißt auch, daß die geringere Abweichung die Posteriori-Verteilung in vollem Umfang bestätigt. Die geringere Abweichung ist in fetter Schrift dargestellt. Programm JUnit 3.8.2 JHotDraw 6.0b1 J2SDK 1.4.2 Sinaxe2 Apache Ant 1.7.0 Apache James 2.3.1 Apache Tomcat 6.0.10 CoBrake Frinika JCommon 1.0.12 JFreeChart 1.0.6 JFreeReport 0.8.7 Orson 0.5.0 JToolChain SimServ SWTSwing-3-2-0004 TAP 1.0.0 Java Chess 0.1.0 Kaffe 1.1.7 Marathon 1.0.4 Abweichung Yule Abweichung Zipf/Estoup 0.18635056631801772 0.2139252939932779 0.22475110504797988 0.19012227629943218 1.1855550352648667 0.784984972715766 0.36653506158059607 0.37747787816560896 0.5533452634033342 0.6244172885230729 0.15808007731559603 0.19031069755143099 1.2659252244811419 0.7469863583426389 1.3943195295211803 0.6590067230093477 0.4206676390460549 0.41466480798539784 0.39069265595919156 0.40685801818957307 0.4419640338985785 0.41114473045336075 0.35488294577760193 0.37578013951639283 0.47358322548335999 0.49268899326698157 0.38237538640069363 0.39626730822490724 0.26056854371873894 0.2795288613168352 0.5170534440285449 0.5039709870349337 0.2852650188290128 0.31588253726842336 0.24328093517665073 0.2671864112061283 1.184271073756466 0.7899465773548795 0.19592428024892475 0.2091552766634423 Tabelle 5.2: Abweichungen der Beispielprogramme 5.3 Fazit Wie man aus den beiden Tabellen 5.1 und 5.2 sehen kann, ergibt sich keine eindeutige Mehrheit für eine der beiden Verteilungen. Man kann also feststellen, daß sich für die hier untersuchten Programme kein eindeutiges Ergebnis zugunsten einer der beiden Verteilungen ergeben hat. Um herauszufinden, welche Faktoren die Entscheidung zugunsten der einen oder der anderen Verteilung beeinflussen, sind weitere Arbeiten notwendig, die 29 KAPITEL 5. ERGEBNISSE wohl mit Unterstützung der Fachbereiche Mathematik bzw. Linguistik durchzuführen wären. 30 6 Typ-Analyse-Plug-in (TAP) 6.1 Anforderungen Das hier zu entwickelnde Typ-Analyse-Plug-in hat als Hauptaufgabe das Finden sämtlicher in Kapitel 3.2 genannten Deklarationselemente eines Java-Programms. Hierzu muß der jeweilige AST der Java-Quelle, unter Eclipse auch Compilation-Unit“ genannt, aus” gewertet werden. Die in diesen Elementen vorkommenden Typen werden erfasst und pro Typ die Häufigkeit seines Vorkommens bezogen auf alle in der untersuchten Java-Quelle vorkommenden Deklarationselemente festgehalten. Das Ergebnis wird anschließend tabellarisch und grafisch ausgegeben. Als weitere Anforderung, neben der eben beschriebenen globalen“ Typ-Analyse, ist von Interesse, in welchen anderen Typen ein bestimmter ” Typ verwendet wird. Das heißt, es wird dargestellt, in welchen anderen Typen ein bestimmter Typ vorkommt. Dies stellt sozusagen eine Detailierung für einen bestimmten, vom Anwender ausgewählten Typ dar. Hierzu wird im Plug-in eine Auswahl angeboten, die diese Auswertung möglich macht. Zum besseren Verständnis soll das folgende kleine Beispiel dienen. Der Typ java.lang.String“ sei insgesamt z.B. sechs mal in einem Programm enthalten, ” was die globale Typ-Analyse ergeben hat. Das Programm selbst besteht wiederum aus den drei Typen, hier z.B. aus den Klassen A, B und C. Der Typ java.lang.String“ ist ” nun folgendermaßen in den drei Typen/Klassen verteilt: Typ Anzahl der Vorkommen A 1 B 3 C 2 Tabelle 6.1: Beispiel von Typ-Vorkommen Hieraus ist ersichtlich, wie sich die Verwendung des Typs java.lang.String“ detailiert ” auf die drei ihn benutzenden Typen/Klassen A, B und C verteilt. Dieses Ergebnis kann für die Analyse der Verwendung von Typ-Abhängigkeiten genutzt werden. Das Ergebnis kann in einer weiteren Ansicht betrachtet werden. Die Ausgabe erfolgt wieder tabellarisch und grafisch. Als zusätzliches Feature kann jeweils zwischen der Verwendung von Typen in Deklarationen und den Instanziierungen von Typen umgeschaltet werden. Die Information über die Typ-Instanziierungen kann als Abfallprodukt“ bei der ” AST-Analyse gewonnen werden. Als weitere Anforderung soll noch eine Übersicht über alle im Eclipse-Projekt analysierten Java-Quelldateien in Form einer Liste ausgegeben werden. 31 KAPITEL 6. TYP-ANALYSE-PLUG-IN (TAP) Für die beschriebenen Anforderungen werden hierfür die folgenden Ansichten (Views) innerhalb des Plug-ins realisiert: • Type Diagram View - stellt die NOD- und NOI-Metrik grafisch dar • Type List View - stellt die NOD- und NOI-Metrik tabellarisch dar • Detail Diagram View - stellt das Vorkommen eines bestimmten Typs in einem anderen Typ grafisch dar • Detail List View - stellt die Details der Vorkommen eines bestimmten Typs in einem anderen Typ tabellarisch dar • File List View - stellt die untersuchten Java-Quellen tabellarisch dar Beim Start eines Analysevorgangs werden diese Views automatisch erzeugt und in den Vordergrund der Workbench“ innerhalb der Eclipse-IDE gebracht. Diese können vom ” Anwender nach Belieben angeordnet werden. Für die nun folgenden beispielhaften Darstellungen wurde der Quellcode des Programms JUnit 3.8.2“ verwendet. JUnit ist eine ” Testumgebung für Java-Programme. 32 KAPITEL 6. TYP-ANALYSE-PLUG-IN (TAP) 6.1.1 Type Diagram View für die grafische Ausgabe der NOD- und NOI-Metrik In der Abbildung 6.1 wird für jeden im analysierten Quellcode vorkommenden Typ die Häufigkeit seiner Verwendung dargestellt. Die Häufigkeit kann an der y-Achse, die mit Number“ bezeichnet ist, abgelesen werden. Der eigentliche Typ wird in diesem Dia” gramm als Nummer angegeben, deshalb ist die x-Achse mir Type#“ bezeichnet. Der ” genaue Typ kann anhand der Nummer im sogenannten Type List View“ nachgelesen ” werden. Dort hat jeder Eintrag in der Liste eine eindeutige Nummer, die den jeweiligen Typ repräsentiert. Eine andere Möglichkeit ist die Aktivierung des implementierten Tooltips, welches erscheint, wenn der Benutzer längere Zeit mit dem Mauszeiger auf einem Balken verweilt. Die Typen werden nach der Häufigkeit ihres Auftretens sortiert, das heißt, der am häufigsten vorkommende Typ steht ganz links und der am wenigsten auftretende Typ ganz rechts. Abbildung 6.1: Histogramm der Häufigkeit der Vorkommen von Typen innerhalb von JUnit 3.8.2 Durch das Umschalten, mittels I“ in der Menüzeile dieser Ansicht, kann hier auf die ” Darstellung der Metrik NOI gewechselt werden. Diese gibt die Häufigkeit der Vorkommen von Instanziierungen von Typen an. 33 KAPITEL 6. TYP-ANALYSE-PLUG-IN (TAP) Die nun folgende Tabelle zeigt die Daten des obigen Ergebnisses nochmals in gekürzter tabellarischer Form. Die Tabelle wird zur weiteren Verarbeitung ebenfalls als TXT- und XLS-Datei von dem Plug-in ausgegeben. Typ NOD NOI void 617 0 java.lang.String 264 0 int 187 0 junit.framework.Test 128 0 boolean 76 0 junit.framework.TestResult 69 22 java.lang.Object 51 7 java.lang.Class 45 0 junit.samples.money.IMoney 45 0 java.util.Vector 35 21 java.util.Enumeration 28 0 junit.samples.money.Money 25 42 java.lang.String[] 25 6 long 25 0 junit.framework.TestSuite 23 32 junit.framework.TestCase 22 0 java.lang.Throwable 21 0 java.awt.Component 17 0 java.awt.GridBagConstraints 16 16 java.awt.event.ActionEvent 16 0 javax.swing.Icon 14 0 double 13 0 javax.swing.JButton 12 7 junit.framework.TestFailure 12 3 junit.samples.money.MoneyBag 11 3 javax.swing.JLabel 9 11 java.util.Hashtable 9 7 ... ... 0 Tabelle 6.2: Tabelle der Häufigkeit der Vorkommen von Typen innerhalb von JUnit 3.8.2 34 KAPITEL 6. TYP-ANALYSE-PLUG-IN (TAP) 6.1.2 Type List View für die tabellarische Ausgabe der NOD- und NOI-Metrik Die Abbildung 6.2 stellt das oben gezeigte Histogramm der NOD-Metrik 6.1 in Form einer Tabelle dar. Die dargestellten Daten werden außerdem jeweils in einer Datei abgespeichert, um anschließend weiterverarbeitet werden zu können. Abbildung 6.2: Listen-Ansicht der Deklarationen/Instanziierungen 35 KAPITEL 6. TYP-ANALYSE-PLUG-IN (TAP) 6.1.3 Detail Diagram View für die grafische Ausgabe der Abhängigkeiten eines bestimmten Typs Durch Auswahl einer Zeile, z.B. junit.framework.TestResult“ in der vorherigen Ansicht, ” kann die Häufigkeit des Vorkommens dieses Typs in Deklarationselementen, die in anderen Typen vorkommen, analysiert werden. Abbildung 6.3: Typ-Abhängigkeiten des Typs junit.framework.TestResult“ ” Durch das Umschalten, mittels I“ in der Menüzeile dieser Ansicht, kann auch hier auf ” die Vorkommen von Instanziierungen des selektierten Typs in anderen Typen dargestellt werden. 36 KAPITEL 6. TYP-ANALYSE-PLUG-IN (TAP) 6.1.4 Detail List View für die tabellarische Ausgabe der Abhängigkeiten eines bestimmten Typs Die detailierten Daten dieser Analyse können in der folgenden Ansicht 6.4 in tabellarischer Form betrachtet werden. Mit Hilfe dieser Daten könnten nun ebenfalls Optimierungen eines bestimmten Typs vorgenommen werden. Es können zum einen häufig benutzte Typen bezüglich Laufzeit oder Speicherbedarf optimiert werden oder zum anderen können selten oder nicht benutzte Typen eliminiert bzw. wegoptimiert werden, z.B. durch Typen aus einer Standardbibliothek ersetzt werden. Außerdem kann man Abhängigkeiten zwischen einzelnen Typen erkennen und hat so frühzeitig die Möglichkeit abzuschätzen, welchen Einfluß eine Änderungen eines Typs auf die von ihm abhängigen Typen mit sich bringt. Es kann damit auch der zeitliche Änderungsaufwand bei größeren Umstellungen in einem Programm abgeschätzt werden. Abbildung 6.4: Detail-Ansicht der Vorkommen eines bestimmten Typs in anderen Typen 37 KAPITEL 6. TYP-ANALYSE-PLUG-IN (TAP) 6.2 Praktische Umsetzung Von dem aktuell selektierten Eclipse-Projekt müssen zunächst alle Java-Quellen, die sogenannten Compilation-Units“ (CUs), identifiziert werden. Anschließend wird von jeder ” Compilation-Unit der zugehörige AST erzeugt. Dies kann durch das folgende beispielhafte Codefragment 6.1 realisiert werden: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public c l a s s ASTHandler { // Set source file File file = new File ( " source .java" ) ; ... // Allocate buffer for file content char [ ] content = new char [ file . length ( ) ] ; // Read in file file . read ( content ) ; ... // Create new ASTParser ASTParser astParser = ASTParser . newParser ( AST . JLS3 ) ; // Set kind of source astParser . setKind ( ASTParser . K_COMPILATION_UNIT ) ; // Create fully quallified type names (e.g. "java.lang. String ") astParser . setResolveBindings ( true ) ; // Set source contents astParser . setSource ( content ) ; // Create the AST CompilationUnit cu = ( CompilationUnit ) astParser . createAST ( null ) ; // Create type declaration vistor TypeDeclarationVisitor tdVisitor = new TypeDeclarationVisitor ( ) ; // Set visitor cu . accept ( tdVisitor ) ; ... } Listing 6.1: AST-Erzeugung und Parsen einer Java-Quelle Hier wird beispielhaft gezeigt, wie für die Quelle source.java“ der AST erzeugt und ” anschließend der eigenen Besucherklasse zugeführt wird. Die Besucherklasse ist dabei eine durch den Anwender zur Verfügung gestellte Klasse, welche das bekannte Visitor“” Entwurfsmuster realisiert. Durch den Aufruf der Funktion createAST“ in Zeile 19 wird ” der AST der Java-Quelle erzeugt. Mit dem Aufruf der Funktion accept()“ in Zeile 23 der ” Compilation-Unit, mit dem TypeDeclarationVisitor“ als Argument, wird die Analyse“ ” ” der Quelle angestoßen, um an die Informationen aus dem AST zu gelangen, an denen das eigene Programm interessiert ist. Die Ergebnisse dieses Besuchervorgangs“ können ” dann zunächst in Tabellen gehalten werden, um nach erfolgreichem Abschluss weiter verarbeitet zu werden. 38 KAPITEL 6. TYP-ANALYSE-PLUG-IN (TAP) Der Hauptaufwand steckt dabei in der Klasse TypeDeclarationVisitor“, welche im ” Rahmen dieser Arbeit enstanden ist und die eigentliche Auswertung der Typen vornimmt. Dazu wird, wie oben bereits erwähnt, das Visitor“-Entwurfsmuster verwendet, ” um den AST rekursiv zu durchlaufen. Die zu implementierende Besucherklasse muss nun alle AST-Knoten abfangen“, an denen sie interessiert ist. Dazu müssen die Besucher” Funktionen, sogenannte Visitor“ implementiert werden. Die folgende Tabelle 6.3 zeigt ” die zugehörigen Knotentypen, für die Visitor-Funktionen zu realisieren sind: AST-Knotentyp VariableDeclarationFragment ArrayCreation MethodDeclaration EnhancedForStatement TypeDeclaration WildcardType Verwendung Variablen-Deklarationen Array-Deklarationen Rückgabetyp und Parametertyp(en) von Methoden und Konstruktoren Deklarationen in for“-Schleifen ” Klassen- bzw. Interface-Deklarationen sowie Deklarationen von Aufzählungstypen Variable Anzahl von Parametern (z.B. String ...) Tabelle 6.3: AST-Knoten die Typ-Deklarationen repräsentieren Das folgende Listing 6.2 zeigt ein Codebeispiel für die relevanten Deklarationen anhand typischer Fälle. 1 2 3 4 5 6 7 8 public c l a s s publicType { // TypeDeclaration private Stack<File> dirs ; // VariableDeclarationFragment private int [ ] array ; // ArrayCreation ... public boolean outputAllFiles ( int num ) { // MethodDeclaration f or ( File file : dirs . pop ( ) . listFiles ( ) ) { // Enh. ForStatement ... } 9 private c l a s s privateType { // TypeDeclaration // Konstruktor private privateType ( String . . . s ) { // WildcardType // " String ..." wird als ein Typ "java.lang. String []" gezaehlt ! ... } ... } ... 10 11 12 13 14 15 16 17 18 19 } Listing 6.2: Deklarationsbeispiele und deren Zuordnung zu obigen AST-Knotentypen 39 KAPITEL 6. TYP-ANALYSE-PLUG-IN (TAP) Dieses Programmfragment hat somit als Ergebnis: Typ NOD boolean 1 int 1 int[] 1 java.io.File 1 java.lang.String[] 1 java.util.Stack<java.io.File> 1 void 1 publicType 0 publicType.privateType 0 Tabelle 6.4: NOD des Programmfragments 6.3 Architektur Das Typ-Analyse-Plug-in wird als Eclipse-Plug-in realisiert. Für die grafische Ausgabe der Histogramme wird die Grafik-Bibliothek JFreeChart“ verwendet (www.jfree.org ” und www.jfreechart.org), die auf der Basis JCommon“ aufbaut. Als weiteres Merkmal ” sei hier noch die Bibliothek JFreeReport“ genannt, die ebenfalls auf JCommon“ aufbaut ” ” und für die Erstellung von allen möglichen Reports dient. Diese könnten in einer weiteren Version des Plug-ins zur Anwendung kommen, um die Dokumentation der Histogramme und die Weiterverarbeitung zu verbessern. Alle übrigen grafischen Elemente sind dem Standard Widget Toolkit“ (SWT) entnommen. ” 6.4 Material und Methoden Für die Erstellung des Plug-ins und des Analyse-Tools wurden die folgenden Hilfsmittel verwendet: • Eclipse-IDE Version 3.3.0 (Europa) • JFreeChart-Bibliothek Version 1.0.4 • JCommon-Bibliothek Version 1.0.8 • Launch4j Cross-Plattform für Java-Applikationswrapper Version 3.0.0-pre2 6.5 Eclipse-IDE Die Eclipse-IDE erlaubt das Erstellen von Java-Applikationen und Plug-ins. Siehe auch www.eclipse.org. 40 KAPITEL 6. TYP-ANALYSE-PLUG-IN (TAP) 6.6 JFreeChart-Bibliothek Diese Bibliothek stellt die Funktionalität für das Erstellen von Grafiken jeglicher Art zur Verfügung. So können z.B. Kuchen-, Balken-, Linien- und Excel-Grafiken erstellt werden. Für weitere Informationen siehe www.jfree.org und www.jfreechart.org. 6.7 JCommon-Bibliothek Diese Bibliothek stellt die Grundfunktionalität für die oben genannte Grafikbibliothek zur Verfügung. Weiterhin ist sie auch Grundlage für das Erstellen von Reports (JFreeReport). 6.8 Launch4j-Applikationswrapper Dieses Hilfsprogramm erlaubt das Erstellen einer EXE-Datei aus einer von der EclipseUmgebung exportierten JAR-Datei. Dies ist im vorliegenden Fall nur für das statistische Auswerteprogramm möglich, welches als eigenständige Applikation gestartet wird. Zur Laufzeit muß für diese Applikation ebenfalls eine Java-Laufzeit-Umgebung (JRE) vorhanden sein. Weitere Informationen können unter http://launch4j.sourceforge.net erhalten werden. 41 7 Schlußbetrachtungen 7.1 Zusammenfassung Ziel dieser Arbeit war die Entwicklung eines Plug-ins, welches ein bestehendes JavaProgramm auf die Häufigkeit der Verwendung der im Programm vorkommenden Typen hin untersucht. Mit dieser Analyse kann man sich einen Überblick über die verwendeten Typen verschaffen und ggf. Optimierungen vornehmen. Aufbauend auf den Ergebnissen der Analyse wurden mit Hilfe statistischer Methoden die (Typ-)Verteilungen in verschiedenen Programmen untersucht und mit bekannten Verteilungen verglichen. Hier wurde festgestellt, daß sich die von den Beispielprogrammen ergebenden Verteilungen, ausnahmslos den zwei bekannten Verteilungen Yule“ und ” Zipf/Estoup“ zuordnen lassen. Daß dies so ist, kann aus meiner Sicht als Zufall gewertet ” werden, denn es hätte sich genausogut eine neue, unbekannte Verteilung ergeben können. Das Ergebnis ist deshalb auch als eine Überaschung einzustufen, weil sich Verteilungen von Typen in Java-Programmen wie die Vereilung von Wörtern in beliebigen Texten verhalten. 7.2 Einsatzmöglichkeiten von TAP Das Typ-Analyse-Plug-in kann überall dort zum Einsatz kommen, wo der Bedarf an Optimierungen der vorhandenen bzw. benutzten Klassen besteht. Es können zum Beispiel die Anzahl der Klassen sowie die Relationen untereinander reduziert bzw. vereinfacht werden. Das Plug-in kann hier bei der Analyse behilflich sein. Die restlichen Schritte, wie z.B. die Auflösung bzw. Vereinfachung von Typ-Abhängigkeiten, müssen vom Anwender manuell durchgeführt werden. 7.3 Ausblick Neben der erfolgten statistischen Untersuchung bezüglich der Verteilung von Typen könnten noch detailiertere Fragen gestellt werden, die Raum für weiterführende Arbeiten bieten. Dazu gehört zum Beispiel die Frage, wie weit sich die Grammatik“ von ” Java-Programmen an die Grammatik von normalen“ Texten anlehnt bzw. damit über” einstimmt. Für den Vergleich der beiden Grammatiken könnte die folgende Zuordnung zugrunde gelegt werden: 42 KAPITEL 7. SCHLUSSBETRACHTUNGEN Text-Grammatik Programm-Grammatik Substantiv Typ Adjektiv Variable Verb Methode Tabelle 7.1: Gegenüberstellung von Grammatiken Um hierzu eine Aussage zu treffen, müsste man einen Parser für beliebige Texte implementieren, der als Ergebnis ein Histogramm pro Textgruppe liefert. Auf der Programmseite müßte z.B. ein Plug-in entstehen, welches, aufbauend auf dem AST, die Grammatik des Programms, ebenfalls in Form eines Histogramms, liefert. In einem letzten Schritt können dann die Ergebnisse miteinander verglichen werden, um eine Aussage über die Übereinstimmung zu erhalten. Dazu sollten dann aber die Erfahrungen von Linguistikern und Mathematikern von vorneherein mit berücksichtigt werden. Mit dem daraus sich ergebenden Ergebnis könnte man einen Vergleich zwischen Programm- und Textgrammatik anstellen. Es wäre also eine Aussage möglich, ob sich die beiden Grammatiken ähnlich sind oder nicht. 7.4 Fazit Durch diese Arbeit steht ein grundlegendes Werkzeug für die Typ-Analyse von JavaProgrammen zur Verfügung, das für weitere statistische Untersuchungen oder Refactorings von Programmen dienen kann. Hierfür können zum einen die Ausgaben der Ergebnisse in den Dateien dienen. Zum anderen könnte aber auch noch ein weiterer Erweiterungspunkt ( Extension-Point“ in Eclipse genannt) in das vorhandene Plug-in ” eingefügt werden, über den die Daten der Ergebnisse ausgelesen werden können. So könnten andere Plug-ins bequem auf die Ergebnisse zugreifen. 43 Abbildungsverzeichnis 3.1 3.2 AST-Workflow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . AST-View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 9 4.1 4.2 Statistisches Analyse-Programm . . . . . . . . . . . . . . . . . . . . . . . Histogramm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 24 5.1 5.2 5.3 Posteriori-Verteilung (JUnit 3.8.2) . . . . . . . . . . . . . . . . . . . . . . Posteriori-Verteilung (JFreeChart 1.0.6) . . . . . . . . . . . . . . . . . . . Posteriori-Verteilung (TAP 1.0.0) . . . . . . . . . . . . . . . . . . . . . . 25 26 27 6.1 Histogramm der Häufigkeit der Vorkommen von Typen innerhalb von JUnit 3.8.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listen-Ansicht der Deklarationen/Instanziierungen . . . . . . . . . . . . . Typ-Abhängigkeiten des Typs junit.framework.TestResult“ . . . . . . . ” Detail-Ansicht der Vorkommen eines bestimmten Typs in anderen Typen 33 35 36 37 6.2 6.3 6.4 A.1 Histogramm der Typ-Instanziierungen . . . A.2 Plug-in-Detail . . . . . . . . . . . . . . . . . A.3 Datei-Ansicht des ausgewählten Programms A.4 Selektion des Exports . . . . . . . . . . . . . A.5 Ablage der JAR-Datei . . . . . . . . . . . . A.6 Optionen des Exports . . . . . . . . . . . . . A.7 Start der Typ-Analyse . . . . . . . . . . . . A.8 JAR-Export . . . . . . . . . . . . . . . . . . A.9 Ressourcen und JAR-Dateiname . . . . . . . A.10 JAR-Packet-Optionen . . . . . . . . . . . . A.11 JAR-Manifest-Spezifikation . . . . . . . . . A.12 Grundeinstellungen . . . . . . . . . . . . . . A.13 Classpath . . . . . . . . . . . . . . . . . . . A.14 Minimum JRE . . . . . . . . . . . . . . . . A.15 Start-Fehlermeldung . . . . . . . . . . . . . 44 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 52 53 55 56 57 59 70 71 72 73 74 75 76 77 Tabellenverzeichnis 3.1 Auswertung des Beispielcodes . . . . . . . . . . . . . . . . . . . . . . . . 6 4.1 4.2 Repräsentative Beispielprogramme . . . . . . . . . . . . . . . . . . . . . Beispieltabelle eines kleinen Beispielprogramms . . . . . . . . . . . . . . 22 23 5.1 5.2 Klassifizierung der Verteilungen . . . . . . . . . . . . . . . . . . . . . . . Abweichungen der Beispielprogramme . . . . . . . . . . . . . . . . . . . . 28 29 6.1 6.2 31 6.3 6.4 Beispiel von Typ-Vorkommen . . . . . . . . . . . . . . . . . . . . . . . . Tabelle der Häufigkeit der Vorkommen von Typen innerhalb von JUnit 3.8.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . AST-Knoten die Typ-Deklarationen repräsentieren . . . . . . . . . . . . NOD des Programmfragments . . . . . . . . . . . . . . . . . . . . . . . . 34 39 40 7.1 7.2 Gegenüberstellung von Grammatiken . . . . . . . . . . . . . . . . . . . . Inhalt der beiliegenden DVD . . . . . . . . . . . . . . . . . . . . . . . . . 43 48 A.1 AST-Knoten, der Typ-Instanziierungen repräsentiert . . . . . . . . . . . A.2 Beispielprogramm von Typ-Instanziierungen . . . . . . . . . . . . . . . . A.3 Pakete und Klassen des Typ-Analyse-Plug-ins . . . . . . . . . . . . . . . 50 50 54 45 Listings 3.1 3.2 3.3 Algorithmus zur Berechnung der Metrik NOD . . . . . . . . . . . . . . . Beispielcode mit relevanten Deklarationselementen . . . . . . . . . . . . . Beispiel für Besucherfunktionen zum Auswerten von AST-Daten . . . . . 5 6 10 6.1 6.2 AST-Erzeugung und Parsen einer Java-Quelle . . . . . . . . . . . . . . . Deklarationsbeispiele und deren Zuordnung zu obigen AST-Knotentypen 38 39 A.1 Algorithmus zur Berechnung der Metrik NOI . . . . . . . . . . . . . . . . A.2 Beispiel für Typ-Instanziierungen . . . . . . . . . . . . . . . . . . . . . . A.3 Listing des statistischen Auswerteprogramms . . . . . . . . . . . . . . . . 49 50 60 46 Literaturverzeichnis [1] Martin Äschlimann. org.eclipse.jdt.astview - ast view. 2007. [2] E. Gamma; K. Beck. Addison-Wesley, 2004. Eclipse Erweitern - Prinzipien, Patterns und Plug-ins. [3] Berthold Daum. Java-Entwicklung mit Eclipse 3.2. dpunkt Verlag, 4. edition, 2006. [4] WIKIPEDIA Die freie Enzyklopadie; Artikel http://de.wikipedia.org/wiki/bibliometrie. zum Thema Bibliometrie. [5] Olivier Thomann (IBM); Thomas Kuhn (Eye Media GmbH). Abstract syntax tree. Eclipse Corner Articles, November 2006. [6] Samuel Kotz; Norman L. Johnson. Discrete Distributions. Houghton Mifflin Company Boston, 1969. [7] J. Hartung; B. Elpelt; K.-H. Klösener. Statistik, volume 13. Oldenbourg, 1993. [8] Karl-Rudolf Koch. Grundprinzipien der Bayes-Statistik. pages 253–260, 1998. [9] Philip Mayer. Eine Metrik-Suite zur Analyse des Einsatzes von Interfaces in Java. 2003. [10] O. Moeschlin; E. Grycko; C. Poppinga. Angewandte Statistik - Vorlesungskript 1361 der FernUniversität in Hagen. 2001. [11] Eric Clayberg; Dan Rubel. Building Commercial-Quality Plug-ins. Addison Wesley, 2. edition, 2006. [12] G. Marinell; G. Steckel-Berger. Einführung in die Bayes-Statistik. Oldenburg Verlag, 3. edition, 2001. [13] O. Moeschlin; F. Steinert. Bayessche Statistik. Birkhäuser Verlag, 1995. [14] L. Fahrmeir; R. Künstler; I. Pigeot; G. Tutz. Statistik - Der Weg zur Datenanalyse. Springer Verlag, 5. edition, 2005. [15] Erich Gamma; Richard Helm; Ralph E. Johnson; John Vlissides. Design Patterns. Addison-Wesley, 1995. 47 Inhalt der beiliegenden DVD Verzeichnis Inhalt pdf Master-Arbeit als PDF latex LaTeX-Quellen der Master-Arbeit plugin Typ-Analyse-Plug-in (TAP) project/zip Beispielprogramme project/txt Typ-Analyse-Ergebnisse statistics Statistisches Auswerteprogramm eclipse Eclipse-SDK 3.3 (Europa) jfree jcommon 1.0.8 jfree jfreechart 1.0.4 launch4j launch4j 3.0.0-pre2 Tabelle 7.2: Inhalt der beiliegenden DVD 48 A Anhang A.1 Metrik - Number of Instantiations (NOI) Diese Metrik wird für die aktuelle Arbeit nicht benötigt, kann aber durch einen relativ geringen Zusatzaufwand realisiert werden, da das Grundgerüst der Software dies erlaubt. Die Metrik mißt die Instanziierungen von Typen in Java-Programmen und kann folgendermaßen angegeben werden: N OI(A) = | {new A} | (A.1) wobei gilt: A = Typ Für das Analyse-Plug-in ergibt sich der folgende Algorithmus in Form von Pseudocode: 1 2 3 4 f or each instantiationElement of actualProject do { Type := getTypeOfInstantiationElement ; CountingArray [ Type ]++; } Listing A.1: Algorithmus zur Berechnung der Metrik NOI 49 ANHANG A. ANHANG A.2 Ermittlung der Typ-Instanziierungen Entsprechend der Ermittlung von Deklarationen gibt es auch einen AST-Knotentyp zur Ermittlung von Typ-Instanziierungen, wie die folgenden Tabelle A.1 zeigt. Diese wurde für spätere Erweiterungen mit implementiert, da nur ein weiterer Visitor notwendig wurde. AST-Knotentyp Verwendung ClassInstanceCreation Typ-Instanziierung Tabelle A.1: AST-Knoten, der Typ-Instanziierungen repräsentiert Das Listing A.2 zeigt ein Codebeispiel für den typischen Anwendungsfall von TypInstanziierungen. 1 2 3 4 5 6 7 8 9 public c l a s s publicType { ... public void publicType ( ) { Integer i = new Integer ( 1 5 ) ; String s = new String ( "Test" ) ; List<String> l = new ArrayList<String >() ; ... } } Listing A.2: Beispiel für Typ-Instanziierungen Bei der Analyse werden alle Vorkommen von Typ-Instanziierungen gezählt, die mit dem Operator new“ vorgenommen werden. Für die Betrachtungen in dieser Arbeit hat diese ” Metrik keine Bedeutung. Sie kann sehr leicht als Erweiterung des Typ-Analyse-Plugins implementiert werden, da nur ein einziger neuer Knotentyp ClassInstanceCreation“ ” in die Analyse mit eingebaut werden muß. Der Einfachheit halber wurde sie deshalb realisiert, um eventuell für weitere Arbeiten als Grundlage zu dienen. Im Beispiel aus A.2 werden 3 Typen jeweils einmal instanziiert, sodaß das Analyseergebnis wie folgt aussieht: Typ Anzahl der Typ-Instanziierungen Integer 1 String 1 List<String> 1 Tabelle A.2: Beispielprogramm von Typ-Instanziierungen 50 ANHANG A. ANHANG Für das Programm JUnit 3.8.2“ sieht die Ausgabe für Instanziierungen wie in Abbildung ” A.1 folgt aus. Der Typ der einzelnen Säulen bleibt dabei unverändert an der gleichen Position wie bei der Darstellung der NOD-Metrik. Dies bedeutet, man kann einfach vergleichen, wie oft ein bestimmter Typ in einer Deklaration verwendet bzw. wie oft er instanziiert wird. Abbildung A.1: Histogramm der Typ-Instanziierungen 51 ANHANG A. ANHANG A.3 Typ-Analyse-Plug-in (TAP) Die folgenden Unterkapitel beschreiben die Installation, Anwendungung, Erstellung und den Start des Typ-Analyse-Plug-ins. A.3.1 Installation des Plug-ins Das vorliegende Plug-in steht als JAR-Archiv zur Verfügung. Der Name lautet de.feu.cap x.y.z.jar“, wobei x.y.z“ die aktuelle Versionsnummer darstellt. Es muß in das ” ” Unterverzeichnis plugins“ der Eclipse-Installation abgelegt werden. Nach erfolgreicher ” Installation sollte sich in der Ansicht Plug-in Details“ der Eclipse-Platform der folgende ” Eintrag wiederfinden. Abbildung A.2: Plug-in-Detail Details zu aktuell installierten Plug-ins kann man durch Aktivierung des Menübefehls Help->About Eclipse SDK“ und anschließender Selektion der Schaltfläche Plug-in De” ” tails“ zur Anzeige bringen. A.3.2 Anwendung des Typ-Analyse-Plug-ins Die Typanalyse kann mittels rechtem Mausklick auf ein Eclipse-Projekt im Package ” Explorer“ der Eclipse-Plattform aktiviert werden. Dazu muss das Projekt geöffnet sein. Der auszuwählende Menüpunkt ist mit Start Type Analysis“ beschriftet und befindet ” sich am unteren Ende des Popup-Menüs. Anschließend startet die Typanalyse, welche sämtliche zum Projekt gehörenden Dateien mit der Endung .java“ mit einbezieht. ” 52 ANHANG A. ANHANG A.3.3 File List View für die Ausgabe aller analysierten Java-Quellen Die folgende Abbildung A.3 zeigt die Ansicht der Quell-Dateien des aktuell analysierten Programms. Diese Ansicht dient zur Kontrolle der analysierten Java-Quellen. Dies sind sämtliche Dateien mit der Endung .java“ des ausgewählten Programms. ” Abbildung A.3: Datei-Ansicht des ausgewählten Programms Die dargestellten Dateien wurden für die Analyse herangezogen, indem jeweils ihr zugehöriger AST erzeugt wurde und in diesem nach den relevanten Deklarationselementen gesucht wurde, die für die Bestimmung der Häufigkeit der Verwendung von Typen herangezogen wurden, siehe auch Kapitel 3.2. Die Informationen werden dabei in Listen gespeichert. Zum einen wird für jeden Typ die Anzahl seiner Verwendung aufsummiert und zum anderen wird festgehalten, in welchem anderen Typ der gefundene Typ vorkommt. Mit diesen Informationen sind dann die Ausgaben in den einzelnen Ansichten möglich, inklusive der Umschaltung zwischen den beiden Metriken NOD und NOI. 53 ANHANG A. ANHANG A.3.4 Paketstruktur des Plug-ins Die folgendende Tabelle A.3 zeigt die Paketstruktur des Eclipse-Projekts. Pakete de.feu.tap de.feu.tap.popup.actions de.feu.tap.views Klassen Activator DetailType FileFinder SearchResult TypeDeclarationVisitor TypeListElement TypeNumber XYBarToolTipGenerator NewAction DetailDiagramView DetailListView FileListView TypeDiagramView TypeListView Tabelle A.3: Pakete und Klassen des Typ-Analyse-Plug-ins 54 ANHANG A. ANHANG A.3.5 Erstellen der JAR-Datei Zuerst muß aus dem durch einen rechten Mausklick auf das geöffnete Eclipse-Projekt de.feu.tap“ erscheinenden Kontextmenü , der Menüpunkt Export...“ ausgewählt wer” ” den. Daraufhin startet der Export-Wizard. In der nun folgenden Ansicht muß der dargestellte Eintrag Deployable plug-ins and fragments“ selektiert werden. ” Abbildung A.4: Selektion des Exports 55 ANHANG A. ANHANG In der nächsten Ansicht wird der Ort für die zu erzeugende JAR-Datei spezifiziert. Dies kann entweder ein Verzeichnis (Option Directory“) sein, in dem dann ein Verzeichnis ” plugins“ erzeugt wird, welches die JAR-Datei enthält. Eine andere Möglichkeit ist die ” Angabe einer Archivdatei (Option Archive file“), welche dann anschließend das JAR” Archiv enthält. Abbildung A.5: Ablage der JAR-Datei In beiden Fällen wird das für die weitere Arbeit benötigte JAR-Archiv de.feu.tap 1.0.0.jar“ ” erzeugt. 56 ANHANG A. ANHANG Im letzten Schritt sollten noch die Einstellungen auf der Registerkarte Options“ über” prüft werden und sichergestellt werden, daß für das Plug-in ein individuelles JAR-Archiv erzeugt wird. Abbildung A.6: Optionen des Exports A.3.6 Hinzufügen der JFreeChart-Bibliotheken Um die JFreeChart-Bibliotheken hinzufügen zu können, muß das JAR-Archiv mit WinZip geöffnet werden. Anschließend müssen die entpackten JFreeChart-Bibliotheken manuell dem Archiv hinzugefügt werden. Hierbei handelt es sich um die beiden JARs jcommon-1.0.8.jar“ und jfreechart-1.0.4.jar“ Dies geschieht am einfachsten mittels ” ” Drag&Drop des entpackten Unterverzeichnisses org“ der beiden Bibliotheken in das ” 57 ANHANG A. ANHANG geöffnete WinZip. Beim Entpacken der beiden JARs entsteht ein gemeinsames Unterverzeichnis, in dem die kompletten Bibliotheken enthalten sind. Wichtig ist hierbei, daß die bereits im JAR-Archiv des Plug-ins enthaltene Datei MANIFEST.MF“, im Unter” verzeichnis meta-inf“, nicht mit der gleichnamigen Datei der JFreeChart-Bibliothek ” überschrieben wird. Im Normallfall warnt WinZip standardmäßig vor dem Überschreiben. Die gleichnamige Datei MANIFEST.MF“ der Grafik-Bibliothek kann einfach ge” löscht werden, da sie nicht benötigt wird. Dies vermeidet den angesprochenen Konflikt. A.3.7 Ausgaben des Typ-Analyse-Plug-ins Zur Ausgabe der Analyseergebnisse werden in der aktuellen Version 1.0.0 die folgenden Dateiformate verwendet: • TXT - enthält die sortierten Typ-Vorkommen ohne Namen im Text-Format • XLS - enthält die sortierten Typ-Vorkommen inklusive Namen im Excel-Format Diese Dateien können für die Weiterverarbeitung in anderen Programmen dienen. Mit der XLS-Datei besteht außerdem die Möglichkeit, die Histogramme in Excel darzustellen. Zusätzlich enthält sie auch die kummulierten und auf die Gesamtsumme aller Typvorkommen normierten Summen der einzelnen Typ-Vorkommen, die für weitergehende Darstellungen/Berechnungen dienen können. 58 ANHANG A. ANHANG A.3.8 Start der Typ-Analyse Zum Start der Typ-Analyse muß man wie folgt vorgehen. Zunächst öffnet man mit einem rechten Mausklick auf ein geöffnetes Eclipse-Projekt das zugehörige Kontextmenü, dargestellt in Abbildung A.7. Dieses enthält einen Menüpunkt zum Starten der Typanalyse. Nach Auswahl dieses Menüpunktes werden nach einer Bearbeitungszeit, abhängig von der Größe des Projekts, automatisch die fünf vordefinierten Ansichten in der EclipseWorkbench angezeigt. Falls die Analyse für ein anderes Projekt gestartet wird, so werden alle Daten und Grafiken automatisch aktualisiert. Abbildung A.7: Start der Typ-Analyse 59 ANHANG A. ANHANG A.4 Statistisches Auswerteprogramm A.4.1 Listing des Programms 1 2 /* Java - Programm zur Bestimmung der Posteriori - Wahrscheinlichkeit und der Dichtefunktion */ package de . feu . bayeschart ; 3 4 5 6 7 8 import import import import import java . awt . Color ; java . awt . GridLayout ; java . io . BufferedReader ; java . io . FileReader ; java . io . IOException ; 9 10 import javax . swing . JOptionPane ; 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import import import import import import import import import import import import import import org . jfree . chart . ChartFactory ; org . jfree . chart . ChartPanel ; org . jfree . chart . JFreeChart ; org . jfree . chart . axis . NumberAxis ; org . jfree . chart . plot . PlotOrientation ; org . jfree . chart . plot . XYPlot ; org . jfree . chart . renderer . xy . XYLineAndShapeRenderer ; org . jfree . chart . title . TextTitle ; org . jfree . chart . title . Title ; org . jfree . data . xy . XYDataset ; org . jfree . data . xy . XYSeries ; org . jfree . data . xy . XYSeriesCollection ; org . jfree . ui . ApplicationFrame ; org . jfree . ui . RefineryUtilities ; 26 27 28 29 /* Applikation zur Anzeige von zwei Liniencharts basierend auf der Klasse XYDataset */ public c l a s s BayesChart extends ApplicationFrame { 30 31 32 33 34 35 36 37 38 39 40 41 private s t a t i c int anz = 0 ; private s t a t i c f i n a l int Npar = 1 0 0 ; private s t a t i c f i n a l int num = 6 ; private s t a t i c String dataFile = null ; s t a t i c double [ ] [ ] K = null ; s t a t i c double [ ] prior = new double [ 2 ∗ Npar + 1 ] ; s t a t i c double [ ] post = new double [ 2 ∗ Npar + 1 ] ; s t a t i c double [ ] rho = new double [ 2 ∗ Npar + 1 ] ; s t a t i c int [ ] sam = null ; s t a t i c double [ ] rh = null ; s t a t i c double [ ] cof = new double [ num ] ; 60 ANHANG A. ANHANG 42 43 44 45 46 char ch ; int gammaoptYule = 0 ; int gammaoptZipf = 0 ; double diffYule = 0 . 0 ; double diffZipf = 0 . 0 ; 47 48 private s t a t i c f i n a l long serialVersionUID = 1L ; 49 50 51 /* Erzeugt eine neue Applikation */ public BayesChart ( f i n a l String title ) { 52 super ( title + " - [" + dataFile . toString ( ) + "]" ) ; getContentPane ( ) . setLayout (new GridLayout ( 1 , 2 ) ) ; 53 54 55 f i n a l XYDataset dataset1 = createDataset1 ( ) ; f i n a l XYDataset dataset2 = createDataset2 ( ) ; f i n a l JFreeChart chart1 = createChart1 ( dataset1 ) ; f i n a l JFreeChart chart2 = createChart2 ( dataset2 ) ; f i n a l ChartPanel chartPanel1 = new ChartPanel ( chart1 ) ; f i n a l ChartPanel chartPanel2 = new ChartPanel ( chart2 ) ; chartPanel1 . setPreferredSize (new java . awt . Dimension ( 5 0 0 , 3 5 0 ) ) ; chartPanel2 . setPreferredSize (new java . awt . Dimension ( 5 0 0 , 3 5 0 ) ) ; getContentPane ( ) . add ( chartPanel1 ) ; getContentPane ( ) . add ( chartPanel2 ) ; 56 57 58 59 60 61 62 63 64 65 66 } 67 68 69 70 /* Ezeugt ein neues Linienchart fuer die Dichtefunktionen beiden Verteilungen basierend auf einem XYDataset */ private JFreeChart createChart1 ( f i n a l XYDataset dataset ) { 71 72 73 74 75 76 77 78 79 80 81 // Neues Chart erzeugen f i n a l JFreeChart chart = ChartFactory . createXYLineChart ( "Yule -/ Zipf -Estoup - Verteilung " , // chart title "rho" , // x axis label " Wahrscheinlichkeit " , // y axis label dataset , // data PlotOrientation . VERTICAL , // vertical orientation true , // include legend false , // tooltips false ) ; // urls 82 83 84 Title subTitle = new TextTitle ( " Posteriori " ) ; chart . addSubtitle ( subTitle ) ; 85 86 87 // Einstellungen fuer das Chart vornehmen chart . setBackgroundPaint ( Color . white ) ; 61 ANHANG A. ANHANG 88 f i n a l XYPlot plot = chart . getXYPlot ( ) ; plot . setBackgroundPaint ( Color . lightGray ) ; plot . setDomainGridlinePaint ( Color . white ) ; plot . setRangeGridlinePaint ( Color . white ) ; 89 90 91 92 93 f i n a l XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer ( ) ; renderer . setSeriesShapesVisible ( 0 , f a l s e ) ; renderer . setSeriesShapesVisible ( 1 , f a l s e ) ; renderer . setSeriesPaint ( 0 , Color . red ) ; renderer . setSeriesPaint ( 1 , Color . magenta ) ; plot . setRenderer ( renderer ) ; 94 95 96 97 98 99 100 // Beide Achsen auf Integer - Anzeigewerte umstellen f i n a l NumberAxis rangeAxis = ( NumberAxis ) plot . getRangeAxis ( ) ; rangeAxis . setStandardTickUnits ( NumberAxis . createIntegerTickUnits ( ) ) ; f i n a l NumberAxis domainAxis = ( NumberAxis ) plot . getDomainAxis () ; domainAxis . setStandardTickUnits ( NumberAxis . createIntegerTickUnits ( ) ) ; 101 102 103 104 105 106 return chart ; 107 108 } 109 110 111 112 113 114 115 116 117 118 119 120 121 122 /* Ezeugt ein neues Linienchart fuer die Anzeige der Posteriori - Verteilungen basierend auf einem XYDataset */ private JFreeChart createChart2 ( f i n a l XYDataset dataset ) { // Neues Chart erzeugen f i n a l JFreeChart chart = ChartFactory . createXYLineChart ( " Analysedaten - vs. Opt.- Verteilung " , // chart title " Typen " , // x axis label " Wahrscheinlichkeit " , // y axis label dataset , // data PlotOrientation . VERTICAL , // vertical orient . true , // include legend false , // tooltips false ) ; // urls 123 124 125 Title subTitle = new TextTitle ( "Delta: [Yule ]=" + diffYule + " [Zipf - Estoup ]=" + diffZipf ) ; chart . addSubtitle ( subTitle ) ; 126 127 128 // Einstellungen fuer das Chart vornehmen chart . setBackgroundPaint ( Color . white ) ; 62 ANHANG A. ANHANG 129 f i n a l XYPlot plot = chart . getXYPlot ( ) ; plot . setBackgroundPaint ( Color . lightGray ) ; plot . setDomainGridlinePaint ( Color . white ) ; plot . setRangeGridlinePaint ( Color . white ) ; 130 131 132 133 134 f i n a l XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer ( ) ; renderer . setSeriesShapesVisible ( 0 , f a l s e ) ; renderer . setSeriesShapesVisible ( 1 , f a l s e ) ; renderer . setSeriesShapesVisible ( 2 , f a l s e ) ; renderer . setSeriesPaint ( 0 , Color . blue ) ; renderer . setSeriesPaint ( 1 , Color . red ) ; renderer . setSeriesPaint ( 2 , Color . magenta ) ; plot . setRenderer ( renderer ) ; 135 136 137 138 139 140 141 142 143 // Beide Achsen auf Integer - Anzeigewerte umstellen f i n a l NumberAxis rangeAxis = ( NumberAxis ) plot . getRangeAxis ( ) ; rangeAxis . setStandardTickUnits ( NumberAxis . createIntegerTickUnits ( ) ) ; f i n a l NumberAxis domainAxis = ( NumberAxis ) plot . getDomainAxis () ; domainAxis . setStandardTickUnits ( NumberAxis . createIntegerTickUnits ( ) ) ; 144 145 146 147 148 149 return chart ; 150 151 } 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 /* Einsprungpunkt main des Statistikprogramms */ public s t a t i c void main ( f i n a l String [ ] args ) { // Check file name i f ( args . length == 0 ) { JOptionPane . showMessageDialog ( null , "No command line argument (data filename ) specified !\n Please specify the data filename as the only argument " , " Bayes chart " , JOptionPane . ERROR_MESSAGE ) ; } else { // Speichern des TXT - Dateinamens dataFile = args [ 0 ] ; // TXT - Datei einlesen readFile ( args [ 0 ] ) ; // Daten bestimmen calcData ( ) ; // Bestimmung der Posteriori - Wahrscheinlichkeit estimateData ( ) ; 170 63 ANHANG A. ANHANG // Neue Charts erzeugen f i n a l BayesChart demo = new BayesChart ( "Bayes Chart" ) ; demo . pack ( ) ; RefineryUtilities . centerFrameOnScreen ( demo ) ; demo . setVisible ( true ) ; 171 172 173 174 175 } 176 177 } 178 179 180 181 182 /* Berechnung der Gamma - Funktion . Es wird der Logarithmus der Gamma - Funktion berechnet */ public s t a t i c double LNGamma ( double xx ) { double x , y , ser , tmp ; 183 cof [ 0 ] = 7 6 . 1 8 0 0 9 1 7 2 9 4 7 1 4 6 ; cof [ 1 ] = −86.50532032941677; cof [ 2 ] = 2 4 . 0 1 4 0 9 8 2 4 0 8 3 0 9 1 ; cof [ 3 ] = −1.231739572450155; cof [ 4 ] = 0 . 1 2 0 8 6 5 0 9 7 3 8 6 6 1 7 9 E −2; cof [ 5 ] = −0.5395239384953 E −5; x = xx ; y = x; tmp = x + 5 . 5 ; tmp = tmp − ( x + 0 . 5 ) ∗ Math . log ( tmp ) ; ser = 1 . 0 0 0 0 0 0 0 0 0 1 9 0 0 1 5 ; f or ( int j = 0 ; j < num ; j++) { y = y + 1.0; ser = ser + cof [ j ] / y ; } return −tmp + Math . log ( 2 . 5 0 6 6 2 8 2 7 4 6 3 1 0 0 0 5 ∗ ser / x ) ; 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 } 201 202 203 204 205 206 /* Berechnung der Beta - Funktion mit Hilfe der Gamma - Funktion . Man beachte , dass der Logarithmus der Gamma - Funktion verwendet wird */ public s t a t i c double Beta ( double a , double b ) { double expo ; 207 expo = LNGamma ( a ) + LNGamma ( b ) − LNGamma ( a + b ) ; return Math . exp ( expo ) ; 208 209 210 } 211 212 213 214 215 216 /* Einlesen der Daten aus der TXT - Datei in das Feld sam */ private s t a t i c void readFile ( String dataFile ) { // Pruefen des Dateinamens der TXT - Datei i f ( dataFile != null ) { // Erzeuge buffered reader 64 ANHANG A. ANHANG BufferedReader br = null ; try { // Oeffne buffered reader br = new BufferedReader (new FileReader ( dataFile ) ) ; } catch ( IOException e ) { e . printStackTrace ( ) ; } try { int lineNumber = 0 ; // Berechne Zeilenanzahl der TXT - Datei while ( br . readLine ( ) != null ) { lineNumber++; } 217 218 219 220 221 222 223 224 225 226 227 228 229 230 // Puffer allokieren entsprechen der Zeilenanzahl ; sam = new int [ lineNumber ] ; K = new double [ 2 ∗ Npar + 1 ] [ lineNumber ] ; rh = new double [ lineNumber ] ; anz = lineNumber ; 231 232 233 234 235 236 // Oeffne neuen buffered reader br = new BufferedReader (new FileReader ( dataFile ) ) ; // Lese Anzahl der Zeilen f or ( int i = 0 ; i < lineNumber ; i++) { sam [ i ] = Integer . valueOf ( br . readLine ( ) ) . intValue ( ) ; } } catch ( IOException e ) { e . printStackTrace ( ) ; } try { // Schliesse buffered reader br . close ( ) ; } catch ( IOException e ) { e . printStackTrace ( ) ; } 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 } 252 253 } 254 255 256 257 258 /* Berechnung der Normalverteilung */ private s t a t i c void calcData ( ) { int i , j ; double sum ; 259 260 261 262 sum = 0 . 0 ; f or ( i = 0 ; i < anz ; i++) { rh [ i ] = sam [ i ] ; 65 ANHANG A. ANHANG sum = sum + rh [ i ] ; 263 } 264 265 f or ( i = 0 ; i < anz ; i++) { rh [ i ] = rh [ i ] / sum ; } 266 267 268 269 f or ( i = −Npar ; i <= Npar ; i++) { prior [ i + Npar ] = 1 . 0 / ( 2 . 0 ∗ Npar + 1 . 0 ) ; } 270 271 272 273 f or ( i = −Npar ; i <= Npar ; i++) { rho [ i + Npar ] = Math . abs ( i / ( double ) Npar ∗ 5 . 0 ) ; } 274 275 276 277 /* Berechnung der Koeffizienten der Matrix K Vergleiche auch Rekursionsalgorithmus */ // ZIPF/ ESTOUP f or ( i = 0 ; i <= Npar ; i++) { sum = 0 . 0 ; f or ( j = 0 ; j < anz ; j++) { K [ i + Npar ] [ j ] = 1 . 0 / Math . exp ( rho [ i + Npar ] ∗ Math . log ( j + 1) ) ; sum = sum + K [ i + Npar ] [ j ] ; } f or ( j = 0 ; j < anz ; j++) { K [ i + Npar ] [ j ] = K [ i + Npar ] [ j ] / sum ; } } 278 279 280 281 282 283 284 285 286 287 288 289 290 291 // YULE f or ( i = −Npar ; i <= −1; i++) { sum = 0 . 0 ; f or ( j = 0 ; j < anz ; j++) { K [ i + Npar ] [ j ] = Beta ( j + 1 . 0 , rho [ i + Npar ] + 1 . 0 ) ; sum = sum + K [ i + Npar ] [ j ] ; } f or ( j = 0 ; j < anz ; j++) { K [ i + Npar ] [ j ] = K [ i + Npar ] [ j ] / sum ; } } 292 293 294 295 296 297 298 299 300 301 302 303 } 304 305 306 307 /* Anwendung der Summenformel des Satzes von Bayes Bestimmung der Posteriori - Wahrscheinlichkeit */ s t a t i c void estimateData ( ) { 66 ANHANG A. ANHANG int i , j , gamma ; double sum ; 308 309 310 f or ( i = anz − 1 ; i >= 0 ; i−−) { f or ( j = 1 ; j < sam [ i ] ; j++) { sum = 0 . 0 ; f or ( gamma = −Npar ; gamma <= Npar ; gamma++) post [ gamma + Npar ] = K [ gamma + Npar ] [ i ] ∗ Npar ] ; sum = sum + post [ gamma + Npar ] ; } f or ( gamma = −Npar ; gamma <= Npar ; gamma++) post [ gamma + Npar ] = post [ gamma + Npar ] / } prior = post ; } } 311 312 313 314 315 316 317 318 319 320 321 322 323 324 { prior [ gamma + { sum ; } 325 326 327 328 329 330 /* Erzeugen der Datensaetze fuer die Dichtefunkionen */ private XYDataset createDataset1 ( ) { int gamma ; double xk , yk ; double yscale = Npar ; 331 f i n a l XYSeries series1 = new XYSeries ( "Yule" ) ; f or ( gamma = −Npar ; gamma <= −1; gamma++) { xk = rho [ gamma + Npar ] ; yk = Math . round ( post [ gamma + Npar ] ∗ yscale ) ; series1 . add ( xk , yk ) ; } 332 333 334 335 336 337 338 f i n a l XYSeries series2 = new XYSeries ( "Zipf - Estoup " ) ; f or ( gamma = 0 ; gamma <= Npar ; gamma++) { xk = rho [ gamma + Npar ] ; yk = Math . round ( post [ gamma + Npar ] ∗ yscale ) ; series2 . add ( xk , yk ) ; } 339 340 341 342 343 344 345 f i n a l XYSeriesCollection dataset = new XYSeriesCollection ( ) ; dataset . addSeries ( series1 ) ; dataset . addSeries ( series2 ) ; return dataset ; 346 347 348 349 350 } 351 352 /* Erzeugen der Datensaetze fuer die Posteriori - Wahrscheinlich - 67 ANHANG A. ANHANG 353 354 355 356 357 358 359 360 keiten , basierend auf den Analysedaten . Bestimmen des jeweiligen Optimums fuer rho */ private XYDataset createDataset2 ( ) { int gamma ; int i ; double xk , yk ; double max ; double scale = Npar ∗ 2 0 ; 361 362 363 364 365 366 367 368 max = 0 . 0 ; f or ( gamma = −Npar ; gamma <= −1; gamma++) { i f ( post [ gamma + Npar ] > max ) { max = post [ gamma + Npar ] ; gammaoptYule = gamma + Npar ; } } 369 370 371 372 373 374 375 376 377 378 max = 0 . 0 ; f or ( gamma = 0 ; gamma <= Npar ; gamma++) { i f ( post [ gamma + Npar ] > max ) { max = post [ gamma + Npar ] ; gammaoptZipf = gamma + Npar ; } } System . out . println ( "Rho[ GammaOptYule ] := " + rho [ gammaoptYule ]) ; System . out . println ( "Rho[ GammaOptZE ] := " + rho [ gammaoptZipf ]) ; 379 380 381 382 383 384 385 386 scale = anz ∗ 3 0 . 0 ; f i n a l XYSeries series1 = new XYSeries ( " Analysedaten " ) ; f or ( i = 0 ; i < anz ; i++) { xk = i + 1 ; yk = Math . round ( scale ∗ rh [ i ] ) ; series1 . add ( xk , yk ) ; } 387 388 389 390 391 392 393 f i n a l XYSeries series2 = new XYSeries ( "Opt. Yule" ) ; f or ( i = 0 ; i < anz ; i++) { xk = i + 1 ; yk = Math . round ( scale ∗ K [ gammaoptYule ] [ i ] ) ; series2 . add ( xk , yk ) ; } 394 395 396 f i n a l XYSeries series3 = new XYSeries ( "Opt. Zipf/ Estoup " ) ; f or ( i = 0 ; i < anz ; i++) { 68 ANHANG A. ANHANG xk = i + 1 ; yk = Math . round ( scale ∗ K [ gammaoptZipf ] [ i ] ) ; series3 . add ( xk , yk ) ; 397 398 399 } 400 401 /* Berechnung des Deltas zwischen opt. Verteilung und den Analysedaten */ f or ( i = 0 ; i < anz ; i++) { diffYule += Math . abs ( rh [ i ] − K [ gammaoptYule ] [ i ] ) ; diffZipf += Math . abs ( rh [ i ] − K [ gammaoptZipf ] [ i ] ) ; } 402 403 404 405 406 407 System . out . println ( " Delta[Yule ]=" + diffYule ) ; System . out . println ( " Delta[Zipf ]=" + diffZipf ) ; 408 409 410 f i n a l XYSeriesCollection dataset = new XYSeriesCollection ( ) ; dataset . addSeries ( series1 ) ; dataset . addSeries ( series2 ) ; dataset . addSeries ( series3 ) ; return dataset ; 411 412 413 414 415 } 416 417 } Listing A.3: Listing des statistischen Auswerteprogramms 69 ANHANG A. ANHANG A.4.2 Erstellen des Eclipse-Projekts Das statistischen Auswerteprogramm wird aus dem, auf der beiliegenden DVD verfügbaren ZIP-Archiv bayeschart.zip“ als Projekt in Eclipse importiert. Dazu muss zuerst ” ein leeres Java-Projekt erzeugt werden. Anschließend kann das ZIP-Archiv in das neue Projekt importiert werden. A.4.3 Erstellen der JAR-Datei Zum Erzeugen der EXE-Datei des statistischen Auswerteprogramms muß zunächst eine JAR-Datei exportiert werden. Dies geschieht mit dem Export-Wizard aus Eclipse heraus. Er wird mit einem rechten Mausklick auf das geöffnete Programm gestartet ( Export...“). ” Danach erscheint das folgende Fenster. Abbildung A.8: JAR-Export 70 ANHANG A. ANHANG Hier muss die Option JAR file“ ausgewählt werden. Im nächsten Schritt werden die ” Komponenten selektiert, die im Export enthalten sein sollen. Hier nur die auf der linken Seite vorhandene Ressource de.feu.bayeschart“ selektieren. Auf der rechten Seite alle ” Dateien deselektieren, da hier für die Datensicherung des Programms bereits Dokumente und fertige JAR- bzw. EXE-Dateien gesichert wurden. Weiter unten im Fenster muß das Ausgabeverzeichnis inklusive JAR-Dateiname spezifiziert werden. Abbildung A.9: Ressourcen und JAR-Dateiname 71 ANHANG A. ANHANG Im nächsten Schritt sollten keine Veränderungen vorgenommen werden. Abbildung A.10: JAR-Packet-Optionen 72 ANHANG A. ANHANG Im letzten Schritt muß das bereits existierende Manifest MANIFEST.MF“ ausgewählt ” werden, welches den Klassenamen enthält, der die Methode main“ beinhaltet, die dann ” beim Start der EXE ausgeführt wird. Abbildung A.11: JAR-Manifest-Spezifikation Mit Auswahl der Schaltfläche Finish“ wird die JAR-Datei im angegebenen Verzeichnis ” erstellt. Nun muß ebenfalls, wie bei der Plug-in-Erstellung vom Typ-Analyse-Plug-in bereits beschrieben, die Grafik-Bibliothek manuell zu der JAR-Datei hinzugefügt werden. Dieser Schritt ist bereits im Kapitel A.3.6 beschrieben worden. Der Ablauf ist aber derselbe ohne Änderungen im Ablauf. 73 ANHANG A. ANHANG A.4.4 Erstellen der EXE-Datei Mit dem Freeware-Tool Launch4j“ (Applikations-Wrapper) kann die vorher erzeugte ” JAR-Datei in eine EXE-Datei umgewandelt werden. Hierzu sind die folgenden Schritte notwendig. Abbildung A.12: Grundeinstellungen Hier die zu erstellende EXE-Datei spezifizieren und das vorhandene JAR auswählen. 74 ANHANG A. ANHANG Bei der folgenden Ansicht müssen keine Änderungen vorgenommen werden. Der sogenannte Classpath“ wird automatisch aus der Manifest-Datei MANIFEST.MF“ des ” ” JARs bezogen. Abbildung A.13: Classpath 75 ANHANG A. ANHANG Bei dieser Ansicht muß die Minimalversion des Java Runtime Environments“ (JRE) ” spezifiziert werden. Für die verwendete Grafik-Bibliothek JFreeChart 1.0.8“ inklusive ” JCommon 1.0.4“ muss die minimale JRE-Version 1.3.0 betragen. Bei zukünftigen Wei” terentwicklungen der JFreeChart-Bibliothek kann sich dies zu einer höheren Version hin ändern. Abbildung A.14: Minimum JRE Nach dem Start des Build-Wrappers wird die EXE-Datei durch die Auswahl der Zahnrad” Ikone“ in der Button-Bar erzeugt. Es sollten sich die gleichen Ausgaben, wie im Fenster mit der Bezeichnung Log“ dargestellt, ergeben. Die Einstellungen können auch in einer ” XML-Datei abgelegt werden, um beim nächsten Erstellen nicht wieder neu eingegeben 76 ANHANG A. ANHANG werden zu müssen. Zum Test kann die generierte EXE-Datei sofort mit einem Doppelklick gestartet werden. Dies sollte die nachstehende Fehlermeldung zur Folge haben, da kein Kommandozeilenargument angegeben wurde. Man kann dann aber trotzdem von einer korrekten Erstellung der EXE-Datei ausgehen. Abbildung A.15: Start-Fehlermeldung 77 Erklärung Hiermit erkläre ich, daß ich die vorliegende Abschlussarbeit selbstständig verfaßt, noch nicht anderweitig für Prüfungszwecke vorgelegt, keine anderen als die angegebenen Quellen und Hilfsmittel benutzt, sowie wörtliche und sinngemäße Zitate als solche gekennzeichnet habe. Stuttgart, den 26. Februar 2008 ——————————————– Gerd Eichhorn 78