Reverse-Engineering (Joomla-Code zu eJSL-Instanzmodell)

Aus THM-Wiki
Wechseln zu: Navigation, Suche
Kurs
Kursname Modellgetriebene Softwareentwicklung in der Praxis SS 15
Kurstyp Praktikum
Dozent Priefer
Semester SS 15
Studiengang Master
Link http://homepages.thm.de/~dprf53


jext2ejsl ist ein Programm das Joomla Erweiterungen in ein Instanz-Modell der Sprache eJSL umwandelt. Bei Joomla handelt es sich um ein Web Content Management System dessen Funktionalität mit Erweiterungen (Joomla Extensions) erweitert werden kann. Bei der Entwicklung der verschiedenen Erweiterungen muss die vorgegebene, wiederkehrende Struktur der Erweiterung durch den Entwickler geschrieben werden. Um den Entwickler von dieser Arbeit zu befreien, wird die Modellsprache eJSL entwickelt. Damit kann der Entwickler verschiedene Erweiterungen Modellieren und sich damit eine Joomla Erweiterung generieren lassen.

eJSL Model --> generator --> Joomla Extensions

jext2ejsl geht den umgekehrten Weg und generieret aus Joomla Erweiterungen ein eJSL Modell.

Joomla Extensions --> jext2ejsl --> eJSL Model

Dies wird benötigt um bestehende Erweiterungen soweit wie möglich, automatisiert als Instanz Modell (eJSL) Abzubilden. Das Programm untersucht dabei die Struktur der Erweiterungen und erstellt aus den gewonnenen Informationen das Instanz Modell. Beim dem so erstellten Instanz Modell handelt es sich um eine Näherung der Erweiterung. Das Bedeutet das bei der Erstellung des Instanz Modells individuelle Bestandteile (wie z.B Code der Funktionen) und wiederkehrende Bestandteile, die nicht zugeordnet werden können, verloren gehen.

Joomla Erweiterungen

Joomla bietet verschiedene Arten von Erweiterungstypen. Die jeweiligen Erweiterungen haben alle eine Manifest-Datei in der die Erweiterungsart und weitere Informationen stehen. Die Manifest-Datei liegt in dem Hauptordner der Erweiterung und heist entweder "manifest.xml" oder "<ext>_<extname>.xml". "<ext>" ist der Erweiterungstyp (z.B com = Component, pkg = Package, ...) und "<extname>" ist der Name der Erweiterung.

Die verschiedenen Erweiterungstypen, ihre Kürzel und die Unterstützung durch eJSL. Die Unterstützung bezieht sich nur auf eJSL, nicht auf den generator der daraus Joomla Erweiterungen generiert.

Kürzel Typ eJSL Unterstützung
com component unterstütz
? file nicht unterstütz
? language nicht unterstütz (Bestandteil des Modells)
lib library unterstütz
mod module unterstütz
pkg package unterstütz
plg plugin unterstütz
tmp template unterstütz

Informationsgewinnung

Zu beginn der Verarbeitung werden die Grundlegenden Informationen aus der Manifest-Datei ausgelesen, dazu zählen:

  • type
  • author (author, authorEmail und authorUrl)
  • creationDate
  • copyright
  • license
  • version
  • und die Sprachdateien.
<?xml version="1.0" encoding="utf-8" ?>
<extension type="component" version="3.3" method="upgrade">
    <name>myexter222</name>

    <author>Joomla! Project</author>
    <authorEmail>admin@joomlaf.org</authorEmail>
    <authorUrl>www.joomla.org</authorUrl>

    <creationDate>2015</creationDate>
    <copyright>Copyright (C) 20053 - 2014 Open Source Matters, Inc. All rights reserved.</copyright>
    <license>GPL 2.0</license>
    <version>1.0.1</version>

    
    <languages folder="site">
    	<language tag="de-DE">language/de-DE/de-DE.com_myexter222.ini</language>
    	<language tag="en-GB">language/en-GB/en-GB.com_myexter222.ini</language>
    </languages>

    <!-- missing for reason of space -->
</extension>


Library

Bei Bibliotheken werden alle in der Manifest-Datei aufgeführten Verzeichnisse ("<files>...</files>") nach PHP-Dateien durchsucht. Diese werden anschließend durch den PHP-Paser ausgelesen und die extrahierten Informationen als Paketstruktur für die Modellierung verwendet. Die Paketstruktur beinhaltet dabei alle Klassen, Attribute, Methoden, Parameter und Typen.

