Swarovski Shop Administration Program
Transcription
Swarovski Shop Administration Program
HTBLuVA Innsbruck Höhere Lehranstalt für Elektronik Ausbildungsschwerpunkt: Technische Informatik DIPLOMARBEIT SSAP Swarovski Shop Administration Program Ausgeführt im Schuljahr 2012/13 von: Betreuer: Tobias Lanzanasto Gregor Neumann Michael Plattner Dipl.- lng. Mag(FH) Felix Klingler Innsbruck, am 22.05.2013 Diplomarbeit Lanzanasto, Neumann, Plattner Eidesstattliche Erklärung Wir erklären an Eides statt, dass wir die vorliegende Diplomarbeit selbstständig und ohne fremde Hilfe verfasst, andere als die angegebenen Quellen und Hilfsmittel nicht benutzt und die den benutzten Quellen wörtlich und inhaltlich entnommen Stellen als solche erkenntlich gemacht haben. Verfasser: Lanzanasto Tobias Innsbruck, am 22.05.2013 Neumann Gregor Plattner Michael Seite 1 Diplomarbeit Lanzanasto, Neumann, Plattner Kurzbeschreibung Die Diplomarbeit befasst sich mit dem Shop Administration Program für das Unternehmen Swarovski. Unser Projekt soll das jahrelang verwendete Excel File, welches zur Shop Administration verwendet wird, ablösen. Die Grundidee zur Verbesserung der Administration besteht darin, die Shops in eine Datenbank zu importieren und diese mittels eines User Interfaces zu verwalten. Das Projekt kann in folgende Teile unterteil werden: 1. Daten Import in die Datenbank Mithilfe eines Visual Basic Programmes sollen die Daten des Excel Files in die Datenbank importiert werden. Dies geschieht einmalig zur Einführung dieses Projektes. 2. Datenbank Die MySQL Datenbank dient zur Speicherung aller Daten. Sie ermöglicht das schnelle Suchen und Editieren der Datensätze. 3. Benutzeroberfläche Um die Datenbank einfach zu verwalten wird mit Java-RCP eine Benutzeroberfläche programmiert. Über diese kann man Datensätze in der MySQL Datenbank suchen, editieren, hinzufügen und löschen. Seite 2 Diplomarbeit Lanzanasto, Neumann, Plattner Summary This work covers the Swarovski Shop Administration Program for the company Swarovski. It should replace the current Excel-File to administrate Swarovski shops used for years. The main theory to improve the administration is to fill a database with all information about the shops and manage this database with a specific user interface. This project can be divided into following parts: 1. Import data into the database (Visual Basic) Import the data from the Excel file into the database by using a Visual Basic Program. This is done only once for the implementation of this project. 2. Database (MySQL) The MySQL database is used to store all data. The database allows quick searching and editing of the records. 3. User Interface The User Interface is programmed with Java-RCP-technology, to enable simple managing of the database. With the UI is it possible to search, edit, add and remove data from the database. Seite 3 Diplomarbeit Lanzanasto, Neumann, Plattner Inhaltsverzeichnis 1. Einleitung............................................................................................................. 6 1.1. Verlauf Diplomarbeit ...................................................................................... 6 1.2. Zielsetzung und Aufgabenstellung................................................................. 7 1.3. Aufbau der Diplomarbeit ................................................................................ 8 1.4. Partnerfirma ................................................................................................... 9 2. Visual Basic Import ............................................................................................ 10 2.1. Allgemein ..................................................................................................... 10 2.2. Excel File ..................................................................................................... 11 2.3. Microsoft Visual Basic 2010 ........................................................................ 12 2.4. Programmübersicht ..................................................................................... 13 2.4.1. Connection Excel .................................................................................. 13 2.4.2. Connection DB ...................................................................................... 14 2.4.3. Import Country ...................................................................................... 14 2.4.4. Import Shops......................................................................................... 15 2.4.5. Import Hardware ................................................................................... 17 3. MySQL Database .............................................................................................. 19 3.1. Allgemein ..................................................................................................... 19 3.2. MySQL Workbench ..................................................................................... 20 3.3. Datenbankübersicht..................................................................................... 21 4. Java RCP Userinterface .................................................................................... 24 4.1. Allgemein ..................................................................................................... 24 4.2. Programmierumgebung ............................................................................... 24 4.2.1. Eclipse .................................................................................................. 24 4.2.2. Eclipse 4 – Eclipse Juno ....................................................................... 25 4.2.3. Subversion Client .................................................................................. 25 4.3. Java-RCP .................................................................................................... 25 4.4. JDBC ........................................................................................................... 26 4.5. Programmübersicht ..................................................................................... 27 4.5.1. Basis-Projekt ......................................................................................... 27 4.5.2. RCP-Struktur......................................................................................... 28 4.5.3. Oberfläche und Application Model ........................................................ 30 4.5.4. Verbindung zur DB................................................................................ 37 Seite 4 Diplomarbeit Lanzanasto, Neumann, Plattner 4.5.5. Handler ................................................................................................. 43 4.5.6. Gesamttabellen ..................................................................................... 45 4.5.7. TreePart ................................................................................................ 46 4.5.8. TreeListener .......................................................................................... 50 4.5.9. Search .................................................................................................. 53 5. 4.5.10. Statistics ............................................................................................ 57 4.5.11. Details................................................................................................ 59 4.5.12. Add .................................................................................................... 69 Manual/Bedienungsanleitung ............................................................................ 70 5.1. Oberfläche ................................................................................................... 70 5.2. Menü ........................................................................................................... 71 5.3. Tree ............................................................................................................. 72 5.4. Arbeitsfläche ................................................................................................ 72 5.5. Suche .......................................................................................................... 73 5.6. Neues Element hinzufügen ......................................................................... 74 5.7. Statistiken .................................................................................................... 75 5.8. Details ......................................................................................................... 75 Bilder-Verzeichnis ..................................................................................................... 78 Seite 5 Diplomarbeit Lanzanasto, Neumann, Plattner 1. Einleitung Diese Diplomarbeit beschreibt die Entwicklung eines neuen Shop Administrations Programmes, das das bisherige Excel Shopfile ablösen wird. Dieses wird im Auftrag von Swarovski im Rahmen einer Diplomarbeit verwirklicht. 1.1. Verlauf Diplomarbeit Dank Herrn Prof. Schwarz, Professor an unserer Schule sowie Dienstnehmer bei Swarovski, erfuhren wir über die Möglichkeit bei Swarovski unsere Diplomarbeit zu verfassen. Von Swarovski wurden uns grundlegende Informationen über die Administration deren Shops gegeben, sowie der Aufbau der Excel Tabelle erklärt. Ebenfalls wurden die zu verwendende Datenbank und die Programmiersprachen festgelegt. Die Entwicklung, angefangen von Informationssammlungen über die Realisierbarkeit bis hin zum funktionsfähigen Programm, war selbstständig durchzuführen. Natürlich standen die Firma Swarovski, unser Betreuer Herrn Prof. Klingler sowie mehrere andere Personen bei auftretenden Fragen immer gerne zur Verfügung. Während des Sommers konnten wir ein vierwöchiges Ferialpraktikum bei Swarovski (Außenstelle Hall) absolvieren, in welchem wir ausschließlich an unserer Diplomarbeit arbeiteten konnten. Dies hatte für uns den großen Vorteil, dass wir bei auftretenden Problemen und Fragen immer sofort eine Ansprechperson hatten. Nach dem Vertrautmachen mit der Excel Tabelle wurde der Grundstein mit der Erstellung eines Datenbank-Strukturplanes gelegt. Alle Daten aus der Excel Tabelle, die nicht berechnet werden können, müssen in die Datenbank übernommen werden. Jene Informationen, die sich aus anderen Einträgen ergeben, werden nicht in der Datenbank gespeichert, sondern direkt vom Benutzeroberflächen-Programm aus den Datenbankeinträgen ermittelt. Anschließend schrieben wir ein Programm zur Übertragung der Daten aus der Excel Tabelle in die Datenbank. Seite 6 Diplomarbeit Lanzanasto, Neumann, Plattner Zum Verwalten der Datenbank für den Endnutzer implementierten wir eine Benutzeroberfläche, mit welcher Datensätze in der Datenbank erstellt, gelöscht, editiert und gesucht werden können. Nach Fertigstellung des Programmes wurde es Swarovski gegeben, wo es in der Abteilung RETAIL zum Testen verwendet wird. 1.2. Zielsetzung und Aufgabenstellung Die Motivation der Diplomarbeit liegt darin, etwas im Fachbereich technische Informatik zu entwickeln, sowie mit einer namhaften Firma zusammenzuarbeiten und deren Arbeitsumfeld kennenzulernen. Die generelle Aufgabenstellung wurde von der Firma Swarovski gestellt, jedoch wurde uns in der Ausführung viel Spielraum gelassen. Laut Aufgabenstellung der Firma, soll eine Lösung gefunden werden Datensätze aus einer bestehenden Excel-Tabelle in eine MySQL-Datenbank zu übertragen. Zur Verwaltung der Datenbank soll eine in Java Rich Client Platform (RCP) geschriebene Benutzeroberfläche implementiert werden. Von Seiten der Firma wurde uns nahegelegt, das Import-Programm von der Excel Tabelle in die Datenbank mittels eines Visual Basic Programmes zu verwirklichen. Die Abteilung RETAIL arbeitet mit Microsoft-SQL-Datenbanken, da diese jedoch Closed Source sind, wurde vereinbart, dass wir eine MySQLDatenbank mit einer General Public License lokal auf unseren eigenen Rechnern für die Entwicklung des Programmes verwenden. Für die spätere Einbindung des Programmes in die MS-SQL-Datenbank muss lediglich der Verbindungsaufbau zur Datenbank angepasst werden, die restlichen SQL-Befele sind in ANSI-SQL geschrieben, wodurch es zu keinen weiteren Problemen kommen sollte. Ebenso wurde vereinbart, dass die Benutzeroberfläche mittels Java Rich Client Platform (RCP) implementiert wird. Zeitliche Begrenzungen gibt es seitens der Firma keine. Der Zeitplan ist uns überlassen worden. Seite 7 Diplomarbeit 1.3. Lanzanasto, Neumann, Plattner Aufbau der Diplomarbeit Die Arbeit kann prinzipiell in drei Teilgebiete gegliedert werden: Visual Basic Das Visual Basic Programm dient zur Übertragung der Datensätze aus der Excel Tabelle in die Datenbank. MySQL-Datenbank Die Aufgabe der MySQL Datenbank ist es, die Datensätze effizient, widerspruchsfrei und dauerhaft zu speichern und benötigte Datensätze in unterschiedlichen bedarfsgerechten Darstellungsformen bereitzustellen. Java Rich Client Platform Das Java Rich Client Platform (RCP) Program ermöglicht die Verwaltung der Datenbank mittels eines User-Interfaces. Abb. 1: Grundaufbau Diese drei Teilgebiete werden in den folgenden Seiten, in entsprechender Reihenfolge, möglichst genau beschrieben und die Funktionen erklärt. Es wurde darauf Wert gelegt, dass jeder Funktionsblock zuerst allgemein beschrieben wird und erst im weiteren Verlauf auf die Details eingegangen wird. Seite 8 Diplomarbeit 1.4. Lanzanasto, Neumann, Plattner Partnerfirma Abb. 2: Firmenlogo Swarovski Die Firma Swarovski ist ein österreichisches Familienunternehmen, welches 1895 in Wattens gegründet wurde. Zum einen produziert Swarovski Kristall diverse Schmuckstücke aus Kristallglas, zum anderen gehören der Swarovski Gruppe ebenfalls die Tyrolit Schleifmittel in Schwaz und die Swarovski Optik in Absam an. Swarovski und seine Tochterunternehmen beschäftigen knapp 30.000 Mitarbeiter in 40 Länder. Die Standpunkte in Wattens und Hall sind durch ihre Nähe zu Innsbruck ideal für unsere Partnerschaft. Seite 9 Diplomarbeit Lanzanasto, Neumann, Plattner 2. Visual Basic Import 2.1. Allgemein Die erste Aufgabe unserer Arbeit war es, die vorhandenen Daten der ExcelTabelle in die Datenbank zu importieren. Dies soll mit einem Programm geschehen, das in Visual Basic geschrieben ist. Was sich zu Beginn recht einfach anhörte, führte jedoch gleich zu einigen Schwierigkeiten. Was die Arbeit an diesem Import-Programm erschwerte, war, dass die Struktur der Daten, die im Excel-File verwendet wurde, nicht unbedingt geeignet für eine Datenbank war. Somit konnten wir die einzelnen Tabellen nicht einfach in der Datenbank übernehmen, sondern mussten zuerst eine geeignete Datenbankstruktur zusammenstellen. Durch die neue Struktur wurde das Import-Programm jedoch um einiges komplizierter, da nun teilweise Daten aus mehreren verschiedenen Sheets der Excel Tabellen zusammengefasst werden mussten. Durch dieses Zusammenfassen der Daten dauert die Ausführung des Programms auch recht lange. Bei einem kompletten Durchlauf des Programms kommt es zu einer Dauer von ca. 30 Minuten. Das Programm muss jedoch nur einmal ausgeführt werden, weshalb nicht zusätzlicher Aufwand in Zeitoptimierung des Programms gesteckt wurde. Da dieses Programm nur zum einmaligen Importieren der Daten verwendet wird, wäre eine aufwändige Oberfläche nicht notwendig. Daher öffnet sich beim Start der Übertragung nur ein Konsolenfenster, das den Fortschritt des Programms grob darstellt. Es wird angezeigt, aus welcher Excel-Tabelle gerade Daten importiert werden und für jede erfolgreiche Übertragung wird ein Punkt auf dem Fenster angezeigt. Seite 10 Diplomarbeit 2.2. Lanzanasto, Neumann, Plattner Excel File Bisher wurden die Shops der Firma Swarovski mit Hilfe einer Excel-Tabelle verwaltet. Angefangen hat das vor ca. 10 Jahren mit noch einigen wenigen Shops. Da die Firma mittlerweile stark expandiert, gibt es 1000 Shops, die auf der ganzen Welt verteilt sind. Dadurch wurde die anfangs noch recht übersichtliche Excel-Tabelle immer größer und mit der Zeit, durch zum Teil auch fehlerhafte Einträge, komplizierter und unübersichtlicher. Dieses Excel-File ist in mehrere Sheets aufgeteilt. Im ersten Sheet, dem „ROC Info“-Sheet, sind Informationen über die einzelnen Länder gespeichert, in denen Shops der Firma Swarovski ihren Standpunkt haben. Man kann diesem Sheet beispielsweise entnehmen, auf welchem Kontinent das Land liegt, wie viel Steuern zu zahlen sind oder in welcher Zeitzone es liegt. Außerdem sind Kennungsnummern wie VG-Nummer oder Country-Code ersichtlich. In den Sheets „EUROPE“, „AMERICAS” und „ASIA PACIFIC” sind Details zu den einzelnen Shops gespeichert. Dabei werden im Sheet „EUROPE“ alle europäischen Shops, im Sheet „AMERICAS“ alle Nord-, Mittel- und Südamerikanischen Shops und im Sheet „ASIA PACIFIC“ alle Shops im Raum Asien, Ozeanien und Australien zusammengefasst. In diesen Sheets sind Informationen wie Shop-Nummer, Shop-Name, Stadt, Land, Adresse, Telefonnummer, Verkaufsfläche und noch einiges mehr gespeichert. Zusätzlich wird die im Shop vorhandene Hardware inklusive einiger Details angezeigt. Eine Ausnahme sind dabei die europäischen Shops, da hier noch ein weiterer Sheet existiert, in dem nur Informationen über die einzelnen Hardwarekomponenten zu sehen sind. Details für Hardware sind zum Beispiel Seriennummer, Installationsdatum, Alter, Modellbezeichnung und MAC-Adresse. Seite 11 Diplomarbeit Lanzanasto, Neumann, Plattner Ein weiterer Sheet ist der „TOTALS & Licenses“-Sheet. Hier werden die Inhalte aller anderen Sheets zu einer Statistik zusammengefasst. Es wird beispielsweise angezeigt, wie viele Shops eines bestimmten Typs in einem Land vorhanden sind. Außerdem sind dort Informationen zum jeweiligen Kassensystem in den jeweiligen Ländern gespeichert. 2.3. Microsoft Visual Basic 2010 Das Visual Basic Programm wird mit mit Hilfe der von Microsoft angebotenen Programmierumgebung Microsoft Visual Basic 2010 Express implementiert. Für die Verwirklichung des Import-Programms mittels Microsoft Visual Basic spricht, dass schlussendlich sowohl eine Microsoft Datenbank also auch eine Microsoft Excel Tabelle verwendet wird und durch die ausschließliche Verwendung von Microsoft Produkten die hundertprozentige Kompatibilität gegeben ist. Die Programmiersprache Visual Basic ist zwar sehr simpel und kann mit Vorkenntnissen von C oder anderen Sprachen sehr schnell erlernt werden, jedoch kann ein komplexes Programm sehr unübersichtlich werden. Es werden Variablen zum Beispiel nicht als INT, VCHAR, etc. deklariert sondern alle als Dim und das Programm erkennt den Typen automatisch, jedoch kann dadurch der Überblick über die verschiedenen Variablen schneller verloren gehen. Seite 12 Diplomarbeit 2.4. Lanzanasto, Neumann, Plattner Programmübersicht 2.4.1. Connection Excel Um mit dem Transfer der Daten in die Datenbank beginnen zu können, muss zuerst eine Verbindung mit dem Excel-File aufgebaut werden. Dazu wird als erstes mit der Funktion CreateObject(„Excel-Application“) ein neues Objekt generiert. Mit der Funktion Workbooks.Open(FileName, ,true) wird nun eine Verbindung zum gewünschten Excel-File aufgebaut. Dabei wird das File schreibgeschützt im Hintergrund geöffnet. Bei einem unerwarteten Programmabsturz würde das File geöffnet bleiben, daher wird es zur Vorsicht sichtbar gemacht, um zu sehen, dass es geöffnet wurde,und man es dann zu schließen hat. Dim objExcel, FileName, objWorkbook objExcel = CreateObject("Excel.Application") FileName = "...\SR Shops - IT Info.xlsm" objWorkbook = objExcel.Workbooks.Open(FileName, , True) ' Open Read Only objExcel.Visible = True Anschließend werden einzelne Unterprogramme zum Import nacheinander gestartet. Zuerst werden die Länder übertragen, danach die Shops und zuletzt die Hardware. Die Unterprogramme für Shops und Hardware werden immer für jeden Kontinent einzeln gestartet, da so für eine Übertragung weniger Worksheets aus dem Excelfile verwendet werden müssen. Seite 13 Diplomarbeit Lanzanasto, Neumann, Plattner 2.4.2. Connection DB Am Beginn jedes Unterprogramms wird immer eine Verbindung zur Datenbank aufgebaut. Für die Verbindung wird ein Connection-String benötigt, der sämtliche Informationen zur Anmeldung enthält. Dieser besteht aus Serveradresse, Username, Passwort und Datenbank. Mit der Funktion conn.Open() wird nun die Verbindung eröffnet. Jetzt können MySqlCommands mit einer Abfrage in Form eines Strings und der passenden Datenbankverbindung generiert werden, welche anschließend mit Hilfe eines MySqlDataReaders ausgeführt werden können. 2.4.3. Import Country Zu Beginn des Imports der Länder werden alle Einträge der „Country“-Tabelle gelöscht. Dadurch wird das Programm stark vereinfacht, da nicht darauf geachtet werden muss, ob ein Eintrag bereits in der Datenbank vorhanden ist und somit keine Fehler durch Duplikate entstehen können. Anschließend werden für jede Spalte des Datensatzes zwei Variablen erstellt. In einer werden später die Einträge des Excel-Files zwischengespeichert, in der anderen wird „hard coded“ angegeben, in welcher Spalte des Excel-Files der passende Eintrag zu finden ist. Nun wird in einer „Do Until“-Schleife für jede Zeile im Excel-File, in diesem Fall beginnend mit Zeile 5, ein Datensatz in der Datenbank erstellt. Zum Auslesen der Excel-Einträge können zwei Funktionen verwendet werden. Die Funktion objWorkSheet.Cells(row, column).Text gibt einfach den Text zurück den man beim Betrachten der Tabelle in den Zellen lesen kann. Das ist bei fast allen Einträgen die richtige Lösung, außer bei den meisten Datumswerten. Die Tabellen sind nämlich so formatiert, dass beispielsweise beim Datum „01.10.2011“ in der Zelle nur der Text „10.11“ zu sehen ist. Erst wenn man auf die Zelle klickt, erscheint oben in der Eingabeleiste das richtige Datum. In solchen Fällen wird die Funktion objWorkSheet.Cells(row, column).Value benötigt, um den kompletten Wert der Zelle zu entnehmen. Außerdem ist bei Datumswerten darauf zu achten, dass wirklich ein gültiges Datum verwendet wird, Seite 14 Diplomarbeit Lanzanasto, Neumann, Plattner da es ansonsten beim Eintrag in die Datenbank zu Fehlern kommt. Daher wird überprüft, ob der erhaltene Eintrag aus dem Excel-File stimmen kann, falls nicht, wird er wenn möglich in ein einheitliches Datumformat umgewandelt oder durch einen Default-Wert ersetzt. Wenn alle Werte des Datensatzes aus dem Excel-File übernommen wurden, wird daraus eine „INSERT INTO“-Abfrage erstellt, welche anschließend in einem MySqlCommand verpackt und mit dem MySqlDataReader an der Datenbank ausgeführt wird. Nach einem solchen Durchlauf wird ein Punkt im Konsolenfenster geschrieben, um den erfolgreichen Eintrag eines Datensatzes in die Datenbank zu signalisieren. Dieser Vorgang wird für jede ausgefüllte Zeile der Excel-Tabelle wiederholt. 2.4.4. Import Shops Dieses Unterprogramm ist im Prinzip ähnlich gegliedert wie das zum Importieren der Länder. Auch hier werden zu Beginn vorhandene Einträge in den Tabellen „shopinfo.shops“ und „shopinfo.closed_shops“ der Datenbank gelöscht. Das darf aber nur dann geschehen, wenn Daten aus dem EUROPE-Sheet (dem ersten Sheet) des Excel-Files übertragen werden, da dieses Unterprogramm für jeden Kontinent neue ausgeführt wird und sonst schon regulär importierte Shops wieder gelöscht werden würden. Anschließend werden wieder Variablen zur Zwischenspeicherung angelegt und die passenden Spaltennummern eingetragen. Dabei muss jedoch darauf geachtet werden, dass die drei Excel-Sheets „EUROPE“, „AMERICAS“ und „ASIA PACIFIC“ nicht genau gleich formatiert sind, daher variieren die Spaltennummern für bestimmte Einträge zwischen den verschiedenen Sheets. Nun beginnt wieder die eigentliche Übertragung der Daten aus dem Excel-File in das VB-Programm. Dieser Vorgang ist bei den Shops jedoch um einiges komplizierter als bei den Ländern, da im Excel-File manchmal Shops ohne SAPNummer auftreten, die die einzelnen Shops identifizieren. In der neuen Datenbank wird diese Nummer als „Primary Key“ gehandhabt und muss daher für alle Shops eindeutig sein. Shops ohne eine solche SAP-Nummer werden als Seite 15 Diplomarbeit Lanzanasto, Neumann, Plattner „Dummy-Shops“ behandelt, das heißt, sie bekommen vorübergehend die SAPNummer „999999999“, somit werden sie vom Programm als gültiger Shop erkannt und sind gleichzeitig als Dummy-Shop gekennzeichnet. Nachdem nun alle Daten aus dem Excel-File ausgelesen wurden, wird überprüft, ob es sich beim vorhandenen Shop um einen Dummy-Shop handelt. Falls das der Fall ist, wird nun eine einzigartige SAP-Nummer generiert. Diese setzt sich aus drei Teilen zusammen. Die ersten drei Ziffern sind immer „999“. Darauf folgt die vierstellige VG-Nummer des Landes, in dem der Shop seinen Standpunkt hat. Diese Nummer wird dem „ROC Info“-Sheet des Excel-Files entnommen. Abschließend wird noch die Shop-Nummer, die innerhalb eines Landes eindeutig ist, angehängt. Ähnlich wie Dummy-Shops werden auch VG-Shops behandelt. Sie besitzen im Excel-File ebenfalls keine SAP-Nummer, daher muss auch hier eine Nummer generiert werden. Auch sie setzt sich aus drei Teilen zusammen. Die ersten drei Ziffern sind dieses Mal „802“. Darauf folgt wieder die VG-Nummer des jeweiligen Landes. Um den VG-Shop innerhalb eines Landes zu identifizieren, wird nun die Workstation-Nummer als zweistellige Zahl verwendet. Danach müssen wieder Einträge überprüft und verändert werden, die für einen Eintrag in die Datenbank nicht geeignet sind. Wie schon bei den Ländern werden Datumswerte umformatiert bzw. durch Default-Werte ersetzt. Bei diesen Daten kann es aber zusätzlich vorkommen, dass Namen von Shops, Shop-Managern, Straßen oder Ähnlichem ein einfaches Hochkomma enthalten. Das führt beim Ausführen der SQL-Abfrage zu Fehlern. Daher müssen alle einfachen Hochkommas durch zwei einfache Hochkommas für das Einfügen in die Datenbank ersetzt werden, sodass die Daten ohne Fehler korrekt in der Datenbank stehen. Beim Erstellen der SQL-Abfrage muss nun noch entschieden werden, ob der Shop in die shopinfo.shops-Tabelle oder in die shopinfo.closed_shops-Tabelle geschrieben werden muss. Geschlossene Shops werden dadurch gekennzeichnet, dass sich ein „X“ im Shop-Typ befindet. Seite 16 Diplomarbeit Lanzanasto, Neumann, Plattner Nachdem eine passende „INSERT INTO“-Abfrage erstellt wurde, wird diese an der Datenbank ausgeführt und dieser Vorgang für alle anderen Shops wiederholt. 2.4.5. Import Hardware Zum Importieren der Hardware werden zwei Unterfunktionen benötigt. Die Unterfunktion Import_hardware stellt die Hauptkomponente der Übertragung dar. Gleich wie bei der Shop-Importierung werden auch hier zu Beginn bei der Übertragung aus dem EUROPE-Sheet alle vorhandenen Datensätze aus der shopinfo.hardware-Tabelle der Datenbank gelöscht. Anschließend werden auch hier die Spaltennummern je nach Sheet gespeichert, da diese nicht bei allen Kontinenten übereinstimmen. Eine weitere Besonderheit dabei ist, dass für Europa ein weiterer Sheet im Excel-File existiert, der detaillierte Informationen über die Hardware in europäischen Shops enthält, wie zum Beispiel bisherige Standorte. Daher wird in der „DO UNTIL“-Schleife immer unterschieden, ob die folgenden Informationen eine europäische Hardware betreffen oder eine andere. Die allgemeinen Details werden bei beiden Möglichkeiten gleich ausgelesen. Da es in einem Shop mehrere Hardwarekomponenten geben kann, muss die richtige SAP-Nummer des Shops gefunden werden. Dabei wird darauf geachtet, ob sich die Shop-Nummer ändert. Immer wenn das der Fall ist, handelt es sich um einen neuen Shop und die Reihe in der die richtige SAP-Nummer zu finden ist, wird aktualisiert. Anschließend werden alle notwendigen Informationen aus dem jeweiligen Sheet des Kontinents ausgelesen. Mit Hilfe des Country-Codes werden nun noch zusätzliche Informationen aus dem „ROC Info“-Sheet gewonnen. Handelt es sich um einen europäischen Shop, muss nun mit Hilfe der Seriennummer die passende Zeile im „EUR HW Details“-Sheet herausgesucht werden. Hier werden nun spezielle Informationen für die europäischen Shops übertragen. Bei anderen Shops werden diese Spalten in der Datenbank mit einem Leerstring bzw. einem Default-Wert gefüllt. Anschließend werden die Einträge wieder überprüft und wenn notwendig verändert. Vor allem beim „ASIA PACIFIC“-Sheet kommt es vor, dass Seite 17 Diplomarbeit Lanzanasto, Neumann, Plattner Seriennummern identisch sind und sie werden daher erweitert, um sie eindeutig identifizieren zu können. Es kommt auch vor, dass keine Seriennummer im ExcelFile eingetragen ist. Wenn dabei jedoch ein gültiger Hostname vorhanden ist, wird eine Seriennummer als Platzhalter generiert. Diese Nummer beginnt immer mit „unknown“. Danach kommen der Country-Code und die Nummer der Zeile, aus der die Information stammt. Damit auch Hardware aus einem VG-Shop richtig verknüpft werden kann, muss wieder die SAP-Nummer generiert werden. Sie muss gleich wie beim Shop selbst generiert werden, damit auch das gleiche Ergebnis geliefert wird und somit auch die Verknüpfung möglich ist. Anschließend wird wieder eine „INSERT INTO“-Abfrage erstellt und der Vorgang für alle weiteren Hardwarekomponenten wiederholt. Sogenannte „Retired Hardware“, also Hardware, die nicht mehr in Shops verwendet wird, wird mit dieser Funktion nicht importiert. Für diese Einträge wird die zweite Unterfunktion Import_retired_hardware verwendet. Einziger Unterschied zur ersten Funktion ist, dass hier alle Informationen nur aus dem Sheet „EUR HW Details“ kommen. Es werden jedoch nur Hardwarekomponenten importiert, die in der Spalte „active/retired“ als retired markiert sind. Seite 18 Diplomarbeit Lanzanasto, Neumann, Plattner 3. MySQL Database 3.1. Allgemein Wir verwenden zum Testen unseres Projekts eine auf unseren Rechnern lokal installierte MySQL-Datenbank, da diese in der Basisversion eine sehr anerkannte Freeware Lösung für eine Datenbank ist. Zum Bearbeiten der Datenbank wird von uns die MySQL Workbench verwendet, welche wie die MySQL Datenbank frei zur Verfügung steht. Der Entwickler der MySQL Datenbank sowie der MySQL Workbench ist Oracle Corporation. Die von uns eingerichtete MySQL Datenbank wird später in eine bestehende Microsoft Datenbank von Swarovski implementiert. Da die SQL-Befehle in ANSISQL geschrieben sind, ist es unerheblich, ob eine Microsoft-SQL- oder eine MySQL-Datenbank verwendet wird. Lediglich der Datenbankzugriff von dem Visual Basic Programm, beziehungsweise des Java Programms muss angepasst werden. Um die Datenbank an sich zu übertragen, werden einfach die Create-Statements kopiert und beim Erstellen einer weiteren Datenbank direkt ausgeführt. Es wird dabei nur die Datenbankstruktur erstellt. Seite 19 Diplomarbeit 3.2. Lanzanasto, Neumann, Plattner MySQL Workbench Die MySQL-Workbench ist ein Modellierungswerkzeug zur Planung und Wartung von Datenbanken. Der Entwickler dieses Modellierungstools ist Oracle Corporation und steht es für alle gängigen Betriebssysteme zur Verfügung. Die MySQL Workbench wird sowohl als „Community Edition“ unter der General Public License (GPL) als auch mit zusätzlichen Funktionalitäten als „Standard Edition“ kommerziell in Form eines Jahresabos angeboten. Es wurde bei diesem Projekt die Version 5.2.40 verwendet. Trotz umfangreichen Funktionen liegt der HauptEinsatzbereich der Workbench bei der Modellierung der Daten. Mittels der MySQL-Workbench können Entity-Relationship-Diagramme (ERDiagramme) erstellt werden. Diese sind oft der erste Schritt bei einer Datenbank Modellierung. Das ER-Diagramm stellt die Zusammenhänge zwischen einzelnen Tabellen graphisch dar, wodurch ein Verlust der Übersicht über eine komplexe Datenbankstruktur verhindert werden soll. Zusätzlich können Relationen zwischen den einzelnen Tabellen definiert werden, welche die Übersicht der Tabellenverknüpfungen verbessern sollen. Die Verbindung zur Datenbank wurde mittels einer TCP/IP-Verbindung ermöglicht. Seite 20 Diplomarbeit Lanzanasto, Neumann, Plattner 3.3. Datenbankübersicht Seite 21 Diplomarbeit Lanzanasto, Neumann, Plattner Die gesamte Datenbank besteht aus 4 miteinander verknüpften Tabellen, in der ersten Tabelle werden die verschiedenen Länder gespeichert. Für die einzelnen Shops gibt es zwei verschiedene Tabellen, eine für die geöffneten Shops und eine für die geschlossenen Shops. Diese beiden Tabellen sind im Prinzip genau gleich, sie haben nur einen einzigen Unterschied: In der „shops“-Tabelle wird die SAP-Nummer als Primary Key verwendet, in der „closed_shops“-Tabelle übernimmt diese Aufgabe ein automatischer Index. Das kommt daher, dass die SAP-Nummern geschlossener Shops wieder freigegeben sind und somit neu vergeben werden dürfen. Würde man dann einen Shop schließen, dessen SAP-Nummer mit der eines geschlossenen Shops übereinstimmt, würde ein Fehler in der Datenbank auftreten, da der Primary Key einzigartig sein muss. Um solchen Problemen vorzubeugen, wird hier ein automatischer Index von der Datenbank generiert. In der vierten und letzten Tabelle sind Informationen über die Hardware gespeichert, die in den einzelnen Shops eingesetzt wird. Die Pfeile im Datenbankstrukturplan stellen die einzelnen Verknüpfungen zwischen den Tabellen dar. Diese Verknüpfungen sind in der Datenbank nicht fest vorgegeben, sondern werden bei jeder Abfrage, bei der sie benötigt werden, angegeben. Die Verknüpfungen zwischen „shops“-Tabelle bzw. „closed_shops“-Tabelle und „country“-Tabelle müssen immer korrekt sein. Sollte ein User versuchen, einen Shop-Datensatz so zu verändern, dass die Spalte mit dem Country-Code mit keinem Land übereinstimmt, wird eine Fehlermeldung ausgegeben. Bei der Verknüpfung der Spalte „ship_to“ der „hardware“-Tabelle mit dem Primary Key eines Shop-Datensatzes muss nicht unbedingt eine passende Nummer angegeben werden. Wenn die Nummer existiert, besteht eine Verknüpfung, wenn nicht, wird dem Programm signalisiert, dass die ausgewählte Hardware in keinem Shop verwendet wird und somit entweder an einen neuen Shop vergeben werden kann oder bereits ausrangiert wurde. Außerdem kann keine Hardware mit einem geschlossenen Shop verknüpft werden, auch wenn die SAP-Nummer zufällig zusammenstimmen würde, wären die beiden Datensätze trotzdem nicht miteinander verknüpft. Seite 22 Diplomarbeit Lanzanasto, Neumann, Plattner Die Verknüpfungen, welche strichliert zwischen Hardware und Shop eingezeichnet sind, werden vom Programm eigentlich nicht benötigt. Hierbei handelt es sich um die Nummern der Shops, in denen die Hardware früher verwendet wurde. Da jedoch früher andere Nummern zur Identifizierung eines Shops verwendet wurden, stehen hier meist noch Nummern des alten Systems. Normalerweise sollte jedoch auch hier eine gültige SAP-Nummer aufscheinen, welche auf den vorherigen Shop hinweist. Seite 23 Diplomarbeit Lanzanasto, Neumann, Plattner 4. Java RCP Userinterface Das Nachfolgende Kapitel (inkl. Bilder) wurde bis auf weiteres mit Hilfe des Buches “Eclipse 4 Application Development“ von Lars Vogel verfasst. (ISBN: 3943747034) 4.1. Allgemein Für das User-Interface wurde die Java-RCP (Rich Client Project) Technologie empfohlen, weil diese plattformunabhängig ausführbar ist und leicht in RAP (Rich Ajax Platform) für Online-Verfügbarkeit umgeschrieben werden kann. Eine generelle Web-Application ist aus rechtlichen Gründen nicht realisierbar gewesen. 4.2. Programmierumgebung Zum Programmieren der GUI verwenden wir speziell Eclipse for RCP and RAP Developers (Juno Release) mit folgenden Add-Ons: E4 CSS Spy (incubation) und Eclipse e4 Tools (incubation). Der Hauptdownload bietet die Möglichkeit JavaRCP zu programmieren, die Add-Ons bieten diverse Hilfen. Zur einfacheren Abgleichung unter den Mitarbeitern wurde uns ein Subversion Client (siehe 4.2.3. Subversion Client) von Swarovski zur Verfügung gestellt. 4.2.1. Eclipse Eclipse ist eine Entwicklungsumgebung, welche ursprünglich zum Programmieren von Java gedacht war. Heute kann man mit passenden Plug-Ins auch andere Sprachen mit Eclipse programmieren. Der modulare Aufbau von Eclipse hilft beim genauen Anpassen der Programmieroberfläche an die eigenen Bedürfnisse. Die Formatierungstools und Autokomplettierung sind äußerst hilfreich für ein flüssiges Programmieren. Seite 24 Diplomarbeit Lanzanasto, Neumann, Plattner 4.2.2. Eclipse 4 – Eclipse Juno Eclipse 4 bietet neue Sets an Technologie, welche die Flexibilität der Eclipse Plug-In Entwicklung erhöhen. Die letzte veröffentlichte Version von Eclipse ist Juno, und basiert auf Eclipse 4.2. Dieses beinhaltet alle Vorteile von der Eclipse 3.x Serie, lässt aber die wunden Punkte der Version aus. Grundsätzlich sind aber viele Konzepte von Eclipse 3 in der neuen Version vorhanden. Es können auch weiterhin Eclipse 3.x Plug-Ins mit Eclipse 4 ohne Änderungen ausgeführt werden. 4.2.3. Subversion Client Mit dem entsprechenden Add-On kann von Eclipse direkt auf einen Subversion Client zugegriffen werden. So liegt das Projekt nicht mehr lokal auf einem Rechner, sondern wird auf einen angegebenen Server ausgelagert. Mit einem Update des Projektes wird die neueste Version heruntergeladen und lokal am Rechner in einer Workspace gespeichert. Nach Änderungen im Projekt wird dieses Commited, um diese Version wieder auf den Server zu laden. Neben der einfachen Abgleichung mit den Mitarbeitern ist ein weiterer großer Vorteil, dass in einer History ebenfalls Zugriff auf ältere Versionen des Projektes besteht. 4.3. Java-RCP Java-RCP (Rich Client Platform) ist eine bestimmte Art, wie man mit Java programmiert. Dabei ist die Grundstruktur der große Unterschied zu „normalem“ Java. Die Sprache ist weiterhin dieselbe. Die Vorteile der Rich Client Struktur liegen in der Anpassungsfähigkeit des Programms an das Problem und die leichte Umsetzung in ein webbasierendes Programm. Seite 25 Diplomarbeit 4.4. Lanzanasto, Neumann, Plattner JDBC Dieses Unterkapitel (inkl. Bilder) wurde mit Hilfe der Oracle-Homepage erstellt. http://www.oracle.com/technetwork/java/javase/jdbc/index.html (30.04.2013 17:00) JDBC (Java Data Base Connectivity) ist eine Datenbankschnittstelle, die eine Kommunikation zwischen einem Java-Programm und einer Datenbank ermöglicht. Die JDBC API ist Bestandteil der Java-Plattform und ist somit bereits überall vorhanden. Im Prinzip übersetzt diese Java-Statements in für die Datenbank leserliche Stücke und umgekehrt. Es gibt dabei vier verschiedene Typen: Links: Typ 4 Links: Typ 1 Mit dem Pure Java JDBC Driver wird Die Java Anfrage wird zuerst in einen ein Java Befehl direkt in einen DBMS ODBC (Open Data Base Connectivity) Befehl übersetzt. Code umgewandelt und muss dann auch von der Datenbank umgewandelt werden. Seite 26 wieder eigens Diplomarbeit Lanzanasto, Neumann, Plattner Rechts: Typ 3 Rechts: Typ 2 Über eine DB Middleware wird mit der Die Java Anfrage wird in Anfragen der Datenbank kommuniziert. Der Java eigenen Client-API übersetzt. Dabei Befehl wird dann speziell für die muss auf beiden Seiten ein zusätzlicher vorliegende Datenbank übersetzt. Code hinzugefügt werden. Java muss sich somit keine Gedanken über die Datenbank oder sonstige Sonderheiten machen. Diese sind die gängigsten Methoden. Diese beiden werden nur bei keiner anderen Möglichkeit verwendet. In diesem Projekt wird der Treiber-Typ 4 verwendet, weil dieser klein und ausreichend für die Anforderungen ist. Um diesen JDBC-Treiber verwenden zu können, muss neben der bereits vorhandenen JDBC API noch ein Jar-File eingebunden werden. Dieses muss im Manifest-File in den Classpath eingefügt werden. 4.5. Programmübersicht 4.5.1. Basis-Projekt Um das Basis-Projekt zu erstellen, verwendet man am besten den vorhandenen Wizard, damit alle benötigten Files und Strukturen automatisch erstellt werden. Dazu wählt man File → New → Others → Eclipse 4 → Eclipse 4 Application Project aus und gibt einen Projektnamen an und wählt entsprechende weitere Optionen aus. Nachdem das Projekt erstellt wurde, kann man das .product-File auswählen, und im Übersicht-Tab auf den Launch an Eclipse Apllication Button klicken. Das RCPProjekt startet. Seite 27 Diplomarbeit Lanzanasto, Neumann, Plattner Dies kann je nach Optionenwahl schon mit Beispiel-Komponenten gefüllt sein, wie zum Beispiel einer Menü-Leiste. Um Fehler beim Start zu vermeiden, müssen die Plug-Ins geprüft werden und bei Bedarf die fehlenden hinzugefügt werden. Dies geschieht im Plug-In-Tab des Product-Files. Dort können mit dem Klick auf Add Required Plug-Ins automatisch die fehlenden Plug-Ins hinzugefügt werden. Mit dem Aktivieren der Checkbox Validate plug-ins prior to launching werden dann vor jedem Start des Programms automatisch die Plug-Ins überprüft. 4.5.2. RCP-Struktur Ein fertiges Projekt besteht aus diversen Files und Ordnern. Wie in der Abb. 3: Ordnerstruktur zu erkennen ist, sind grundsätzlich einmal die JRE (Java Runtime Environment) System Library und die Plug-in Dependencies vorhanden. Diese stellen die Grundfunktion von Java dar. Im Application.e4xmi File ist die Oberfläche des Programms definiert. Beim Öffnen dieser Datei öffnet sich ein Tool, mit dem die Oberfläche verändert kann. Die Oberflächenbearbei- tung ist im Kapitel werden Abb. 3: Ordnerstruktur 4.5.3 Oberfläche genauer beschrieben. Das wichtigste File des Projekts ist das *.product File (hier: at.swarovski.rcp.shopinfo.product). Dabei handelt es sich nicht um ein Textfile, sondern um eine graphische Oberfläche, die formularartig Parameter für das Projekt umsetzt. In diesem File kann das Programm unter anderem auch Seite 28 Diplomarbeit Lanzanasto, Neumann, Plattner gestartet werden. Dort befindet sich auch ein Export-Wizard, mit dem das Projekt exportiert werden kann. In weiteren Reitern lassen sich z. B. die Dependencies eintragen oder ein Branding (Icons) hinzufügen. Im File build.properties können weitere Einstellungen zum Generieren des Programms vorgenommen werden. Das plugin.xml File öffnet wieder ein Optionen-Feld mit mehreren Reitern. Das plugin.xml File ist dabei so ein Reiter. Dieses File wird automatisch erstellt und kann nicht „per Hand“ abgeändert werden. Mit diesem Optionen-Feld lassen sich fast alle Einstellungen für das Projekt vornehmen. Es beinhaltet auch manche Reiter des *.product Files. Zu diesen vorgenerierten Dateien kommen noch einige vorgenerierte Ordner und selbsterstellte Ordner. Im Ordner src befinden sich alle Source-Files, der Quelltext sozusagen. Diese Files nennt man Klassen und sie sind in Packages aufgeteilt. Diese Packages haben aber keine große Rolle, sie agieren wie Ordner. Im ersten Package at.swarovski.rcp.shopinfo befindet sich Activator.java. Dies ist ein automatisch generiertes File, welches für unser Projekt nicht abgeändert werden muss. Im Package at.swarovski.rcp.shopinfo.database sind alle notwendigen Klassen vorhanden, um mit einer Datenbank Verbindung aufzunehmen und auch Daten auszutauschen. Die Verbindung zur Datenbank wird im Kapitel 4.5.4 Verbindung zur DB genauer beschrieben. Das Package at.swarovski.rcp.shopinfo.handlers beinhaltet alle Funktionen des Programms. Im Kapitel 4.5.5 Handler werden diese genauer beschrieben. Im Package at.swarovski.rcp.shopinfo.listener befindet sich der Treelistener, eine Klasse, welche das ausgewählte Item im Tree feststellen kann. Im Package at.swarovski.rcp.shopinfo.view werden die Oberflächen der einzelnen Parts definiert. Im Kapitel 4.5.3 Oberfläche und Application Model wird dies genauer beschrieben. Seite 29 Diplomarbeit Lanzanasto, Neumann, Plattner Im Ordner css befindet sich das Style-Sheet File, welches für dieses Projekt nicht verändert wird. Im Ordner icons sind alle Icons vorhanden, die für das Projekt benötigt werden. Sie werden z. B. in der Menüleiste verwendet. Im Ordner lib befindet sich der Treiber für die Verbindung zur Datenbank. Und schlussendlich befindet sich im Ordner META-INF das MANIFEST.MF. Dieses öffnet auch das Optionen-Feld, welches weiter oben in diesem Kapitel schon beschrieben wurde. 4.5.3. Oberfläche und Application Model Um das Basis-Projekt an die jeweiligen Bedürfnisse des Kunden anzupassen, muss zuerst einmal die Oberfläche verändert werden, bevor die Intelligenz hineinprogrammiert werden kann. Mit den zusätzlichen Tools, die nachträglich zu Eclipse hinzugefügt wurden, kann relativ einfach die Oberfläche des Programms verändert werden. Dazu muss das Application-File geöffnet werden. Es erscheint ein Menü, mit dem man die benötigten Komponenten hinzufügen kann. Die wichtigsten User Interface Komponenten einer Eclipse 4 Applikation sind Windows, Parts, Perspectives, PartStacks und PartSashContainers. Typischerweise besteht eine Applikation, ein Programm, aus einem Window (Fenster). Man ist aber nicht darauf beschränkt, es ist möglich, weitere Fenster hinzuzufügen. Parts sind User Interface Komponenten, die einem ermöglichen, Daten zu navigieren und zu modifizieren. Diese Parts sind meistens eingeteilt in Views und Editors. Diese Unterteilung basiert primär aber nicht auf technischen Unterschieden, sondern auf der Art der Verwendung dieser Parts. Ein View wird verwendet, um ein Daten-Set, welches möglicherweise hierarchisch aufgebaut ist, direkt zu verändern. Wird in einer View eine Änderung vorgenommen, so geschieht diese auch direkt in der darunterliegenden DatenStruktur. Seite 30 Diplomarbeit Lanzanasto, Neumann, Plattner Mit einem Editor werden einzelne Daten-Elemente verändert, zum Beispiel eine Datei. Vorgenommene Änderungen treten aber erst in Kraft, wenn diese explizit gespeichert werden. Perspectives sind visuelle Container, welche Parts beinhalten. Sie werden verwendet, um Parts für verschiedene Anwendungen zu positionieren. Parts können in den Perspectives geöffnet, geschlossen und neu arrangiert werden. Parts können direkt im Window oder in einer Perspective platziert werden. Um solche Parts zu arrangieren, können PartStacks und PartSashContainer verwendet werden. PartStacks beinhalten Parts, welche immer nur zur gleichen Zeit dargestellt werden können. Solche PartStacks können über Tabs ausgewählt werden. PartSashContainer stellen den Inhalt entweder horizontal oder vertikal zur gleichen Zeit dar. Zu den visuellen Teilen in Eclipse (Perspectives, Parts, etc.) gibt es noch verschiedene nicht-visuelle Komponenten wie zum Beispiel Handlers, Commands und Key Bindings. Bei Eclipse 4 wird das sogenannte Application Model verwendet, um die Struktur einer Applikation zu beschreiben. In diesem Modell sind sowohl die sichtbaren als auch die nicht sichtbaren Elemente der Oberfläche enthalten. Jedes Element hat bestimmte Eigenschaften, welche den derzeitigen Zustand beschreiben. Manche Elemente müssen in einer hierarchischen Struktur angeordnet werden (Parts in Perspectives). Dieses Application Model definiert jedoch nur die Struktur der Oberfläche und nicht den Inhalt des User Interfaces. Es stellt die Parts zur Verfügung, wo dann später zum Beispiel SWT-Widgets integriert werden können. Diese müssen nach wie vor im Source-Code eingebettet werden. Das Application model entspricht sozusagen nur einem Haus mit Räumen, der Inhalt muss extra programmiert werden. Seite 31 Diplomarbeit Lanzanasto, Neumann, Plattner Abb. 4 Application Model Dieses Application Model ist ein einziges File mit dem Namen Application.e4xmi und befindet sich im Hauptverzeichnis des Projekts. Die Verbindung der einzelnen Elemente zu den Java Klassen geschieht über Uniform Resource Identifier (URI). Diese beschreibt den Ort, wo sich die Klasse befindet. Die Klasse beschreibt nun den Inhalt und das Verhalten dieses Inhalts. Wird nun ein Element aus dem Application Model aktiviert, so wird gleichzeitig die Klasse instanziert. 4.5.3.1. Commands, Handlers Das Application Model kann auch Commands und Handlers beinhalten. Ein Command ist in diesem Fall eine Beschreibung einer Aktion, wie zum Beispiel „speichern“ oder „kopieren“. Das Verhalten eines Commands wird bestimmt von Handler. Ein Handler definiert eine Klasse über den contributionURI. Dieses Attribut wird als Class URI im Model Editor dargestellt. Solche Handler können global für die gesamte Applikation fungieren oder nur für einzelne Parts. So kann es zum Beispiel eine Save-Aktion für die gesamte Applikation geben, und eine eigene Save-Aktion für einen speziellen Part. Seite 32 Diplomarbeit 4.5.3.2. Lanzanasto, Neumann, Plattner Menus, Toolbars Für Windows und Parts können Menus und Toolbars erstellt werden. Menu und Toolbar Items enthalten Referenzen zu Commands. Ist dann so ein Command ausgewählt, wird von der Runtime der dazugehörende Handler ausgewählt. Toolbars sind im Application Model als Trimbar eingebunden. Eine Trimbar kann für Windows definiert werden. Es können hier zur Übersicht auch Separatoren und Untermenüs erstellt werden. Um eine Toolbar/Menüleiste im Fenster zu implementieren, so benötigt man dreierlei Dinge: ein Handled Tool Item, einen Command und einen Handler. Das Handled Tool Item beschreibt dabei die optischen Eigenschaften des Menüeintrags. Dabei kann unter anderem der angezeigte Text, die Art des Eintrags (Push, Radial, Check) oder ein Icon verändert werden. Diesem Item muss noch der vorher erwähnte Command eingetragen werden. Dem Handler müssen der Command und eine URI zugewiesen werden, denn diese weist auf die dazugehörende Klasse in Java hin. Schlussendlich wird bei einer Aktion mit dem Menüeintrag diese Klasse ausgeführt. Damit alle Änderungen im Application Model auch gleich übernommen werden, muss im Product-Configuration-File bei im Reiter Launching beim Unterpunkt Programm Arguments folgender Eintrag hinzugefügt werden: -clearPersistedState Seite 33 Diplomarbeit 4.5.3.3. Lanzanasto, Neumann, Plattner Inhalt des Application Model Das Application Model beschreibt nur die Struktur des UI. Alles, was in diese Struktur hinein kommt, Labels, Textfelder, Buttons usw., kann mit SWT-Widgets in den Parts integriert werden. SWT bedeutet Standard Widget Toolkit und umfasst eine große Bibliothek an diversen graphischen Ein- und AusgabeKomponenten. Um diese org.eclipse.swt.widgets zu verwenden, muss und org.eclipse.swt.custom die Bibliothek zuerst einge- bunden werden. Die Widgets werden per Snippet (Code-Besipiel) im Internet angeboten. Der Code kann einfach in den Quellcode hineinkopiert werden. Um diese Widgets optisch ansprechend zu positionieren, muss zuerst ein Layout mit SWT erstellt werden. Dann kann das Widget genau positioniert werden. SWT beitet eine Reihe von Layout-Managern. Dabei gibt es AbsoluteLayout, FillLayout, RowLayout, GridLayout und FormLayout. Jedes Layout verfügt über andere Eigenschaften und ist somit nicht für jeden Fall geeignet. In diesem Projekt werden hauptsächlich GridLayouts verwendet, da diese den Anforderungen am ehesten entsprechen. Grundsätzlich ordnet dieses die Widgets in einer Gitterform an. Es können dabei die Anzahl der Spalten und die Spalten- und Abstand-Größe angegeben werden. Zu jedem Layout Manager gibt es auch noch eine Layout Data, in dem widgetspezifische Eigenschaften eingetragen werden können. Dabei muss die Data dem Manager entsprechen, ansonsten kommt es zu einem Fehler bei der Ausführung des Programms. Durch die Benützung eines GridLayouts wird das GridData Objekt verwendet. Seite 34 Diplomarbeit 4.5.3.4. Lanzanasto, Neumann, Plattner Fenster-Struktur Dieses Programm besteht aus nur einem Window (Trimmed Window). In diesem Window wird ein PerspectiveStack und darin eine Perspective erstellt, um Parts zu beherbergen. Um immer zwei Parts gleichzeitig darstellen zu lassen benötigt es einen PartSashContainer. Dieser trennt das Fenster vertikal in zwei Hälften, dass die Parts nebeneinander angezeigt werden. In diesem PartSashContainer befinden sich zwei PartStacks. Abb. 5: Fenster-Struktur Einer besteht aus zwei, der andere aus drei Parts. Diese können per Tabs durchgeschalten werden. Am oberen Fensterrand befindet sich eine Toolbar. Dazu muss ein Window Trim erstellt werden, und darin eine Toolbar. Diese wird dann mit Handled Tool Items und Separatoren befüllt. Abb. 6: Toolbar Zusätzlich zu den visuellen Komponenten kommen noch acht Handlers und acht Commands. Diese beziehen sich hauptsächlich auf die Funktionen der Toolbar. Seite 35 Diplomarbeit Lanzanasto, Neumann, Plattner Abb. 7: Handlers Abb. 8: Commands Schlussendlich sieht die Grundstruktur des Programms, gestaltet im Application Model wie folgt aus: Toolbar PartStack 1 PartStack 2 Abb. 9: Grundstruktur Seite 36 Diplomarbeit Lanzanasto, Neumann, Plattner 4.5.4. Verbindung zur DB Die Verbindung zur Datenbank wird mit Hilfe der „DatabaseConnection“-Klasse verwirklicht. Wichtig ist, dass von dieser Klasse immer nur eine Instanz existiert, da ansonsten Fehler auftreten können, wenn gleichzeitig mehrere Verbindungen zur Datenbank aufgebaut werden. Daher verwendet man einen sogenannten Singleton. Somit wird beim Generieren einer solchen Instanz immer zuerst abgefragt, ob diese schon existiert und anschließend der weitere Vorgang entschieden. public static DatabaseConnection getInstance() { if (instance == null) { instance = new DatabaseConnection(); } return instance; } Innerhalb der Klasse existieren zwei verschiedene Variablen, die möglicherweise verwechselt werden könnten. DatabaseConnection: Hier spricht man von der Instanz dieser Klasse, die sämtliche Informationen nach außen hin bereithält. Connection: So wird die eigentliche Verbindung zur Datenbank bezeichnet. Seite 37 Diplomarbeit 4.5.4.1. Lanzanasto, Neumann, Plattner Verbindungsaufbau In der Methode „DatabaseConnection()“ wird die Verbindung zur Datenbank mittels JDBC -Treiber (siehe 4.4 JDBC) aufgebaut. private DatabaseConnection() { try { Class.forName("com.mysql.jdbc.Driver").newInstance(); connection = DriverManager.getConnection( "jdbc:mysql://localhost/shopinfo?zeroDateTimeBehavior=convertToNull", "root", "gipfelkreuz"); connection.setReadOnly(false); } catch (InstantiationException e) { System.out.println("InstantiationException: " + e); } catch (IllegalAccessException e) { System.out.println("IllegalAccessException: " + e); } catch (ClassNotFoundException e) { System.out.println("ClassNotFoundException: " + e); } catch (SQLException e) { System.out.println("SQLException: " + e); } } Wie man sieht, handelt es sich hier um die Verbindung zu einer lokalen MySQL Datenbank, die während der Entwicklung verwendet wird. Die Argumente beziehen sich dabei erstens auf die Adresse der Datenbank, zweitens auf den Benutzer und drittens auf das Passwort der Datenbank. Die Adresse der Datenbank ergibt sich aus dem Treiber, dann die Art der Datenbank und schlussendlich der wirklichen Adresse der Datenbank. In diesem Fall muss zusätzlich noch ein zeroDateTimeBehavior=convertToNull Statement hinzugefügt werden, um Fehler mit Datumswerten in der Datenbank zu vermeiden. Da beim Verbindungsaufbau Fehler auftreten können, wenn beispielsweise die Datenbank nicht erreicht werden kann oder Benutzer und Passwort nicht stimmen, muss dieser Code in einer Try-Catch-Anweisung ausgeführt werden, um diverse Exceptions abzufangen. Seite 38 Diplomarbeit 4.5.4.2. Lanzanasto, Neumann, Plattner Auslesen der Daten Zum Auslesen der Daten aus der Datenbank gibt es für jede Tabelle der Datenbank drei verschiedene Methoden. public List<Shop> getShops() { . . . } public List<Shop> getShops(String sSQLStatement) { . . . } public List<Shop> getShops(TreeListener treeListener) { . . . } Bei der ersten Methode werden keine Parameter übergeben, da immer die gesamte Tabelle als Liste von Datensätzen zurückgegeben wird. Diese Methode wird hauptsächlich für die Gesamttabellen und für die Erstellung der Baumstruktur verwendet. Die beiden weiteren Methoden sind für die speziellere Rückgabe von Datensätzen konzipiert. Dabei kann entweder eine fertige SQL-Abfrage als String übergeben werden, wie es zum Beispiel bei der erweiterten Suche der Fall ist, oder es wird die im TreeListener gespeicherte Information übergeben, um daraus eine SQL-Abfrage zu formen und einzelne Datensätze, unter anderem für die Details-Anzeige, zu erhalten. Seite 39 Diplomarbeit Lanzanasto, Neumann, Plattner sSQLStatement = "SELECT Shops.sap_ship_to, Shops.country_code, Shops.type,..." sSQLStatement = sSQLStatement + "FROM shopinfo.country Country, shopinfo.shops Shops"; sSQLStatement = sSQLStatement + " WHERE Country.country_code = Shops.country_code "; if (treeListener.getSelectedRegion() != "") { sSQLStatement = sSQLStatement + "AND Country.region = '" + treeListener.getSelectedRegion() + "'"; } if (treeListener.getSelectedCountry() != "") { sSQLStatement = sSQLStatement + "AND Shops.country_code = '" + treeListener.getSelectedCountry() + "'"; } if (treeListener.getSelectedShop() != "") { sSQLStatement = sSQLStatement + "AND Shops.sap_ship_to = '" + treeListener.getSelectedShop() + "'"; } ResultSet rs = stmt.executeQuery(sSQLStatement + " LIMIT 10000"); Obwohl nur ein Datensatz zurückgegeben werden muss, wird aus Einfachheitsgründen eine Liste zurückgegeben, die jedoch nur einen Eintrag besitzt, dieser eine Eintrag wird im Anschluss über die „get(0)“-Funktion herausgeholt. Die vorhin angesprochenen Listen bestehen aus mehreren Objekten, bei denen die Inhalte eines Datensatzes mittels „set“-Funktion in privaten Variablen eingetragen werden. Es gibt hierbei vier verschiedene Klassen für die verschiedenen Tabellen der Datenbank. Darin befinden sich nur private Variablen entsprechend der Spalten einer Tabelle, welche mittels „set“- und „get“Funktionen verarbeitet werden. public class Shop { private String sapShipTo; . . . public String getSapShipTo() { return sapShipTo; } public void setSapShipTo(String sapShipTo) { this.sapShipTo = sapShipTo; } . . . } Seite 40 Diplomarbeit Lanzanasto, Neumann, Plattner Zum Verändern von Datensätzen der Datenbank werden in der Klasse zwei weitere Methoden verwendet. Beide haben prinzipiell die gleiche Funktion, sie führen die als String übergebene SQL-Schreib-Abfrage auf der Datenbank durch. Einziger Unterschied ist, dass eine Funktion mögliche Exceptions selbst abfängt, die andere Funktion gibt solche nur an die ausführende Instanz zurück, wo diese dann weiter verarbeitet werden, zum Beispiel wird diese Vorgangsweise bei der Ausgabe von Pop-Up-Fehlermeldungen benötigt. public void editDatabase(String sSQLStatement) { try { Statement stmt = connection.createStatement(); stmt.execute(sSQLStatement); stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } public void editDatabaseWithException(String sSQLStatement) throws SQLException { Statement stmt = connection.createStatement(); stmt.execute(sSQLStatement); stmt.close(); } Weitere Funktionen sind die „getSingleCount()“- und die „getHwAge()“-Methoden. Die „getSingleCount()“-Methode wird vor allem zum Erstellen von Statistiken verwendet, findet Überprüfungsvorgängen. aber Hier auch wird Anwendung wieder als in String eine verschiedenen SQL-Abfrage übergeben, welche jedoch nur einen „Count“-Befehl enthält und somit lediglich eine Zahl wieder zurückgibt, diese Zahl wird anschließend als String weitergegeben und muss dann verarbeitet werden. public String getSingleCount(String sSQLStatement) { try { Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery(sSQLStatement); String sCount = "Error"; if (rs.next()) sCount = rs.getString(1); rs.close(); stmt.close(); return sCount; } catch (SQLException e) { e.printStackTrace(); Seite 41 Diplomarbeit Lanzanasto, Neumann, Plattner return null; } } Die „getHwAge()“-Methode wird eigentlich nur bei der Anzeige der HardwareDetails benötigt, um das Alter der Hardware zu ermitteln. Hier wird das Datum der Erstinstallation als String übergeben und anschließend mittels SQL-DATEDIFFAbfrage die Differenz zum aktuellen Datum in Jahren über die Datenbank ermittelt. public String getHwAge(String sDate) { String sSQLStatement; String sAge = "-"; int iAge = 0; float fAge = 0; try { Statement stmt = connection.createStatement(); sSQLStatement = "SELECT DATEDIFF ( NOW(), \""; sSQLStatement = sSQLStatement + sDate; sSQLStatement = sSQLStatement + "\")"; ResultSet rs = stmt.executeQuery(sSQLStatement); if (rs.next()) iAge = rs.getInt(1); rs.close(); stmt.close(); fAge = iAge; fAge = fAge / 365; sAge = Float.toString(fAge); try { sAge = sAge.substring(0, sAge.indexOf(".") + 3); } catch (StringIndexOutOfBoundsException e1) { } if (fAge > 1000) sAge = "-"; return sAge; } catch (SQLException e) { e.printStackTrace(); return "-"; } } Seite 42 Diplomarbeit Lanzanasto, Neumann, Plattner 4.5.5. Handler Alle Klassen im Package „at.swarovski.rcp.shopinfo.handlers“ werden für die Schaltflächen in der Menüleiste benötigt. Dabei existiert für jeden Button ein Handler. Die meisten dieser Handler öffnen beim Auslösen einen neuen Part, um beispielsweise das Suchfenster oder Statistiken anzuzeigen. Dabei wird der neue Part im Prinzip immer gleich generiert. Nachdem der Part erschaffen wurde, werden Eigenschaften wie Label und „Closeable“-Status eingestellt. Anschließend wird definiert, mit der Instanz welcher View-Klasse der Part gefüllt werden soll. Danach wird der passende PartStack gesucht, um dann den neuen Part zu diesem Stack hinzuzufügen. public class NewShopHandler { @Execute public void execute(EPartService partService, MApplication application, EModelService modelService) { MPart part = MBasicFactory.INSTANCE.createPart(); part.setLabel("New"); part.setCloseable(true); part.setContributionURI("bundleclass://at.swarovski.rcp.shopinfo/ at.swarovski.rcp.shopinfo.view.NewShop"); List<MPartStack> stacks = modelService.findElements(application, null, MPartStack.class, null); stacks.get(1).getChildren().add(part); partService.showPart(part, PartState.ACTIVATE); } } Der Handler des „showDetails“-Buttons in der Menüleiste generiert die Parts auch nach genau diesem Verfahren, jedoch wird dabei vorher mittels TreeListener entschieden, welche Art von Details angezeigt werden soll, damit schlussendlich auch die richtige Label-Beschriftung ausgewählt wird. Seite 43 Diplomarbeit Lanzanasto, Neumann, Plattner public class DetailsHandler { @Execute public void execute(EPartService partService, MApplication application, EModelService modelService) { TreeListener treeListener = TreeListener.getInstance(); if (treeListener.getSelectedShop() != "") { MPart part = MBasicFactory.INSTANCE.createPart(); part.setLabel("Shop-Details: " + treeListener.getSelectedShop()); part.setCloseable(true); . . . } else if (treeListener.getSelectedClosedShop() != "") { MPart part = MBasicFactory.INSTANCE.createPart(); part.setLabel("Closed Shop-Details: " + treeListener.getSelectedClosedShop()); part.setCloseable(true); . . . } else if (treeListener.getSelectedHardware() != "") { MPart part = MBasicFactory.INSTANCE.createPart(); part.setLabel("HW-Details: " + treeListener.getSelectedHardware()); . . . } else if (treeListener.getSelectedCountry() != "") { MPart part = MBasicFactory.INSTANCE.createPart(); part.setLabel("Country-Details: " + treeListener.getSelectedCountry()); . . . } } } Andere Handler wie der „About“-Handler oder der „Quit“-Handler werden automatisch bereitgestellt und müssen bei Bedarf nur abgeändert werden, um zum Beispiel die „About“-Information anzupassen. Seite 44 Diplomarbeit Lanzanasto, Neumann, Plattner 4.5.6. Gesamttabellen Die Gesamttabellen werden sofort nach Start des Programms angezeigt. Sie stellen den kompletten Inhalt der Datenbank-Tabellen „country“, „shops“ und „closed_shops“ dar. Dabei wird für jede Tabelle ein eigener Part erstellt. Zuerst wird dabei eine Tabelle mit sämtlichen Spalten für die Datensätze erstellt. final Table tableShop = new Table(parentShop, SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION); tableShop.setLinesVisible(true); tableShop.setHeaderVisible(true); GridData dataShop = new GridData(SWT.FILL, SWT.FILL, true, true); dataShop.heightHint = 200; tableShop.setLayoutData(dataShop); final String[] titlesShop = { "SAP ship-to", "country code", "Type", "Pilote",... for (int i = 0; i < titlesShop.length; i++) { TableColumn column = new TableColumn(tableShop, SWT.NONE); column.setText(titlesShop[i]); } Anschließend werden zwischengespeichert. die Diese Datensätze eingelesen Liste dann wird in und einer in einer Liste for-each-Schleife durchlaufen und dabei jeder Eintrag in die Tabelle geschrieben. shops = db_conShop.getShops(); if (shops != null) { for (Shop entry : shops) { TableItem item = new TableItem(tableShop, SWT.NONE); item.setText(0, entry.getSapShipTo()); item.setText(1, entry.getCountryCode()); item.setText(2, entry.getType()); item.setText(3, entry.getPilote()); . . . Abschließend wird noch ein Listener generiert. So kann ein Datensatz in der Tabelle durch Klicken ausgewählt werden, um später in der Detailansicht Einträge zu verändern. Seite 45 Diplomarbeit Lanzanasto, Neumann, Plattner Rectangle clientArea = parentShop.getClientArea(); tableShop.setLocation(clientArea.x, clientArea.y); Point size = tableShop.computeSize(SWT.DEFAULT, 200); tableShop.setSize(size); tableShop.addListener(SWT.MouseDown, new Listener() { public void handleEvent(Event event) { Rectangle clientArea = tableShop.getClientArea(); Point pt = new Point(event.x, event.y); int index = tableShop.getTopIndex(); while (index < tableShop.getItemCount()) { boolean visible = false; TableItem item = tableShop.getItem(index); for (int i = 0; i < titlesShop.length; i++) { Rectangle rect = item.getBounds(i); if (rect.contains(pt)) { final TreeListener searchListener = TreeListener.getInstance(); searchListener.setSelectedShop(tableShop.getItem(index).getText(0)); System.out.println(tableShop.getItem(index).getText(0)); } if (!visible && rect.intersects(clientArea)) { visible = true; } } if (!visible) return; index++; } } }); 4.5.7. TreePart Der TreePart stellt im Programm das Kernelement zur schnellen, strukturierten Suche von Datensätzen dar. Einerseits besteht die Möglichkeit über ein Textfeld direkt einen Shop auszuwählen, indem man die zu ihm gehörige SAP-Nummer eintippt, andererseits wird die eigentliche Baumstruktur zur Auswahl der Datensätze angezeigt. Seite 46 Diplomarbeit 4.5.7.1. Lanzanasto, Neumann, Plattner Quick search Zur schnellen Suche wird im Prinzip nur ein Textfeld verwendet, das mit einem Listener ausgerüstet wird, sodass die Eingabe mittels Entertaste bestätigt werden kann. Dabei wird die eingegebene Nummer versucht im TreeListener zu speichern, um so den gewünschten Shop auszuwählen (siehe 4.5.8 TreeListener). textstyle; parent.setLayout(new GridLayout(1, false)); final TreeListener searchListener = TreeListener.getInstance(); Listener listener = new Listener() { public void handleEvent(Event event) { Text t = (Text) event.widget; String msg = t.getMessage(); if (event.detail == SWT.ICON_CANCEL) { System.out.println("Cancel on " + msg); } else if (event.detail == SWT.ICON_SEARCH) { System.out.println("ICON on " + msg); } else { System.out.println("Default selection on " + msg + ": " + t.getText()); searchListener.setSelectedShop(t.getText()); } } }; textstyle = new Text(parent, SWT.SEARCH); textstyle.setMessage("Quick search"); textstyle.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); textstyle.addListener(SWT.DefaultSelection, listener); 4.5.7.2. Baumstruktur Um die Baumstruktur aufbauen zu können, müssen zuerst alle Datensätze aus der Datenbank eingelesen und zwischengespeichert werden. Dazu wird jeweils eine Liste für Shops, Länder und Hardware generiert. Zusätzlich werden noch Listen für Regionen und Shoptypen benötigt, in denen die bereits generierten Items gespeichert werden. Außerdem wird eine bool‘sche Variable verwendet, um anzugeben, ob der aktuelle Datensatz als neues Item erstellt werden muss oder nicht. Zuerst werden beim dynamischen Aufbau der Baumstruktur alle Datensätze der „country“-Tabelle durchlaufen. Dabei wird überprüft, ob der Region-Eintrag des Landes mit einem Punkt des Trees übereinstimmt, genauer gesagt wird der Seite 47 Diplomarbeit Lanzanasto, Neumann, Plattner Eintrag mit der Liste der Items verglichen, denn hier werden diese immer eingetragen, wenn ein solches erstellt wird. if (countries != null) { for (Country entryRegion : countries) { bNewItem = true; for (String sUniqueRegion : ItemsRegion) { if (entryRegion.getRegion().equals(sUniqueRegion)) { bNewItem = false; } } if (bNewItem == true) { // Region-----------------------------------------------------TreeItem treeItemRegion = new TreeItem(tree, 0); treeItemRegion.setText(entryRegion.getRegion()); ItemsRegion.add(entryRegion.getRegion()); . . . } } } Wenn also der Region-Eintrag des Landes noch nicht verwendet wurde, wird für diese Region ein Punkt erstellt und in der Item-Liste eingetragen. Anschließend werden in einer for-each-Schleife alle Country-Datensätze durchlaufen und immer dann wird ein neuer Unterpunkt erstellt, wenn die Region des Landes mit der ausgewählten Region übereinstimmt. Außerdem wird hier die Item-Liste, in der die Shoptypen zwischengespeichert sind, geleert, da natürlich in jedem Land immer wieder die gleichen Shoptypen vorkommen können. . . . // Country-----------------------------------------------------for (Country entryCountry : countries) { if (entryCountry.getRegion().equals(entryRegion.getRegion())) { ItemsType.clear(); TreeItem treeItemCountry = new TreeItem(treeItemRegion, 0); treeItemCountry.setText(entryCountry.getCountryCode() + " (" + entryCountry.getVgNr() + ")"); ItemsCountry.add(entryCountry.getCountryCode()); . . . } } . . . Seite 48 Diplomarbeit Lanzanasto, Neumann, Plattner Nun werden die Shops in einer for-each-Schleife durchlaufen, um für jeden neu gefunden Shoptypen einen neuen Unterpunkt zu erstellen. Dabei muss aber zusätzlich noch überprüft werden, ob der gefundene Eintrag auch wirklich einen Shop dieses Landes beinhaltet. Wenn dies der Fall ist, wird wieder ein neues Item erstellt und in der Item-Liste für Shoptypen eingetragen. . . . // Shop-Type-----------------------------------------------------for (Shop entryType : shops) { bNewItem = true; for (String sUniqueType : ItemsType) { if (entryType.getType().equals(sUniqueType)) { bNewItem = false; } } if (bNewItem == true) { if (entryCountry.getCountryCode().equals(entryType.getCountryCode())) { TreeItem treeItemType = new TreeItem(treeItemCountry, 0); treeItemType.setText(entryType.getType()); ItemsType.add(entryType.getType()); . . . } } } . . . Als Nächstes werden die Datensätze der „shops“-Tabelle ein weiteres Mal in einer for-each-Schleife durchlaufen, um hier für jeden Shop, der sich im richtigen Land befindet und den passenden Shoptypen besitzt, einen neuen Unterpunkt zu erstellen. Zum Abschluss werden noch die Hardware-Einträge mit einer for-each-Schleife durchsucht, um auch für die zum Shop gehörige Hardware noch weitere Unterpunkte zu erstellen. Seite 49 Diplomarbeit Lanzanasto, Neumann, Plattner . . . // SAP_Ship-to-----------------------------------------------------for (Shop entrySap : shops) { if (entrySap.getCountryCode().equals(entryCountry.getCountryCode()) && entrySap.getType().equals(entryType.getType())) { TreeItem treeItemSap = new TreeItem(treeItemType, 0); treeItemSap.setText(entrySap.getSapShipTo() + " (" + entrySap.getLocation() + " - " + entrySap.getStoreName() + ")"); // Hardware-----------------------------------------------------for (Hardware entryHardware : hardware) { if (entryHardware.getShipTo().equals(entrySap.getSapShipTo())) { TreeItem treeItemHardware = new TreeItem(treeItemSap, 0); treeItemHardware.setText(entryHardware.getHostname() + " - (" + entryHardware.getSn() + ")"); ItemsHw.add(entryHardware.getHw()); } } ItemsSap.add(entrySap.getCountryCode()); } } . . . Nachdem die Baumstruktur nun dynamisch erstellt wurde, wird diese noch mit dem TreeListener verknüpft, um durch Klicken eines Unterpunktes auch wirklich einen Eintrag auswählen zu können. TreeListener treeListener = TreeListener.getInstance(); treeListener.SelectionListener(tree); 4.5.8. TreeListener Die TreeListener-Klasse hatte in den ersten Versionen des Programms eigentlich nur die Aufgabe, herauszufinden, welcher Eintrag der Baumstruktur geklickt wurde, und diese Information für die weitere Verarbeitung zu speichern. Mit Voranschreiten der Programmierung gewann diese Klasse aber immer mehr an Bedeutung und stellt schlussendlich einen zentralen Punkt des Programms dar. Seite 50 Diplomarbeit Lanzanasto, Neumann, Plattner Während des Programmablaufs existiert immer nur eine Instanz dieser Klasse, ein sogenannter Singleton. Das bedeutet, wenn diese Instanz benötigt wird, wird nicht in einer anderen Klasse eine Instanz generiert, sondern immer die Anfrage „TreeListener.getInstance();“ an diese Klasse gesendet. Hier wird überprüft, ob die Instanz bereits existiert, wenn nötig generiert und anschließend zurückgegeben. Das gleiche Prinzip wird bei der Datenbankverbindung verwendet, da auch hier natürlich immer nur einmal eine Verbindung aufgebaut werden darf. Würde man beim TreeListener eine Instanz erstellen, einen ausgewählten Eintrag zwischenspeichern und anschließend in einem anderen Teil des Programms wieder eine andere TreeListener-Instanz erstellen, könnte man nicht auf den gespeicherten Eintrag zugreifen und der Zweck dieser Klasse wäre nicht erfüllt. Der TreeListener arbeitet also sozusagen als globale Variable, auf die beinahe alle Instanzen anderer Klassen zugreifen. public static TreeListener getInstance() { if (instance == null) { instance = new TreeListener(); } return instance; } Der TreeListener beinhaltet im Prinzip nur sechs String-Variablen, in denen die jeweilige Auswahl gespeichert ist. Diese Variablen sind jedoch „private“, können also von außen nicht beeinflusst werden. Daher gibt es für jede Variable eine „set“- und eine „get“-Methode. Bei jeder „set“-Methode wird überprüft, ob die gewünschte Änderung Sinn macht und man schützt somit die zugehörige Variable vor fehlerhaften Einträgen. Die „get“-Methoden liefern nur die gewünschte Variable, haben aber sonst keine weitere Funktion. Hier die „set“und „get“-Methode für die Shop-Variable: Seite 51 Diplomarbeit Lanzanasto, Neumann, Plattner public String getSelectedShop() { return sSelectedShop; } public void setSelectedShop(TreeItem selectedShop) { String sNewSelectedShop = selectedShop.getText(); int iSAP; if ((sNewSelectedShop.indexOf('(')) > 0) { sNewSelectedShop = sNewSelectedShop.substring(0, sNewSelectedShop.indexOf('(')).trim(); if (sNewSelectedShop.length() >= 4 && sNewSelectedShop.length() <= 9) { try { iSAP = Integer.parseInt(sNewSelectedShop); resetSelection(); this.sSelectedShop = sNewSelectedShop; System.out.println("Shop: " + getSelectedShop()); } catch (NumberFormatException e) { this.sSelectedShop = ""; } } else if (sNewSelectedShop.substring(0, 3).equals("999")) { resetSelection(); this.sSelectedShop = sNewSelectedShop; System.out.println("Dummy-Shop: " + getSelectedShop()); } else this.sSelectedShop = ""; } else this.sSelectedShop = ""; } Eine weitere Funktion ist die „resetSelection()“-Methode. Sie schreibt in alle sechs Variablen einen Leerstring und löscht somit die gespeicherte Auswahl. Es sollte nämlich immer nur eine der Variablen einen Eintrag enthalten, da beispielsweise nicht gleichzeitig ein Shop und ein Land ausgewählt sein können. Daher wird bei jeder „set“-Methode vor Erneuerung des Variableneintrags diese Funktion aufgerufen. Es gibt jedoch Ausnahmen wie bei der Suche nach Hardware, die mit einem Shop verlinkt ist. Hier werden nämlich bei der Auswahl die SAP-Nummer des Shops und zusätzlich der Hostname der Hardware für die weiter Verarbeitung gespeichert (siehe 4.5.9 Search). Zusätzlich gibt es noch die ursprüngliche „SelectionListener()“-Funktion. Sie erkennt welcher Eintrag in der Baumstruktur angeklickt wurde und leitet diese Information anschließend an die „set“-Methoden weiter, die dann überprüfen zu welcher Art von Eintrag die übertragene Information passt. Seite 52 Diplomarbeit Lanzanasto, Neumann, Plattner public void SelectionListener(final Tree tree) { tree.addListener(SWT.MouseDown, new Listener() { public void handleEvent(Event event) { Point point = new Point(event.x, event.y); if (tree.getItem(point) != null) { setSelectedRegion(tree.getItem(point)); setSelectedCountry(tree.getItem(point)); setSelectedShop(tree.getItem(point)); } } }); } 4.5.9. Search Bei der Suche wird der Part in einen TabFolder mit zwei Elementen unterteilt. Je nachdem, welches der beiden Elemente ausgewählt wurde, wird entweder nach einem Shop oder nach einer Hardware gesucht. Die beiden Elemente des TabFolders sind im Prinzip genau gleich aufgebaut, es befinden sich jeweils zwei ContentGroups darin, eine für die Eingabe der Suchkriterien, die andere beinhaltet eine Tabelle, in der die Suchergebnisse dargestellt werden. Diese Tabelle ist so programmiert, dass beim Klick auf eine der Zeilen die SAP-Nummer des Shops bzw. die Seriennummer der Hardware zwischengespeichert wird und somit genauere Informationen durch einen Klick auf „Details“-Button angezeigt werden (siehe 4.5.11 Details). Wichtigster Bestandteil der Programmierung des Suchfensters ist natürlich der „Search“-Button beziehungsweise die Funktion, die beim Klick dieses Buttons aufgerufen wird. Hier wird eine SQL-Abfrage mit mehreren WHERE-Bedingungen erstellt, die alle Suchkriterien beinhalten. Bei der Suche nach einem Shop besteht außerdem noch die Möglichkeit nach geschlossenen Shops zu suchen, indem in der CheckBox „Closed Shop“ ein Haken gesetzt wird. Im Prinzip wählt man mit dieser Suchoption lediglich aus, in welcher Tabelle der Datenbank nach Datensätzen gesucht werden soll, da mit der Datenbankstruktur getrennte Tabellen für geöffnete und geschlossene Shops verwendet werden. Im Anschluss wird das Suchergebnis entweder in einer Objektliste der Klasse „Shop“ oder der Klasse „ClosedShops“ gespeichert, je nachdem, welche Art der Seite 53 Diplomarbeit Lanzanasto, Neumann, Plattner Suche ausgewählt wurde. Diese Unterteilung zieht sich die weiteren Schritte immer weiter hindurch, da die beiden Tabellen der Datenbank nicht genau gleich sind (siehe 3.3 Datenbankübersicht). if (btnCheckClosedShop.getSelection() == false) { sSQLStatement = "SELECT Shops.sap_ship_to, Shops.country_code, Shops.type, ... sSQLStatement = sSQLStatement + "FROM shopinfo.country Country, shopinfo.shops Shops "; } else if (btnCheckClosedShop.getSelection() == true) { sSQLStatement = "SELECT Shops.auto_id, Shops.sap_ship_to, Shops.country_code, ... sSQLStatement = sSQLStatement + "FROM shopinfo.country Country, shopinfo.closed_shops Shops "; } sSQLStatement sSQLStatement "; sSQLStatement sSQLStatement "%' "; sSQLStatement = sSQLStatement + "WHERE Country.country_code = Shops.country_code "; = sSQLStatement + "AND Shops.sap_ship_to LIKE '%" + textShopShipTo.getText() + "%' = sSQLStatement + "AND Shops.type LIKE '%" + textShopType.getText() + "%' "; = sSQLStatement + "AND Shops.store_name LIKE '%" + textShopShopname.getText() + = sSQLStatement + "AND (Shops.country_code LIKE '%" + textShopCountry.getText() + "%' OR Country.country LIKE '%" + textShopCountry.getText() + "%') "; sSQLStatement = sSQLStatement + "AND (Shops.location LIKE '%" + textShopLocation.getText() + "%' OR Shops.city LIKE '%" + textShopLocation.getText() + "%') "; sSQLStatement = sSQLStatement + "AND Shops.zip_code LIKE '%" + textShopZIP.getText() + "%' "; sSQLStatement = sSQLStatement + "AND Shops.phone LIKE '%" + textShopPhone.getText() + "%' "; sSQLStatement = sSQLStatement + "LIMIT 10000"; if (btnCheckClosedShop.getSelection() == false) searchResult = db_con.getShops(sSQLStatement); else if (btnCheckClosedShop.getSelection() == true) searchResultClosed = db_con.getClosedShops(sSQLStatement); Danach werden sämtliche bereits vorhandenen Einträge, die nach der vorhergehenden Suche in der Ergebnistabelle angezeigt werden, entfernt. Sollte die Suche mit den eingegebenen Kriterien keine Ergebnisse liefern, wird ein Popup-Fenster am Bildschirm angezeigt, welches den Benutzer auf dieses Problem hinweist. if (searchResult != null) { if (searchResult.size() == 0) { System.out.println("No shop was found!"); MessageBox dialogSQLError = new MessageBox(shell, SWT.ICON_WARNING | SWT.OK); dialogSQLError.setText("No result!"); dialogSQLError.setMessage("No shop was found with this parameters!"); returnCode = dialogSQLError.open(); } else { System.out.println(searchResult.get(0).getSapShipTo()); } } Seite 54 Diplomarbeit Lanzanasto, Neumann, Plattner Bei der Suche nach Hardware sieht die prinzipielle Suchfunktion ähnlich aus, wie die der Shop-Suche. Es werden auch hier alle Bedingungen in ein SQL-Abfrage verpackt. Aus Sicht des Benutzers fungiert auch die CheckBox zur Suche nach entfernter Hardware nach dem gleichen Prinzip wie bei der Suche nach geschlossenen Shops. Programmintern wird diese Suchbedingung aber etwas anders abgehandelt, da bei der Hardware nur eine Tabelle in der Datenbank vorhanden ist, in der alle Hardware-Datensätze gespeichert sind. Hier wird diese Auswahloption zur eigentlichen Suche in der Datenbank ignoriert, es werden also alle Hardware-Datensätze, egal ob entfernt oder nicht, in einer Objektliste der Klasse „Hardware“ gespeichert. Anschließend werden alle Datensätze in einer for-each-Schleife in die Tabelle eingetragen, dabei wird jedoch bei jedem Datensatz vorher mit einer SQL-Abfrage (COUNT-Funktion), wie sie auch beim Erstellen der Statistiken verwendet wird (siehe 4.5.10 Statistic), überprüft, wie oft die jeweilige Hardware mit einem Shop verlinkt ist. Diese Abfrage gibt entweder den Wert 1, die Hardware wird in einem Shop verwendet, oder den Wert 0, die Hardware ist in keinem Shop vorhanden (also entfernt), zurück. Anschließend wird mit einer IF-Abfrage entschieden, ob der Datensatz in der Ergebnistabelle angezeigt wird, indem überprüft wird, ob die Hardware der gewünschten Suchbedingung entspricht. Diese kleine Abfrage muss für jeden Datensatz, der bei der ursprünglichen SQLAbfrage zurückgegeben wurde, wiederholt werden. Dadurch dauert die Suche bei ungenauen Angaben der Kriterien etwas länger, was dem Benutzer jedoch nicht zu sehr auffallen wird. Seite 55 Diplomarbeit Lanzanasto, Neumann, Plattner if (searchHWResult != null) { for (Hardware entry : searchHWResult) { sSQLHWStatement sSQLHWStatement sSQLHWStatement sSQLHWStatement = = = = "SELECT COUNT(sap_ship_to) FROM shopinfo.shops "; sSQLHWStatement + "WHERE sap_ship_to='"; sSQLHWStatement + entry.getShipTo(); sSQLHWStatement + "'"; if (((btnCheckRemoved.getSelection() == false) && (Integer.parseInt(db_con.getSingleCount(sSQLHWStatement)) == 1)) || ((btnCheckRemoved.getSelection() == true) && (Integer.parseInt(db_con.getSingleCount(sSQLHWStatement)) == 0))) { bNoResult = false; TableItem item = new TableItem(tableHWResult, SWT.NONE); item.setText(0, entry.getSn()); item.setText(1, entry.getShipTo()); item.setText(2, entry.getHostname()); item.setText(3, entry.getHw()); item.setText(4, entry.getStoreType()); item.setText(5, entry.getOsVersion()); } } } Auch bei dieser Suche wird wieder ein Popup-Fenster angezeigt, falls keine Suchergebnisse vorliegen. Da jedoch nicht alle Ergebnisse der eigentlichen SQLAbfrage angezeigt werden, kann hier nicht die Objektliste zur Speicherung auf „null“ abgefragt werden, sondern es muss eine eigene bool’sche Variable „bNoResult“ generiert werden, die kennzeichnet, ob die Suche erfolgreich war. Diese Variable ist standardmäßig auf „true“, also kein Ergebnis, und wird, sobald der erste Datensatz in die Ergebnistabelle eingetragen wird, auf „false“ gesetzt. Eine Besonderheit bei der Suche von Hardware ist die Auswahl des gewünschten Datensatzes zur Anzeige der Details. Beim Klick auf eine Zeile der Ergebnistabelle wird überprüft, ob die ausgewählte Hardware mit einem Shop verlinkt ist. Wenn dies der Fall ist, wird nicht die Seriennummer der Hardware, sondern die SAP-Nummer des Shops in Verbindung mit dem Hostnamen der Hardware im TreeListener gespeichert. So kann später bei der Anzeige der Details der Shop dargestellt werden und zugleich die gesuchte Hardware im TabFolder ausgewählt werden (siehe 4.5.11 Details). Am Ende der „Search“-Klasse wird noch die Selection des TabFolders auf „0“ gesetzt, damit schon beim Öffnen des Parts die Shop-Suche angezeigt wird und nicht nur ein leerer Bildschirm. Seite 56 Diplomarbeit 4.5.10. Lanzanasto, Neumann, Plattner Statistics Zuerst wird im Stastistics-Part ein „TabFolder“ für die einzelnen Regionen erstellt. Darin werden anschließend Tabellen eingefügt. Hier werden bereits alle Spalten fertig beschriftet und angepasst. Nun startet die eigentliche Auswertung der Daten. Zur Zwischenspeicherung wird für jede Spalte eine Variable erstellt, zusätzlich werden Felder definiert, in denen die zusammengehörigen Daten der einzelnen Länder immer zusammengezählt werden, um am Ende die Gesamtwerte für die Regionen ausgeben zu können. Anschließend werden in einer Schleife die drei Kontinente abgearbeitet. Dabei werden zu Beginn die Daten aller Länder eines Kontinents eingelesen. Wichtig sind hier im Prinzip nur die Country-Codes, da mit Hilfe dieser die richtigen Shops aus der „shops“-Tabelle herausgeholt werden. Mit diesem Ergebnis wird nun eine „for-each“-Schleife für alle Länder des Kontinents durchgeführt. sSQLStatement = "SELECT * FROM shopinfo.country WHERE region='"; sSQLStatement = sSQLStatement + sRegion; sSQLStatement = sSQLStatement + "'"; searchResultCountries = db_con.getCountries(sSQLStatement); Wichtigster Bestandteil des Programms ist für die Erstellung der Statistiken die Funktion „getSingleCount()“. Dieser Funktion wird immer eine SQL-Query übergeben, die eine einzige „Count“-Abfrage enthält. In diesem Fall enthält die Antwort der Datenbank nur einen Wert, welcher von der Funktion als String zurückgegeben wird, anschließend wird dieser String jedoch immer in einen Integer-Wert umgewandelt. Genaueres über diese Funktion ist bei der Datenbankverbindung zu finden. (siehe 4.5.4 Verbindung zur DB) Diese Funktion wird immer wieder ausgeführt. Das Einzige, das sich ändert, ist bei der Abarbeitung eines Landes der Typ der Shops, die gezählt werden sollen. Hier sieht man die Code-Zeilen zum Zählen der AW-Shops: Seite 57 Diplomarbeit Lanzanasto, Neumann, Plattner sSQLStatement = "SELECT COUNT(country_code) FROM shopinfo.shops WHERE country_code='"; sSQLStatement = sSQLStatement + entry.getCountryCode(); sSQLStatement = sSQLStatement + "' AND type='AW'"; iCountAW = Integer.parseInt(db_con.getSingleCount(sSQLStatement)); if (sRegion.equals("EUROPE")) iCountEurope[0] += iCountAW; if (sRegion.equals("AMERICAS")) iCountAmericas[0] += iCountAW; if (sRegion.equals("ASIA PACIFIC")) iCountAsia[0] += iCountAW; Sobald alle Shops des Landes durchgezählt wurden, wird eine Tabellenzeile in der passenden Tabelle angelegt und mit den in den Variablen zwischengespeicherten Werten gefüllt. Wurde der Vorgang für alle Länder und auch alle Kontinente beendet, müssen nun noch die Statistiken für die Regionen in die Tabelle eingetragen werden. Die Werte wurden für die Kontinente bei jedem Land aufsummiert und sind in den Variablen-Feldern gespeichert. Diese müssen nur noch addiert werden, um auch die Gesamtwerte der ganzen Welt anzeigen zu können. for (iLoop = 0; iLoop < 13; iLoop++) { iCountWorld[iLoop] = iCountEurope[iLoop] + iCountAmericas[iLoop] + iCountAsia[iLoop]; } Um diese Statistik nach dieser Methode zu erstellen, wird ca. eine Sekunde benötigt. Bei einem ersten Versuch ließen wir alle Daten der Shops einlesen und wollten die Shops eines Typs innerhalb des Java-Programms zählen lassen. Diese Methode funktionierte zwar auch, es brauchte aber um ein Vielfaches mehr Zeit, die beim normalen Gebrauch des Programms nicht zumutbar gewesen wäre, deshalb entschieden wir uns die Shops direkt von der Datenbank zählen zu lassen. Seite 58 Diplomarbeit 4.5.11. Lanzanasto, Neumann, Plattner Details Bei einem Klick auf den „Details“-Button im Menü wird ein Part der Klasse „ShopDetails“ geöffnet. Dabei wird als erster Schritt die Funktion „showDetails“ ausgeführt. Im Prinzip wird mit dieser Funktion lediglich herausgesucht, welche Details welchen Country’s, welchen Shops bzw. welcher Hardware angezeigt werden sollen. Dazu wird das TreeListener-Objekt importiert, in der die Auswahl immer zwischengespeichert wird (siehe 4.5.8 TreeListener). Je nachdem, welcher Eintrag des TreeListeners ein Ergebnis liefert, wird die passende Funktion zur Anzeige aufgerufen und dabei unter anderem auch der TreeListener übergeben, um in der Funktion den passenden Datensatz aus der Datenbank herauszusuchen. TreeListener treeListener = TreeListener.getInstance(); if (treeListener.getSelectedShop().equals("") == false) { showShopDetails(parent, treeListener); } else if (treeListener.getSelectedClosedShop().equals("") == false) { showClosedShopDetails(parent, treeListener); } else if (treeListener.getSelectedHardware().equals("") == false) { showHardwareDetails(parent, treeListener); } else if (treeListener.getSelectedCountry().equals("") == false) { showCountryDetails(parent, treeListener); } 4.5.11.1. showShopDetails() Diese Funktion wird erst aufgerufen, wenn die Funktion „treeListener.getSelectedShop()“ eine SAP-Nummer zurückgibt. Hier wird zu Beginn eine Verbindung zur Datenbank hergestellt und alle Shops mit der übergebenen SAP-Nummer werden eingelesen und in einer Liste gespeichert. Da die Shops mittels SAP-Nummer ausgefiltert werden und die SAP-Nummer in der Datenbank als Primary Key verwendet wird, dürfte jedoch maximal nur ein Shop gefunden werden. Aus Einfachheitsgründen wird hier aber die gleiche Funktion angewandt, wie wenn nach mehreren Shops gesucht werden würde. Da also nur Seite 59 Diplomarbeit Lanzanasto, Neumann, Plattner ein Eintrag in der Liste vorhanden ist, wird beim Eintragen in die TextBoxes mit der „get(0)“-Funktion immer der erste Eintrag verwendet. Im Anschluss wird für jedes Detail des Shops jeweils ein Label, das die Beschreibung anzeigt, und, wie schon erwähnt, eine TextBox, die mit dem passenden Detail befüllt wird, innerhalb einer ContentGroup generiert. Der „editable“-Status der TextBoxes wird dabei auf „false“ gesetzt, somit kann der Inhalt nicht verändert, aber trotzdem markiert und kopiert werden. List<Shop> shop; shop = db_con.getShops(treeListener); Label labelShopname = new Label(contentGroupShop, SWT.NONE); labelShopname.setText("Store Name: "); final Text textShopname = new Text(contentGroupShop, SWT.SINGLE); textShopname.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); textShopname.setText(shop.get(0).getStoreName()); textShopname.setEditable(false); Label labelSAP = new Label(contentGroupShop, SWT.NONE); labelSAP.setText("SAP - Ship to: "); final Text textSAP = new Text(contentGroupShop, SWT.SINGLE); textSAP.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); textSAP.setText(shop.get(0).getSapShipTo()); textSAP.setEditable(false); . . . Am unteren Ende der Gruppe befinden sich zwei Buttons, einer zum Verändern der Details und einer zum Schließen des Shops. Noch eine Zeile weiter unten befindet sich eine unsichtbare TextBox, in der die derzeitige SAP-Nummer des Shops noch einmal hinterlegt ist, diese versteckte Information ist für den Benutzer nicht zu sehen und wird beim Editieren von Shop-Informationen benötigt. Seite 60 Diplomarbeit Lanzanasto, Neumann, Plattner Editieren Beim Klick auf den „Edit“-Button wird der „editable“-Status aller TextBoxes auf „true“ gesetzt. Ausnahmen bilden dabei die Informationen „Country“ und „VG-Nr.“. Diese beiden Informationen kommen nicht aus der „shops“-Tabelle der Datenbank, sondern sind Informationen, die über den Country-Code aus der „country“-Tabelle ausgelesen werden. Diese beiden Einträge können also nur in Verbindung mit dem Country-Code verändert werden. Außerdem wird der Hintergrund der Einträge „Store Name“, „SAP – Ship to“, „Type“ und „Country Code“ gelb eingefärbt, da es sich hierbei um Pflichtfelder handelt. Weiters wird der „Edit“-Button in einen „Save“-Button umgewandelt, indem die Beschriftung des Buttons verändert wird. Daher wird auch bei jedem Klick auf den Button mittels If-Abfrage auf die Buttonbeschriftung ausgewählt, welche Funktionen ausgeführt werden sollen. buttonEditSave.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { . . . if (buttonEditSave.getText().equals("Edit")) { textShopname.setEditable(true); textShopname.setBackground(ColorLightYellow); textSAP.setEditable(true); textSAP.setBackground(ColorLightYellow); . . . } else if (buttonEditSave.getText().equals("Save")) { . . . } } }); Nachdem der Benutzer die gewünschten Daten geändert hat und den „Save“Button geklickt hat, werden manche der neuen Einträge überprüft, um Fehler in der Datenbank zu verhindern. Überprüft werden dabei die gelb markierten Pflichtfelder und alle Datumsfelder. Bei den Feldern „Store Name“ und „Type“ wird lediglich überprüft, ob ein Eintrag in dem Feld vorhanden ist. Beim Eintrag Seite 61 Diplomarbeit Lanzanasto, Neumann, Plattner „SAP – Ship to“ wird überprüft, ob die eingetragene SAP-Nummer bereits in der Datenbank vorhanden ist. Falls dieser Eintrag beim Editieren jedoch nicht verändert wurde, befindet sich klarerweise bereits ein Datensatz mit diesem Primary Key in der Datenbank, hier kommt das versteckte „Current SAP“-Textfeld zum Einsatz. Falls die beiden Einträge übereinstimmen, liegt hier natürlich kein ungültiger Eintrag vor. Beim Eintrag „Country Code“ muss überprüft werden, ob der angegebene Code auch wirklich ein Eintrag in der „country“-Tabelle ist, damit eine gültige Verknüpfung der Tabellen gewährleistet werden kann. // Check "SAP-Ship to" sSQLStatement = "SELECT COUNT(sap_ship_to) FROM shopinfo.shops WHERE sap_ship_to='"; sSQLStatement = sSQLStatement + textSAP.getText(); sSQLStatement = sSQLStatement + "'"; if (((Integer.parseInt(db_con.getSingleCount(sSQLStatement)) > 0) && (textCurrentSAP.getText().equals(textSAP.getText()) == false)) || (textSAP.getText().equals(""))) { bFailed = true; textSAP.setBackground(ColorLightRed); } else { textSAP.setBackground(ColorLightYellow); } . . . // Check "Country-Code" sSQLStatement = "SELECT COUNT(country_code) FROM shopinfo.country WHERE country_code='"; sSQLStatement = sSQLStatement + textCountryCode.getText(); sSQLStatement = sSQLStatement + "'"; if ((Integer.parseInt(db_con.getSingleCount(sSQLStatement)) != 1) || (textCountryCode.getText().equals(""))) { bFailed = true; textCountryCode.setBackground(ColorLightRed); } else { textCountryCode.setBackground(ColorLightYellow); } Da in den einzelnen Tabellen des Öfteren Datumseinträge vorkommen, wurde für diese Überprüfung eine eigene Funktion angelegt, die „isDateFormatCorrect()“Funktion. Hier wird der Datumswert als String übergeben und eine bool’sche Variable zurückgegeben. Innerhalb der Funktion wird der übergebene String in die Anteile für Jahr, Monat und Tag zerlegt. Diese Werte müssen durch einen Bindestrich getrennt sein. Anschließend wird überprüft, ob die Einträge plausibel sind, wobei die Werte nicht genau überprüft werden. Beispielsweise würde die Seite 62 Diplomarbeit Lanzanasto, Neumann, Plattner Funktion den Eintrag „2013-02-30“ als gültig einstufen, obwohl dieses Datum natürlich nicht korrekt wäre. boolean isDateFormatCorrect(String sDate) { boolean bCorrect = true; String sYear; String sMonth; String sDay; int iYear; int iMonth; int iDay; if (sDate.length() != 10) bCorrect = false; if (bCorrect) { sYear = sDate.substring(0, 4); sMonth = sDate.substring(5, 7); sDay = sDate.substring(8, 10); if (sDate.substring(4, 5).equals("-") == false) bCorrect = false; if (sDate.substring(7, 8).equals("-") == false) bCorrect = false; try { iYear = Integer.valueOf(sYear).intValue(); iMonth = Integer.valueOf(sMonth).intValue(); iDay = Integer.valueOf(sDay).intValue(); if (iYear < 1 || iYear > 9999) bCorrect = false; if (iMonth < 1 || iMonth > 12) bCorrect = false; if (iDay < 1 || iDay > 31) bCorrect = false; } catch (NumberFormatException e1) { bCorrect = false; } } return bCorrect; } Sollten bei den überprüften Einträgen ein oder mehrere ungültige entdeckt worden sein, werden die entsprechenden TextBoxes rot hinterlegt und ein PopupFenster wird geöffnet, welches den User auf diese Felder hinweist. Falls jedoch alle Einträge als korrekt anerkannt wurden, wird versucht, die nun generierte UPDATE-Abfrage auf der Datenbank auszuführen. Sollte hierbei ein, trotz der Überprüfungen, Fehler auftreten, wird ein Popup-Fenster geöffnet, das die Fehlernachricht der Datenbank anzeigt. Solche Fehler können beispielsweise ein Seite 63 Diplomarbeit Lanzanasto, Neumann, Plattner Datumswert sein oder ein zu langer Eintrag. Diese Einträge können aber vom Programm leider nicht gekennzeichnet werden, sie müssen also vom Benutzer selbst gefunden werden, was mit Hilfe der Error-Message jedoch kein Problem darstellen sollte. Nun wird noch überprüft, ob die SAP-Nummer des Shops verändert wurde. Ist dies der Fall, muss auch die SAP-Nummer der verknüpften HardwareKomponenten geändert werden, um die Verknüpfung nicht zu verlieren. Sollten all diese Prozesse ohne Fehler abgelaufen sein, werden alle TextBoxes wieder auf den ursprünglichen Status zurückgesetzt und der „Save“-Button wird wieder zum „Edit“-Button umgewandelt. Außerdem muss für die nächste Änderung der Eintrag für die „CurrentSAP“ aktualisiert werden. Shop schließen Der „Close this shop“-Button ermöglicht es dem Benutzer einen Shop zu schließen. Aus Sicht der Datenbank wird der Shop nicht gelöscht, sondern der Datensatz lediglich von der „shops“-Tabelle in die „closed_shops“-Tabelle übertragen. Beim Klick auf diesen Button wird zuerst ein Popup-Fenster geöffnet, in dem der User seine Absicht den Shop zu schließen bestätigen muss. Als Erstes wird überprüft, ob der gerade betrachtete Shop wirklich in der „shops“Tabelle gespeichert ist und nicht schon vorher geschlossen wurde. Anschließend werden die Daten in die „closed_shops“-Tabelle eingetragen, das sollte unbedingt geschehen, bevor der Shop aus der „shops“-Tabelle entfernt wird, damit es bei einem Fehler zu keinem Datenverlust kommt. Danach wird der Shop aus der „shops“-Tabelle gelöscht. Sollte während der Übertragung ein Fehler auftreten, wird ein Popupfenster geöffnet, welches Informationen über den Zeitpunkt des Fehlers enthält und die Fehlermessage der Datenbank wiedergibt. Seite 64 Diplomarbeit Lanzanasto, Neumann, Plattner if (bFailed == false) { // Check if shop is already closed sSQLStatement = "SELECT COUNT(sap_ship_to) FROM shopinfo.shops "; sSQLStatement = sSQLStatement + "WHERE sap_ship_to='"; sSQLStatement = sSQLStatement + textSAP.getText(); sSQLStatement = sSQLStatement + "'"; if ((Integer.parseInt(db_con.getSingleCount(sSQLStatement)) == 0)) {...} } if (bFailed == false) { // Insert shop into closed_shop - table sSQLStatement = "INSERT INTO shopinfo.closed_shops "; sSQLStatement += "(sap_ship_to, country_code,...) VALUES ('"; sSQLStatement += textSAP.getText(); sSQLStatement += "', '"; sSQLStatement += textCountryCode.getText(); sSQLStatement += "', '"; . . . try { db_con.editDatabaseWithException(sSQLStatement); } catch (SQLException e2) {...} // Delete shop from shops - table if (bFailed == false) { sSQLStatement = "DELETE FROM shopinfo.shops WHERE sap_ship_to=\""; sSQLStatement += textSAP.getText(); sSQLStatement += "\""; try { db_con.editDatabaseWithException(sSQLStatement); } catch (SQLException e2) {...} } } Bei der Darstellung der Shop-Details werden auf der rechten Seite der Arbeitsfläche in einem TabFolder die Details der verlinkten Hardware angezeigt. Dabei stellt jeder Tab des Folders eine Hardewarekomponente dar, die einzelnen Datensätze werden dabei mittels SAP-Nummer aus der „hardware“-Tabelle der Datenbank herausgefiltert. Der Inhalt eines solchen Tabs wird im Prinzip gleich generiert, wie wenn nur eine Hardwarekomponente einzeln dargestellt wird (siehe 4.5.11.3 showHardwareDetails()). Abschließend wird nun noch die Selection des Tabfolders, in dem die Hardware dargestellt wird, ausgewählt. Bei normaler Auswahl des Shops wird einfach das erste Element des Folders angezeigt. Wurde jedoch eine spezielle Hardware über die Suche ausgewählt (siehe 4.5.9 Search), welche mit einem Shop verknüpft ist, Seite 65 Diplomarbeit Lanzanasto, Neumann, Plattner wird jener Tab gesucht, welcher der gesuchten Hardware entspricht und ausgewählt. int iIndex = 0; int iLoop; for (iLoop = 0; iLoop < folder.getItemCount(); iLoop++) { if (folder.getItem(iLoop).getText(). equals(treeListener.getSelectedHWHostname())) { iIndex = iLoop; } } folder.setSelection(iIndex); 4.5.11.2. showClosedShopDetails() Diese Funktion wird nur dann ausgeführt, wenn die Details eines geschlossenen Shops angezeigt werden sollen. Das wiederum kann nur dann sein, wenn ein solcher Shop mit Hilfe der Suchfunktion ausgewählt wurde. Die Funktion an sich ähnelt sehr der Funktion „showShopDetails()“, es gibt nur wenige kleine Unterschiede. Zum einen befindet sich eine Zeile mehr in der Arbeitsfläche, nämlich die für die automatisch vergebene ID. Diese Nummer ist aber auch nach Klicken des „Edit“Buttons nicht veränderbar, da es erstens nicht notwendig ist und zweitens die Handhabung einer UPDATE-Abfrage um einiges vereinfacht wird. Ein weiterer Unterschied ist der zweite Button neben dem „Edit“-Button, der „ReOpen this Shop“-Button. Diese Schaltfläche bildet das Gegenstück zum „Close this Shop“-Button aus der „showShopDetails()“-Funktion. Hiermit kann ein geschlossener Shop wieder geöffnet werden, indem der Datensatz von der „closed_shops“-Tabelle der Datenbank in die „shops“-Tabelle verschoben wird. Der dritte und letzte Unterschied ist, dass in der Anzeige immer nur die Details eines Shops dargestellt werden und nie die dazugehörigen Hardwarekomponenten, da Hardware-Datensätze nicht mit einem geschlossenen Shop verlinkt werden können. Ansonsten funktioniert die Darstellung und Veränderung von Einträgen gleich wie bei einem normalen Shop. Seite 66 Diplomarbeit Lanzanasto, Neumann, Plattner 4.5.11.3. showHardwareDetails() Diese Funktion wird immer dann ausgeführt, wenn eine Hardwarekomponente einzeln dargestellt werden soll. Das passiert aber nur dann, wenn über das Suchmenü ein Datensatz ausgewählt wurde, der mit keinem Shop verlinkt ist. Falls eine Verknüpfung vorliegt, wird immer der gesamte Shop inklusive aller Hardwarekomponenten angezeigt. Da aber die einzelnen Tabs in der ShopDarstellung für die Hardwaredetails gleich generiert werden wie in dieser Funktion, wird diese Funktion nun auch genauer erklärt. Die Darstellung der einzelnen Details findet ähnlich wie bei der Anzeige der Shop-Details statt. Für eine bessere Übersicht werden TextBoxes eingefügt, deren „enable“-Status auf „false“ gesetzt wird, die eine Unterteilung in allgemeine Informationen, Software-spezifische Details und bisherige Standorte andeuten. Auch bei dieser Darstellung wurde wieder eine TextBox versteckt, die die derzeitige Seriennummer speichert. Hier befindet sich die TextBox direkt neben der „General“-Überschrift. Diese Nummer wird wie schon bei den Shops für Veränderungen der Details benötigt. Der „Edit“-Button funktioniert auch gleich wie bei den Shops, einziger kleiner Unterschied ist, dass manche Überprüfungen der Einträge nach einem etwas anderen Muster ablaufen. Bei der Seriennummer wird wieder überprüft, ob diese eindeutig ist, da es sich hierbei um den Primary Key in der Datenbank-Tabelle handelt, beim Hostnamen muss lediglich ein Eintrag vorhanden sein. Der Eintrag im Feld „Ship to“ muss entweder auf einen geöffneten Shop verweisen oder komplett leer sein. Ein leeres Feld gibt an, dass diese Hardware zu keinem Shop verlinkt ist. Auf Grund dieser Überprüfung kann es jedoch passieren, dass es Hardwarekomponenten gibt, die zwar nach dem Importieren aus der Excel-Tabelle eine SAP-Nummer eingetragen haben, diese aber zu keinem gültigen Shop eine Verknüpfung darstellt. Ein solcher Datensatz wird in der Datenbank zwar akzeptiert, kann aber nur verändert werden, wenn die eingetragene SAP-Nummer heraus gelöscht wird oder zu einer gültigen Nummer geändert wird. Seite 67 Diplomarbeit Lanzanasto, Neumann, Plattner 4.5.11.4. showCountryDetails() Die vierte und letzte „showDetails“-Funktion ist den vorhergehenden ziemlich ähnlich. Diese Funktion wird immer dann ausgeführt, wenn die Details eines Landes angezeigt werden sollen. Ein Land kann ausgewählt werden, indem man entweder links in der Baumstruktur auf die entsprechende Verzweigung klickt, oder indem man in der Gesamttabelle für die Länder eines auswählt. Die Anzeige der Details funktioniert im Prinzip gleich wie bei den anderen Funktionen. Um die Details veränderbar zu machen, wird wieder ein „Edit“-Button angezeigt. Eine kleine Besonderheit ist hier, dass im Feld „Region“ kein beliebiger Eintrag eingefügt werden kann. Hier wird also keine TextBox verwendet, sondern eine sogenannte ComboBox. Sie hat mehrere verschiedene Auswahl- möglichkeiten, die programmtechnisch festgelegt werden, in unserem Fall kann via „DropDown“-Funktion zwischen „Europe“, „Americas“ und „Asia Pacific“ gewählt werden. Label labelRegion = new Label(contentGroupMain, SWT.NONE); labelRegion.setText("Region: "); final Combo comboRegion = new Combo(contentGroupMain, SWT.READ_ONLY); comboRegion.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); comboRegion.setItems(new String[] { "Europe", "Americas", "Asia Pacific" }); comboRegion.setText(country.get(0).getRegion()); comboRegion.setEnabled(false); Sonstige Maßnahmen zur Vorbeugung von fehlerhaften Einträgen werden bei den Feldern „Country-Code“, „Country“ und „VG-Nummer“ getroffen, wobei hier nur überprüft wird, ob ein Eintrag vorhanden ist bzw. ob dieser eine vorgegebene Länge einhält. Um Änderungen des „Country-Code“-Eintrags zu ermöglichen, wird wieder in einer versteckten TextBox der derzeitige Country-Code zwischengespeichert, da dieser den Primary Key in der Datenbanktabelle bildet, gleich wie die SAPNummer bei Shops bzw. die Seriennummer bei Hardware-Datensätzen. Seite 68 Diplomarbeit 4.5.12. Lanzanasto, Neumann, Plattner Add Beim Erschaffen einer Instanz dieser Klasse wird ein TabFolder im Part platziert. Über diese Tabs kann ausgewählt werden, welche Art von neuem Eintrag gemacht werden soll. Es kann zwischen Shop, Hardware und Country gewählt werden. In allen drei Tabs wird, gleich wie bei der Anzeige von Details, eine ContentGroup erstellt, in der sich sämtliche TextBoxes für die einzelnen Einträge befinden. Alle diese TextBoxes sind standardmäßig mit einem Leerstring gefüllt, außer jene, in denen ein Datumswert eingetragen werden soll. In diesen Feldern wird als Default-Wert „0001-01-01“ verwendet, um dem User auch gleich die richtige Datumsformatierung anzuzeigen. Um das richtige Verknüpfen von Datensätzen für den User zu erleichtern, werden die relevanten Einträge automatisch ausgefüllt. Dieser Eintrag wird aus der zurzeit getätigten Auswahl im treeListener abgeleitet. Falls der User beispielsweise ein Land ausgewählt hat, wird beim Klick auf den „Add“-Button im neu geöffneten Part sofort der „Add Shop“-Tab ausgewählt und der passende „Country-Code“ für des ausgewählte Land eingetragen. Diese Funktion ist vor allem beim Hinzufügen von Hardware sehr hilfreich, da der User so nicht die meist siebenstellige SAP-Nummer des dazugehörigen Shops eintragen muss. So wird fehlerhaften Verknüpfungen vorgebeugt. if (treeListener.getSelectedCountry() != "") folder.setSelection(itemShop); else if (treeListener.getSelectedShop() != "") folder.setSelection(itemHW); else if (treeListener.getSelectedRegion() != "") folder.setSelection(itemCountry); Falls bei der Eingabe Felder trotzdem falsch befüllt werden, werden diese Fehler beim Klick auf den „Save“-Button erkannt und gleich wie beim Editieren in der Detail-Ansicht farblich markiert. Es werden hier die gleichen Felder überprüft wie beim Editieren von Einträgen. Seite 69 Diplomarbeit Lanzanasto, Neumann, Plattner 5. Manual/Bedienungsanleitung In diesem Kapitel wird das User Interface Punkt für Punkt beschrieben und erklärt, wie man es bedient und zu einem gewünschten Ergebnis kommt. 5.1. Oberfläche Startet man das Programm, so erscheint folgende Oberfläche (hier mit gekennzeichneten Flächen): Menü Arbeitsfläche Abb. 10: Oberflächen Übersicht Tree Seite 70 Diplomarbeit 5.2. Lanzanasto, Neumann, Plattner Menü Abb. 11: Menü Das Menü befindet sich in der oberen linken Ecke und ist dort immer zu sehen. Die Icons öffnen entweder einen neuen Tab in der Arbeitsfläche oder ein kleines Dialogfenster. Die Funktion der Icons von links nach rechts beschrieben: Suche: Es öffnet sich der Suche-Tab. Weitere Informationen bei 5.5 Suche. Neues Element hinzufügen: Es öffnet sich ein neuer Tab zur Erstellung eines neuen Elements. Weitere Informationen bei 5.6 Neues Element hinzufügen. Statistiken: Es öffnet sich ein neuer Tab mit diversen Statistiken. Weitere Informationen bei 5.7 Statistiken. Details aufrufen: Es öffnet sich ein neuer Tab mit den Details des ausgewählten Objekts. Weiter Informationen bei 5.8 Details. Über: Es öffnet sich eine Messagebox mit Informationen über das Programm. Beenden: Es wird eine „Beenden“-Dialogbox aufgerufen. Seite 71 Diplomarbeit 5.3. Lanzanasto, Neumann, Plattner Tree Der Tree soll einen schnellen Überblick und Zugriff auf alle Inhalte der Datenbank liefern. Mit den zwei Tabs am oberen Rand des Trees kann ausgewählt werden, ob bestehende oder aufgelöste Shops angezeigt werden sollen. Das Textfeld darunter bietet einen Quick search, mit dem man sofort einen bestimmten Shop über die SAPNummer auswählen kann. Der Tree an sich besteht aus fünf Ebenen. Abb. 12: Tree Die Ebenen sind folgende: Kontinent – Land – Shoptyp – Shop – Hardware. So kann man sich intuitiv und hierarchisch von oben herab zum gewünschten Shop klicken und dort sofort auch die Hardware einsehen. 5.4. Arbeitsfläche In der Arbeitsfläche werden mittels Tabs die gewünschten Anfragen angezeigt. Beim Programmstart sind drei nicht schließbare Tabs standardmäßig geöffnet: Es werden die Tabs Country, Shop und Closed Shop mit deren Informationen jeweils in Tabellenform angezeigt. Die Arbeitsfläche ist der einzige Bereich, in dem der Benutzer Änderungen vornehmen kann, wie zum Beispiel Einträge von der Datenbank editieren. Jede Aktion in der Toolbar öffnet einen neuen Tab in der Arbeitsfläche, der auch wieder geschlossen werden kann. Seite 72 Diplomarbeit 5.5. Lanzanasto, Neumann, Plattner Suche Mit der Suche kann man nach speziellen Elementen in der Datenbank suchen, auch wenn man nicht ganz genau weiß, was man sucht. Es kann nicht nur nach einem Element gesucht werden, sondern auch nach mehreren. Um die Suche aufzurufen, drückt man den „Suche“-Button im Menü (siehe 5.2 Menü). Es öffnet sich dann folgender Tab: Abb. 13 Suche Mit den zwei Tabs in der linken oberen Ecke kann ausgewählt werden, ob man nach Shops oder Hardware suchen will. Darunter befinden sich einige Suchparameter, die eingegeben werden können, um die Suche zu spezialisieren. Es müssen keine vollständigen Suchparameter sein, es können auch nur Wortphrasen eingegeben werden. Wird in mehreren Feldern etwas eingetragen, so liefert das Suchergebnis nur Elemente, die allen Suchparametern entsprechen. Ist die Checkbox „Closed Shop“ aktiviert, so wird nur nach aufgelösten Shops gesucht. Seite 73 Diplomarbeit 5.6. Lanzanasto, Neumann, Plattner Neues Element hinzufügen Abb. 14: Hinzufügen einer Hardware Da das Unternehmen nicht schläft und sich im Bereich der Shops und Hardware immer etwas ändert, kann man mit diesem „Wizard“ ein neues Land, einen neuen Shop oder eine neue Hardware hinzufügen. Mit den drei Tabs am oberen Rand kann ausgewählt werden, ob nun ein Shop, eine Hardware oder ein Land hinzugefügt wird. Es erscheint dann ein Formular, in das alle Informationen eingetragen werden können, die das Element betreffen. Die gelben Felder zeigen Pflichtfelder an, die unbedingt ausgefüllt werden müssen. Die anderen Felder können, müssen aber nicht gefüllt werden. Datumsfelder müssen in folgendem Format eingetragen werden: YYYY-MM-DD. Wenn kein Datum eingetragen werden kann, so bleibt das Default-Datum im Feld stehen. Dies ist programmtechnisch bedingt. Zum Abschließen des Vorgangs drückt man den Save-Button am unteren Rand des Formulars. Seite 74 Diplomarbeit 5.7. Lanzanasto, Neumann, Plattner Statistiken Mit dieser Funktion erhält man einen Überblick über die Kontinente und Länder z.B. in Bezug auf die Anzahl der Shops und Hardware in einem Land. Abb. 15: Statistiken Mit den vier Tabs am oberen Rand kann man die gewünschte Region auswählen. Es werden dann jeweils die verschiedenen Shoptypen und weitere Elemente aufgelistet. 5.8. Details Mit den Details ruft man die speziellen Eigenschaften eines Elements auf, man erhält alle eingetragenen Informationen auf einen Blick. Um die Details aufzurufen, wählt man das gewünschte Element im Tree, aus der Liste oder mittels der Suche aus und drückt anschließend den „Details“-Button im Menü (siehe 5.2 Menü). Es öffnet sich folgender Tab, wobei der Tabname aus „Shop Details: SAP-Nummer“ besteht, um den Überblick über die geöffneten Tabs zu behalten: Seite 75 Diplomarbeit Lanzanasto, Neumann, Plattner Abb. 16: Shop Details Ruft man einen Shop auf, so wie in Abb. 16: Shop Details, so sieht man auf der linken Seite der Arbeitsfläche die Details, die den Shop an sich betreffen. Auf der rechten Seite befindet sich die Hardware, die im Shop vorhanden ist. Bei mehreren Teilen kann man über die Tabs direkt oberhalb der Hardware zwischen den einzelnen Hardwarekomponenten wählen. Am unteren Ende beider Seiten befindet sich der Button „Edit“. Dieser wird zum Editieren der Elemente verwendet. Wird ein Shop aufgelöst, so kann man ihn im Programm mit dem „Close this Shop“-Button am unteren Ende des Formulars als geschlossen kennzeichnen. Der Shop ist nun nur noch unter den „Closed Shops“ im Programm zu finden. Seite 76 Diplomarbeit Lanzanasto, Neumann, Plattner Editieren Ändert sich irgendeine Information zu einem Element, so kann man einfach mit dem Button „Edit“ in der Details-Ansicht (siehe 5.8 Details) das Element editieren. Dabei bleibt der Tab derselbe, die Informationen lassen sich aber nun verändern. Abb. 17: Shop Editieren So wie beim Hinzufügen eines neuen Elements erscheinen auch hier Textfelder, die zum Eintragen der Informationen genutzt werden. Die gelben Textfelder zeigen Pflichtfelder an, die unbedingt ausgefüllt werden müssen. Sind alle Änderungen vorgenommen, so schließt man das Editieren mit einem Klick auf den Save-Button am unteren Ende des Formulars ab. Die Änderungen werden nun in der Datenbank übernommen. Seite 77 Diplomarbeit Lanzanasto, Neumann, Plattner Bilder-Verzeichnis Abb. 1: Grundaufbau .................................................................................................. 8 Abb. 2: Firmenlogo Swarovski .................................................................................... 9 Abb. 3: Ordnerstruktur .............................................................................................. 28 Abb. 4 Application Model .......................................................................................... 32 Abb. 5: Fenster-Struktur ........................................................................................... 35 Abb. 6: Toolbar ......................................................................................................... 35 Abb. 7: Handlers ....................................................................................................... 36 Abb. 8: Commands ................................................................................................... 36 Abb. 9: Grundstruktur ............................................................................................... 36 Abb. 10: Oberflächen Übersicht ................................................................................ 70 Abb. 11: Menü .......................................................................................................... 71 Abb. 12: Tree ............................................................................................................ 72 Abb. 13 Suche .......................................................................................................... 73 Abb. 14: Hinzufügen einer Hardware ........................................................................ 74 Abb. 15: Statistiken ................................................................................................... 75 Abb. 16: Shop Details ............................................................................................... 76 Abb. 17: Shop Editieren ............................................................................................ 77 Seite 78