-
- The Prototype
-
-
- Research
Software Engineering for Smart Data Analytics & Smart Data Analytics for Software Engineering
In diesem Tutorial soll gezeigt werden, wie man in einem Testprojekt einen einfachen Detektor zum Erkennen von “;” ohne vorherige Statements schreibt. So dass also der folgende Code zu zwei Warnungen führt:
package a; public class A { public void x() { ;; } }
Dies entspricht dem bereits in Cultivate enthaltenen NopSmell-Detektor. Dabei wird hauptsächlich der syntaktische Rahmen für Detektoren erklärt, auf Prolog selber und Smell-Erkennung im Allgemeinen wird nicht eingegangen.
siehe cultivateconfiguration
Downloaden Sie die Datei “test-project.zip”, die ein Projekt “test” enthält. Für das Tutorial muss dieses Projekt im Workspace von Eclipse verfügbar sein. Alternativ kann einfach die unter 0. genannte Klasse in ein leeres Projekt kopiert werden.
Sowohl die JTransformer als auch die Prolog Project Nature müssen im Testprojekt aktiviert sein. Dies kann per Rechtklick auf den Projekt-Ordner geprüft werden. Allerdings kann man pro Workspace nur eine JTransformer-Nature haben, d.h. eine eventuell gesetzt andere JTransformer-Nature muss vorher abgewählt werden. Achtung: Sie ist nur als aktiv sichtbar, wenn der JTransformer schon gestartet ist.
In der Eigenschaftsseite des Projektes muss unter dem Punkt Cultivate die Cultivate Nature aktiv sein. Dieser Punkt ist nur verfügbar, wenn vorher die JTransformer Nature aktiviert wurde.
Im Hautpverzeichnis des test-Projektes muss eine Datei “conventions.pl” mit folgendem Inhalt angelegt werden:
:- multifile detector_/4. :- multifile detector_description/4.
Projektspezifische Detektoren können in dieser Datei oder in von dieser Datei über
:- consult('eineAndereDatei.pl'). :- consult('inEinemUnterverzeichnis/nochEineAndereDatei.pl').
verlinkten Dateien abgelegt werden.
In die Datei “conventions.pl” wird nun ein erster Detektor eingefügt:
detector_description(meinErsterDetektor,'ein kurzer Hilfetext',statement,unrated). detector_(meinErsterDetektor,occurrence,[(statement,CallID)],[]) :- fail.
Dabei haben die Argumente der Prädikate folgende Bedeutung:
detector_description
detector_
(Unterstrich beachten!)Nach dem Speichern von “conventions.pl” sollten über den Menüpunkt “Refresh Detector Definitions” im Kontextmenu des Projektes jetzt die Detektor-Beschreibungen aktualisiert werden:
Über den Menüpunkt “Window / Show View / Other” und dann “Cultivate / Metrics and Detector Results” kann man die Ansicht mit den Detektor-Ergebnissen öffnen. Diese Ansicht hat rechts oben ein Menü, in dem man den eben geschriebenen Detektor “meinErsterDetektor” auswählen kann:
Er liefert allerdings noch keine Ergebnisse zurück, da er mit “fail” implementiert ist, d.h. dass heißt, dass es auf kein Quelltextelement zutrifft.
Als nächstes ersetzt man das “detector_(meinErsterDetektor,..)” - Prädikat durch folgendes:
detector_(meinErsterDetektor, occurrence, [(statement, CallID)], []) :- nopT(CallID, _, _).
Das war einfach, oder?
Wir greifen auf eine Darstellung unseres Java Codes in einer Faktenbasis zu. Man darf sich darunter ruhig auch eine relationale Datenbank vorstellen, auf die man aber mit einer ausgefuchsteren Abfragesprache (Prolog) zugreifen kann. Für diesen Detektor hatten wir das Glück, dass ein einsames Semikolon durch einen einfachen Fakt nopT repräsentiert ist. (Relational gedacht: Durch ein Tupel in der Tabelle nopT).
Auf weitere Details wird hier nicht eingegangen, auf folgenden Seiten kann man aber weitere Informationen dazu finden:
Nach dem Speichern von “conventions.pl” sollten über den Menüpunkt “Refresh Detector Definitions” im Kontextmenu des Projektes jetzt die Detektor-Beschreibungen aktualisiert werden.
In der Ansicht “Metric and Detector Results” sollten nun zwei Ergebnisse für den Detektor “meinErsterDetektor” auftauchen. Ein Doppelklick öffnet die entsprechenden Quelltextstellen.
/* Utility predicate that checks if a class is in a package */ package_contains_class(PackageName,ClassID) :- packageT(PackageID,PackageName), classDefT_In(PackageID,ClassID). /* Simple convention to identify classes that form the model */ model_class(ClassID) :- package_contains_class('model',ClassID). /* Simple convention to identify classes that form the view */ view_class(ClassID) :- package_contains_class('view',ClassID). /* Forbid any access from model classes to view classes. */ detector_description(modelAccessRestriction,'model must not reference view elements',class,unrated). detector_(modelAccessRestriction,occurrence,[(class, ModelClassID)],[]) :- model_class(ModelClassID), view_class(ViewClassID), detector(classDependency,occurrence,[(class, ModelClassID),(referenced_class, ViewClassID)],[]).
In diesem Beispiel wird der Zugriff von Klassen des Paketes “model” auf Klassen des Paketes “view” gesucht. Dabei werden mit “_In”[2] erweiterte PEFs[3] verwendet und über das detector-Prädikat ein anderer Detektor[4] zum Suchen der Klassenabhängigkeit aufgerufen. (Achtung: Es ist KEIN Tippfehler, dass hier “detector” ohne Unterstrich am Ende steht. Hinter der Unterscheidung steckt die Implementierung des Cachings. Die Regel ist einfach: Definiere “detector_(…)”, benutze “detector(…)”.)
Das Beispiel soll außerdem eine Leitlinie für die Implementierung von Konventionen illustrieren. Man kann drei Ebene unterscheiden:
package_contains_class
steht exemplarisch für all die nützlichen Prädikate, die mir erlauben etwas mehr semantische Information aus der Repräsentation des Java Codes zu gewinnen, als sich aus der Struktur der Faktenbasis ergibt.model_class
und view_class
deklarieren gewisse Konventionen über die Code Struktur, die nicht direkt in Java Code ausgedrückt werden können, wie z.B. eine Untergliederung in “Subsysteme” o. Ä.detector_(modelAccessRestriction…)
Nutzt nun die beide Ebenen um darauf aufbauen Programmierrichtlinien zu entwickeln, die in Java selbst nicht ausdrückbar wären, für die Erweiterbarkeit und Wartbarkeit des Systems aber entscheidend sind. Der Entwickler wird von der lästigen Pflicht entlastet, diese Richtlinien selbst zu überprüfen.