<?xml version="1.0" encoding="UTF-8" ?>
<extension type="library" version="3.3.0" method="upgrade">
    <libraryname>core</libraryname>
    <!-- missing for reason of space -->

    <files>
    	<folder>util</folder>
    </files>
    
    <languages>
    </languages>
    
</extension>
class coreCar
{
	/**
	 *
	 * @param	integer	$speed	Parameter description
	 *
	 * @return	integer	Description
	 */
	public function drive($speed)
	{
		// TODO: auto-generated method stub
		$diff = 0;
		return $diff;
	}
	
}

Module

Aus dem Modul wird derzeit nur, das Manifest und die Sprachdateien erstellt. Eine Zuordnung zu einer Seite konnte auf Grund fehlender Informationen nicht getroffen werden.

<?xml version="1.0" encoding="utf-8"?>
<extension type="module" version="3.3" client="site" method="install">
    <!-- missing for reason of space -->
    <files>
        <filename module="mod_weblinks">mod_weblinks.php</filename>
        <filename>tmpl/index.html</filename>
        <filename>tmpl/default.php</filename>
    </files>
    
    <languages>
        <language tag="de-DE">de-DE.mod_weblinks.ini</language>
        <language tag="en-GB">en-GB.mod_weblinks.ini</language>
    </languages>
</extension>

Komponente

Aus einer Komponente können vergleichsweise die meisten Informationen für ein eJSL Model extrahiert werden. Da eJSL nicht zwischen Sprachdateien für Backend und Frontend unterscheidet, werden nur die Sprachdateien für das Fronend ausgelesen. Anschließend werden die SQL Installationsdateien gesucht, geparst und aus den Daten die Entitäten des eJSL Instanz Models erstellt. Dabei werden auch die Primärschlüssel übernommen.

Beachte: Wenn der Primärschlüssel "id" heißt (ist meistens der fall), wird er in das eJSL Instanz Modell übernommen. In eJSL ist "id" jedoch kein gültiger Bezeichner für ein Attribut!

Der Datenbanktyp einer Entität wird aus der entsprechenden SQL Datei übernommen. Der HTML-Typ steht zwar in dem Model der Komponente ("<site | admin>/models/<entität>/forms/<entität>.xml"), jedoch unterscheidet sich der Name von der Entität aus der SQL Datei, womit keine eindeutige Zuordnung besteht. Daher werden die HTML Typen aus den Datenbanktypen "geraten".

  • Alle int Typen werden auf "int" abgebildet.
  • Alles Andere wird auf "text" abgebildet.

Backend und Fronend wird Strukturgleich behandelt. Es werden dabei die Ordner "site" und "admin" vorausgesetzt. In diesen werden Ordner aus dem Unterordner "views" gelesen. Die Namen der Ordner bilden die Seitennamen. Die Endung des Namens entscheidet darüber ob es sich um eine detaillierte Seite oder um eine Index-Seite (auf "s" endend) handelt. Statische Seiten werden nicht erkannt! In den jeweiligen Ordnern, wird der Ordner "tmpl" und die XML-Datei "default.xml" gesucht. Ist dieser vorhanden, werden daraus die Parameter für die Seite erstellt.

Neben den Parametern für die Seite gibt es noch globale Parameter die jeweils direkt im Fronend oder Backend in der Datei "config.xml" zu finden sind. Diese werden ebenfalls gelesen und in das eJSL Instanz Modell übernommen.

<?xml version="1.0" encoding="utf-8"?>
<extension type="component" version="3.3" method="upgrade">
    <name>myexter222</name>
    <!-- missing for reason of space -->
  
    <install>
        <sql>
            <file driver="mysql" charset="utf8">sql/install.mysql.utf8.sql</file>
        </sql>
    </install>
    
    <files folder="site">
        <filename>myexter222.php</filename>
        <filename>controller.php</filename>
        
        <folder>views</folder>
        <folder>models</folder>
        <folder>controllers</folder>		       
    </files>
    
    <languages folder="site">
    	<language tag="de-DE">language/de-DE/de-DE.com_myexter222.ini</language>
    	<language tag="en-GB">language/en-GB/en-GB.com_myexter222.ini</language>
    </languages>
    
    <administration>
        <menu>COM_MYEXTER222</menu>
        <submenu>
	     <menu link="option=com_myexter222&amp;view=weblinks" alias="Weblinks" 
                        view="weblinks">COM_MYEXTER222_TITLE_WEBLINKS</menu>
        </submenu>
        
        <files folder="admin">
            <filename>myexter222.php</filename>
            <filename>controller.php</filename>
            <filename>access.xml</filename>
            <filename>config.xml</filename>
            
            <folder>sql</folder>
            <folder>tables</folder>
            <folder>models</folder>
            <folder>views</folder>
            <folder>controllers</folder>
        </files>
		
	<languages folder="administrator">
    	    <language tag="de-DE">language/de-DE/de-DE.com_myexter222.ini</language>
    	    <language tag="en-GB">language/en-GB/en-GB.com_myexter222.ini</language>
	</languages>
    </administration>
</extension>

Package

Bein einlesen eines Paketes wird der Packetname ausgelesen und bildet den Namen des eJSL Modells. Anschließend wird der Ordner gelesen in welchem sich die Erweiterungen befinden und die Namen der Erweiterungen. Bei den Namen wird die Endung abgeschnitten und der entsprechende Ordner gesucht. Daher dürfen die eingetragenen Erweiterung nicht gepackt sein! Die Einzelnen Erweiterungen werden dann für sich einzeln bearbeitet.

<?xml version="1.0" encoding="UTF-8" ?>
<extension type="package" version="3.5" method="upgrade">
    <packagename>weblink</packagename>
    <!-- missing for reason of space -->
    <packager>MDD Prifer Team</packager>
    <packagerurl>http://www.yoururl.com/</packagerurl>
    <update>http://www.updateurl.com/update</update>
    
    <files folder="packages">
        <file type="component" id="com_myexter222" >com_myexter222.zip</file>
        <file type="module" id="mod_weblinks" client="site">mod_weblinks.zip</file>
        <file type="library" id="lib_core">lib_core.zip</file>
    </files>
    
</extension>

Benutzung

jext2ejsl kann in drei verschiedenen Varianten verwendet werden. Als ...

  • standalone Applikation
  • Bibliothek
  • oder als Eclipse Plugin

jext2ejsl ist ein in der Programmiersprache Scala geschriebenes Programm. Daher benötigt es die Java Virtual Machine um ausgeführt zu werden.

Standalone

Bei der Ausführung der standalone Variante erscheint ein Auswahldialog in dem eine Manifestdatei einer Joomla Erweiterung ausgewählt werden muss. Die Anwendung generiert daraufhin im selben Verzeichnis in dem die Manifest-Datei der Erweiterung liegt ein eJSL Modell.

Bibliothek

Die Bibliothek kann verwendet werden um jext2ejsl in eigenen Applikationen zu verwenden. Dabei muss jext2ejsl (Jar Datei) als Bibliothek eingebunden werden. Die Klasse "de.thm.icampus.mdd.Builder" bietet die Methode "build(manifest: Path): EJSLModel", welche den Pfad zu einer Manifest-Datei einer Joomla Erweiterung erwartet und daraus ein eJSL Modell generiert und zurück gibt, welches anschließend zu einem Text umgewandelt werden kann. Die Umwandlung in einen Text kann durch den impliziten "EJSLModelFunctionWrapper" geschehen.

package de.example

import java.nio.file.Paths

import de.thm.icampus.mdd.Builder
import de.thm.icampus.mdd.Builder.EJSLModelFunctionWrapper // <-- EJSL Function Wrapper

object Main extends App {
  
  val manifestPath = Paths.get("<Path To Joomla Extension>/<ext>_<extension>.xml") // <-- Path to Manifest File

  val eJSLModel = Builder.build(manifestPath) // <-- generate the abstract Model for eJSL

  println(eJSLModel.asText) // <-- don't use .toString to print the eJSL Text Model
}

Eclipse Plugin

Die Verwendung in Eclipse ist durch ein Plugin möglich. Dieses bietet bei Auswahl auf ein Manifest die Möglichkeit ein eJSL Instanz Modell im selben Ordner zu erzeugen.

Rechtsklick auf Manifest.xml --> jext2ejsl --> generate ejsl

Fehler beim Erstellen des Vorschaubildes: Datei fehlt

Weiterentwicklung

Die Quelldateien befinden sich auf dem Git Server der Technischen Hochschule Mittelhessen (THM) und sind zum klonen öffentlich zugänglich.

Git-URI: git@git.thm.de:asjn83/jext2ejsl.git

Bei dem Projekt handelt es sich um ein SBT Projekt. SBT ist ein Programm zum bauen von Anwendungen (vergleichbar mit Maven). Es kann Abhängigkeiten durch Angabe einer URI automatisch herunter laden und einbinden.

Beachte: Das Projekt muss erstmal mit sbt gebaut (sbt compile ) damit die im Projekt enthaltenen Templates in Scala Objekte übersetzt und alle Abhängigkeiten geladen werden.

Voraussetzungen & Abhändigkeiten

Je nach verwendeter IDE wird zusätzlich ein Scala und SBT Plugin benötigt.

Die aktuellen Abhängigkeiten lassen sich aus der "build.sbt" Datei des Projektes auslesen.


Aufbau

Die Struktur des Projektes hat den folgenden grundlegenden Aufbau. (Das ist kein vollständiges Modell!)

Beachte: Das EJSLModel ist eine Klasse, die Informationen beinhaltet um das eJSL Instanz-Modell zu erstellen.

Fehler beim Erstellen des Vorschaubildes: Datei fehlt
  1. Der Builder liest die Manifest-Datei die als XML vorliegt und prüft den den Typ der Erweiterung. Anhand des Typs entscheidet er was für ein Handler die Erweiterung verarbeiten soll.
  2. Der jeweilige Handler bearbeitet eine Erweiterung nach ihrem Typ und Liefert eine entsprechende Extension als Rückgabe. Dabei lesen erstmal alle den Erweiterungsnamen, erstellen ein Manifest und lesen vorhandene Sprachdateien ein.
    1. Der Component Handler Erstellt aus der SQL Installationsdatei die Entitäten, aus den XML Konfigurationsdateien die Parameter, aus den Ordnern in den Views die Pages und bildet daraus Gruppen (Backend, Frontend und Parameter-Gruppen).
    2. Der Module Handler liest nur die Manifestdatei und die Sprachdateien da er keine Zuordnung zu den Pages erstellen kann.
    3. Der Library Handler liest die Ordnerstruktur mit samt aller PHP Dateien und generiert daraus PHP Klassen mit Attributen, Methoden mitsamt ihrer Kommentare und Typen.
    4. Der Plugin Handler ist ein Platzhalter da der generator noch keine Plugins generieren kann.
    5. Der Language Handler ist ein Platzhalter und nicht implementiert, da eJSL keine separaten Spracherweiterungen vorsieht.
    6. Der Package Handler Liest aus dem Manifest welche Erweiterungen eingetragen sind, lässt sich diese durch den Builder bauen und bildet somit eine Sammlung von Erweiterungen.
  3. Die jeweiligen Extensions bilden eine Sammlung von Informationen und werden durch den dafür verantwortlichen Handler erstellt.
  4. Das EJSLModel das durch den Builder erstellt wird, benötigt nur den Modellnamen, welcher dem Namen der Erweiterung entspricht und alle eingelesenen Erweiterungen. Daraufhin untersucht es alle Erweiterungen und liest sich Informationen wie Seiten, Parameter, Datentypen und Entitäten die für die Darstellung des eJSL Instanz Modells gebraucht werden aus den Erweiterungen aus.

Das was im oben dargestellten Modell nicht abgebiltet ist, sind die Templates (Views) zu den jeweiligen Extensions und anderen Modellklassen. Diese Textdateien werden durch Twirl (ein SBT Plugin) in Scala Objekte übersetzt. Die Templates bilden das Gerüst für ein eJSL Instanz Modell. Durch eingebettete Scala Befehle werden benötigte Informationen aus den Modellklassen ausgelesen und verschachtelte Templates aufgerufen. Ein Überblick über die Templates ist in der Beschreibung des Playframeworks (Scala Templates), für was es ursprünglich entwickelt wurde, zu finden.

@(model: de.thm.icampus.mdd.model.EJSLModel)
eJSLModel "@{model.name}" {
	datatypes {
		@model.datatypes.map(e ⇒ "Datatype " + "\"" + e + "\"").mkString(",\n")
	}
	globalparameters {
		@for(p <- model.globalParams) {
			@param(p)
		}
	}
	parametergroups {
		@for(pg <- model.paramGroups) { @if(pg.params.nonEmpty) {
			@paramgroup(pg)
		}}
	}
	entities {
		@for(ent <- model.entities) {
			@entity(ent)
		}
	}
	pages {
		@for(p <- model.pages) {
			@page(p)
		}
	}
	extensions {
		@for(ext <- model.extensions) {
			@extension(ext)
		}
	}
}

Wie bereits im Abschnitt Dispatcher erwähnt, wurden für die "Datengewinnung" aus dem Quellcode einer Joomla-Extension Parser benötigt, welche in der Lage sind, die entsprechenden Dateien auszulesen. Während wir bereits nach kurzer Zeit einen "funktionsfähigen" Parser für SQL-Anweisungen finden konnten, gestaltete sich die Suche nach einem PHP-Parser sehr schwierig.

PHP-Parser

Die Aufgabe des PHP-Parser ist in erster Linie das Auslesen der (generierten) PHP-Dateien und derer Inhalte. Dabei liegt der Fokus auf den Klassen, Interfaces, Methoden, Attributen sowie den zugehörigen Kommentaren. Ein einsatzbereiter PHP-Parser, welcher in der Lage ist diese Krieterien zu erfüllen konnte nicht gefunden werden, zumindest keiner der in Java oder Scala implementiert ist. Tatsächlich gibt es einen in Java implementierten PHP-Parser, welcher jedoch nur mit PHP <= 4 kompatibel ist, sodass auch dieser nicht eingesetzt werden konnte.

Das Kernproblem bei der Implementierung eines PHP-Parsers in einer andren Sprache als PHP selbt ist, dass man nicht direkt auf den Token-Stream zugreifen kann und auf String-Ebene arbeiten muss. Basierend auf dieser Tatsache gab es folgende Möglichkeiten, die in Frage kamen:

  • In den Quellcode des PHP 4-Parsers einarbeiten und diesen mit PHP 5 kompatibel machen
    • Für uns in der Kürze der Zeit kaum realisierbar
  • Einen einsatzfähigen PHP-Paser (in PHP implementiert) aus dem Java-System heraus aufrufen, benötigte Informationen sammeln und in einer Datei ablegen. Anschließend muss die erzeugte Datei eingelesen, verarbeitet und aufbereitet werden.
    • Bei dieser Methode, vorausgesetzt man hat einen passenden Parser, versucht man über Umwege an die Daten zu gelangen und ist gezwungen Schnittstellen zu definieren, welche wohl definiert werden müssen und entsprechend umfangreich ausimplementiert und getestet werden müssten. Bei der kurzen Projektlaufzeit war auch dies keine Option für uns.
  • Einen "vereinfachten" PHP-Parser nativ in Scala (Java) implementieren, welcher in der Lage ist die Kerninformationen aus PHP-Dateien auszulesen.

Nach unzähligen Brainstormings und Diskussionen haben wir uns dazu entschieden, den benötigten PHP-Parser nativ in Scala zu implementieren. Dabei mussten folgende Informationen aus den PHP-Dateien ausgelesen werden:

  • Klassen
    • Kommentar
      • "@generated flag"
    • Super-Klasse (extends)
    • Interfaces der Klasse (implements)
    • Attribute
      • Kommentar
      • Name
      • Typ
      • AccessModifier (public, private, protected)
    • Methoden
      • Kommentar
        • "@generated flag"
      • Name
      • AccessModifier (public, private, protected)
      • Rückgabewert
      • Parameter
        • Name
        • Datentyp
  • Interfaces
    • Kommentar
      • "@generated flag"
    • Name
    • Interfaces
    • Methoden (siehe Klassen)

Die Datenhaltung auf Seiten des Plugins erfolgt mithilfe von Klassen für jedes der oben aufgeführte Konstrukte:

case class Clazz(name: String, superClass: Option[String] = None, isGenerated: Boolean = false,
         interfaces: List[Interface] = List(), attributes: List[Attribute] = List(), 
         methods: List[Method] = List()) extends Struct {}
case class Attribute(name: String, dataType: String, accessModifier: String) {}
case class Method(name: String, accessModifier: String, returnType: Option[String] = None, 
         isGenerated: Boolean = false, params: List[Parameter] = List()) {}
case class Parameter(name: String, dataType: String) {}
case class Interface(name: String, isGenerated: Boolean = false, interfaces: List[Interface] = List(),
         methods: List[Method] = List()) extends Struct {}

Das Datamining basiert auf kombinierten Regulären Ausdrücken, die jedes einzelne Konstrukt als solches erkennen und auswerten:

  val tabRE = "[\\t]"
  val spaceRE = "\\s"
  val tabOrSpaceRE = s"($tabRE|$spaceRE)"
  val lineBreakRE = "[\\n\\r]"

  val mlCommentRE = "(?s:/\\*(((?!\\*/).)*)?\\*/)"
  val classNameRE = s"(abstract)?$spaceRE*class$spaceRE+([a-zA-Z_][a-zA-Z0-9_]*)"
  val extendsFromRE = s"(extends$spaceRE*([a-zA-Z_][a-zA-Z0-9_]*))"
  val interfacedRE = s"(implements$spaceRE*([a-zA-Z_][a-zA-Z0-9_]*)+[^\\{]*)"

  val methodRE = s"$tabOrSpaceRE*$mlCommentRE$tabOrSpaceRE*(public|private|protected)?$spaceRE+(static$spaceRE+)?function$spaceRE+(\\w+)$spaceRE*([(] [^)]* [)])" // /xms
  val attributeRE = s"$tabOrSpaceRE*$mlCommentRE$tabOrSpaceRE*(public|private|protected)?$spaceRE+(static$spaceRE+)?([$$]+[a-zA-Z_][a-zA-Z0-9_]*)+$tabOrSpaceRE*=" // m
  val atFinderRE = s"$tabOrSpaceRE?\\*$tabOrSpaceRE*@%s$tabOrSpaceRE?(\\w+)?$lineBreakRE?" // i
  val paramFinderRE = s"$tabOrSpaceRE?\\*$tabOrSpaceRE*@param$tabOrSpaceRE+([\\S]+)$tabOrSpaceRE+(&?[$$]+[a-zA-Z_][a-zA-Z0-9_]*)?" // i
  val singleParamRE = s"^\\((&?[$$]+[a-zA-Z_][a-zA-Z0-9_]*).*\\)$$" // i

  val classRE = s"$mlCommentRE*$lineBreakRE?$classNameRE$spaceRE*$extendsFromRE?$spaceRE?$interfacedRE?$spaceRE?$lineBreakRE?\\{(.*?)^\\}" // xms
  val interfaceRE = s"$mlCommentRE?$lineBreakRE?interface$spaceRE+([a-zA-Z_][a-zA-Z0-9_]*)$spaceRE*$interfacedRE?$spaceRE?$lineBreakRE?\\{(.*?)^\\}" // xms

Für einen vereinfachten Zugriff auf die einzelnen erkannten Komponenten des Codes wurden alle Gruppen der Ausdrücke indiziert und in leserlicher Form zugänglich gemacht.

Aktuell lassen sich nahezu alle uns bekannten Klassen- und Interfacestrukturen erkennen, solange diese "korrekt" definiert wurden. Weiter ist der PHP-Parser in der Lage kombinierte Dateien einzulesen, unabhängig von der Anzahl der inbegriffenen Klassen, Interfaces oder anderer Konstrukte.

Zu beachten:

  • Da die Ausdrücke mehrfach kombiniert und verschachtelt wurden, beeinflussen Änderungen an den Kernkomponenten die Indizes der unterliegenden Komponenten, sofern sich Gruppendefinitionen ändern.
  • Beim einlesen von "größeren" Libraries, wie z.B. der in lib_thm_core inbegriffenen library "PHP-Excel" kann es sehr schnell zum stack overflow kommen, wenn der Lookahead zu locker erweitert wird
    • Die Regulären Ausdrücke wurden dahingehend optimiert
    • Entsprechende Stellen wurden im Code kommentiert und sollten nur mit äußerster Sorgfalt modifiziert werden
    • Für die Modellierung unwichtige Informationen (nach unserem Ermessen) werden ignoriert z.B. Array-Attribute mit unzähligen vordefinierten Einträgen
  • Damit der Lookahead nicht ins unermessliche erhöht werden muss, wurde die Erkennung der Klassen und Interfaces stark vereinfacht
    • Die Klassen- bzw. Interfacedefinition muss am Anfang einer Zeile beginnen (ohne Einrückung) und genau so am Anfang einer Zeile Enden. Andernfalls wird das Konstrukt nicht erkannt!

SQL-Parser

Der von uns eingesetzte SQL-Parser ist in der Lage die notwendigen "Create-Table-Anweisungen" für MySQL einzulesen und auszuwerten.

Zumindest theoretisch, da bereits die ersten Tests mit einfachen Statements für Exceptions sorgten:

CREATE TABLE IF NOT EXISTS `#__rettred222` (
    `id` int(11) AUTO_INCREMENT,
    `ordering` INT(11)  NOT NULL ,
    `state` TINYINT(1)  NOT NULL ,
    PRIMARY KEY(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
==========================================================
net.sf.jsqlparser.JSQLParserException
	at net.sf.jsqlparser.parser.CCJSqlParserManager.parse(CCJSqlParserManager.java:40)
	at Main.main(Main.java:25)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: net.sf.jsqlparser.parser.ParseException: Encountered " "NOT" "NOT "" at line 1, column 17.
Was expecting one of:
    <EOF>
    ";" ...
    "(" ...

	at net.sf.jsqlparser.parser.CCJSqlParser.generateParseException(CCJSqlParser.java:5530)
	at net.sf.jsqlparser.parser.CCJSqlParser.jj_consume_token(CCJSqlParser.java:5406)
	at net.sf.jsqlparser.parser.CCJSqlParser.Statement(CCJSqlParser.java:149)
	at net.sf.jsqlparser.parser.CCJSqlParserManager.parse(CCJSqlParserManager.java:38)
	... 6 more
....

Das zu parsende Statement im diesem Beispiel beinhaltet rein theoretisch keinerlei komplizierter Konstrukte oder Anweisungen und kann dennoch nicht durch den Parser eingelesen werden. Entsprechend mussten alle Inkomaptibilitäten des Parsers erfasst und korrigiert werden. Aktuell sind uns folgende Fehlerquellen bekannt:

  • "CRAETE TABLE...":
    • Falsch: "CRAETE TABLE IF NOT EXISTS ..."
    • Richtig: "CREATE TABLE `[table_name]`..."
  • "`" dürfen bei Spaltennamen nicht verwendet werden, müssen aber beim Tabellennamen gesetzt sein
  • Primary keys dürfen keinen Namen haben:
    • Falsch: "primary key [name] ([Spaltenname(-n)])"
    • Richtig: "primary key ([Spaltenname(-n)])"
  • Unique keys:
    • Falsch: "unique key [name] ([Spaltenname(-n)])"
    • Richtig: "unique [name] ([Spaltenname(-n)])"


Da man dem Endverbraucher nicht zumuten kann, SQL-Files zu durchsuchen und die aufgelisteten Fehler zu korrigieren, haben wir diese mithilfe von Regulären Ausdrücken korrigiert. Das Einzige, bisher nicht gelöste Problem beim SQL-Parser stellen die Spaltennamen dar. Da man hierbei keine "`" verwenden darf und wir diese entsprechend zum Parsen automatisiert entfernen, ist es nicht möglich sql-keywords als Spaltennamen zu verwenden!

Konnte ein SQL-Statement erfolgreich geparst werden, erhält man pro Anweisung eine Tabelle (case class), welche aus dem Tabellennamen und den einzelnen Spalten (case class) besteht. Die Tabelle und die Spalten beinhalten dabei alle notwendigen Informationen zum Modellieren von Entitäten (Entity) und Datentypen (Datatype) in eJSQL.

Probleme & Bekante Bugs

  • Fehlende Fehlerfallbehandlung
  • Eingerückte PHP Klassen können nicht durch den RegEx Parser erkannt werden.
  • Der SQL Parser unterstützt nur ein sehr einfaches Format.
  • Bei der Erstellung von eJSL durch das Eclipse Plugin muss der Ordner in Eclipse aktualisiert (Rechtsknick -> Refresh) werden.