eské vysoké u£ení technické v Praze Fakulta elektrotechnická Katedra po£íta£·
Diplomová práce
Nástroj pro práci s rodopisy Bc. Martin Fábry
Vedoucí práce:
Ing. Pavel Strnad
Studijní program: Elektrotechnika a informatika, strukturovaný, Navazující magisterský
Obor: Výpo£etní technika
4. kv¥tna 2012
iv
v
Pod¥kování Rád bych na za£átku práce vyjád°il dík a uznání v²em, kte°í m¥ v psaní této práce podpo°ili. A´ uº ²lo o pomoc p°ímou (vedoucí práce Pavel Strnad) nebo jen morální podporu, jsem vám v²em velmi vd¥£ný. Nejv¥t²í dík pat°í mým rodi£·m za to, ºe m¥ p°es r·zné výst°elky m¥li rádi a na studiích m¥ nenechali zhynout hladem. D¥kuji.
vi
vii
Prohlá²ení Prohla²uji, ºe jsem práci vypracoval samostatn¥ a pouºil jsem pouze podklady uvedené v p°iloºeném seznamu. Nemám závaºný d·vod proti uºití tohoto ²kolního díla ve smyslu 60 Zákona £. 121/2000 Sb., o právu autorském, o právech souvisejících s právem autorským a o zm¥n¥ n¥kterých zákon· (autorský zákon).
V Praze dne 3. 5. 2012
.............................................................
viii
Abstract This thesis aims to create new software support tool for genalogy research. The application (called GENiE) uses international XML standard GenXML for genealogy data exchange. It also supports data import from plain text genealogy data format GEDCOM. The GENiE project is developed in objective-functional programming language Scala. Description of Scala language advanced features and their evaluation on real life project is the second goal of this thesis.
Abstrakt Tato diplomová práce se zam¥°uje na vytvo°ení software pro podporu genealogického výzkumu pro b¥ºného uºivatele - GENiE. Vyuºívá mezinárodní XML standard pro vým¥nu genealogických dat GenXML, podporuje také textový formát GEDCOM. Projekt GENiE je vytvo°en ve funkcionáln¥ objektovém programovacím jazyce Scala. Popis pokro£ilých funkcí jazyka Scala a jejich vyzkou²ení na reálném projektu je druhým cílem této práce.
ix
x
Obsah I Práce
1
1 Úvod
3
2 Popis °e²eného problému
5
2.1 2.2 2.3
2.4 2.5
Slovní£ek základních genealogických pojm· . . Poºadavky na aplikaci . . . . . . . . . . . . . . 2.2.1 Funk£ní poºadavky . . . . . . . . . . . . 2.2.2 Systémové poºadavky . . . . . . . . . . Formáty pro ukládání genealogických dat . . . 2.3.1 GEDCOM . . . . . . . . . . . . . . . . . 2.3.2 Gentech Genealogy Data Model (GDM) 2.3.3 GenXML . . . . . . . . . . . . . . . . . 2.3.4 A dal²í . . . . . . . . . . . . . . . . . . . Popis vít¥zného formátu GenXML . . . . . . . 2.4.1 Struktura formátu . . . . . . . . . . . . Situace na trhu s genealogickými programy . . 2.5.1 GenealogyJ . . . . . . . . . . . . . . . . 2.5.2 MyHeritage Family Tree Builder . . . . 2.5.3 Cognatio . . . . . . . . . . . . . . . . . 2.5.4 GRAMPS . . . . . . . . . . . . . . . . . 2.5.5 Pou£ení z testovaných program· . . . .
3 Analýza 3.1 3.2 3.3 3.4
P°ípady uºití . . . . . . . . . . . . . . . . . . 3.1.1 Mapování poºadavk· na p°ípady uºití Architektura aplikace . . . . . . . . . . . . . . Konceptuální datový model . . . . . . . . . . 3.3.1 Komponenta Model . . . . . . . . . . Konceptuální dynamický model . . . . . . . . 3.4.1 Diagramy aktivit . . . . . . . . . . . . 3.4.2 Sekven£ní diagramy . . . . . . . . . .
4 Pouºité technologie 4.1 4.2
. . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
Zpracování XML dokument· v jazyce Scala . . . . . . . 4.1.1 Dotazování nad XML dokumentem . . . . . . . . 4.1.2 Porovnání zpracování XML v jazyce Java a Scala GUI v jazyce Scala . . . . . . . . . . . . . . . . . . . . . 4.2.1 Filozoe vytvá°ení GUI v jazyce Scala . . . . . . xi
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
5 6 6 6 7 7 8 9 9 9 10 11 12 12 13 14 15
17
17 21 23 24 24 28 28 30
33
34 35 37 41 41
xii
OBSAH
4.2.2
Provázání s knihovnou Java Swing . . . . . . . . . . . . . . . . . . 44
5 Návrh 5.1
Logický datový model . . . . . 5.1.1 Komponenta Model . . 5.1.2 Komponenta GUI . . . . 5.1.3 Komponenta Controller
6 Implementace 6.1
6.2
6.3
6.4
6.5
6.6
6.7
6.8
První iterace . . . . . . . . 6.1.1 Cíle . . . . . . . . . 6.1.2 Pr·b¥h iterace . . . 6.1.3 Zajímavé poznatky . 6.1.4 Záv¥r první iterace . Druhá iterace . . . . . . . . 6.2.1 Cíle . . . . . . . . . 6.2.2 Pr·b¥h iterace . . . 6.2.3 Zajímavé poznatky . 6.2.4 Záv¥r druhé iterace . T°etí iterace . . . . . . . . . 6.3.1 Cíle . . . . . . . . . 6.3.2 Pr·b¥h iterace . . . 6.3.3 Zajímavé poznatky . 6.3.4 Záv¥r t°etí iterace . tvrtá iterace . . . . . . . . 6.4.1 Cíle . . . . . . . . . 6.4.2 Pr·b¥h iterace . . . 6.4.3 Zajímavé poznatky . 6.4.4 Záv¥r £tvrté iterace . Pátá iterace . . . . . . . . . 6.5.1 Cíle . . . . . . . . . 6.5.2 Pr·b¥h iterace . . . 6.5.3 Zajímavé poznatky . 6.5.4 Záv¥r páté iterace . está iterace . . . . . . . . . 6.6.1 Cíle . . . . . . . . . 6.6.2 Pr·b¥h iterace . . . 6.6.3 Zajímavé poznatky . 6.6.4 Záv¥r ²esté iterace . Sedmá iterace . . . . . . . . 6.7.1 Cíle . . . . . . . . . 6.7.2 Pr·b¥h iterace . . . 6.7.3 Zajímavé poznatky . 6.7.4 Záv¥r sedmé iterace Osmá iterace . . . . . . . . 6.8.1 Cíle . . . . . . . . . 6.8.2 Pr·b¥h iterace . . . 6.8.3 Zajímavé poznatky .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
47
47 47 48 50
53
53 53 54 54 55 55 55 55 56 57 57 57 57 57 58 58 58 59 59 59 60 60 60 60 61 62 62 62 62 63 64 64 64 64 64 65 65 65 65
xiii
OBSAH
6.8.4
Záv¥r osmé iterace . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
7 Testování 7.1
7.2
Framework Scalatest . . . . . . . . . . . . . . . . . . . . 7.1.1 Test-driven vývoj (TDD) . . . . . . . . . . . . . 7.1.2 Behaviour-driven vývoj (BDD) . . . . . . . . . . 7.1.3 Akcepta£ní testy . . . . . . . . . . . . . . . . . . Vyuºití frameworku Scalatest pro vývoj aplikace GENiE 7.2.1 Loader . . . . . . . . . . . . . . . . . . . . . . . . 7.2.2 Exporter . . . . . . . . . . . . . . . . . . . . . . . 7.2.3 Model . . . . . . . . . . . . . . . . . . . . . . . .
8 Záv¥r 8.1 8.2 8.3 8.4
Shrnutí . . . . . . . . . . . Zhodnocení práce s jazykem Tato práce v £íslech . . . . A co dál? . . . . . . . . . .
. . . . Scala . . . . . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
67
67 67 68 69 70 70 71 72
73
73 74 74 74
II Dodatek
77
Literatura
79
A Slovní£ek
81
B Seznam zkratek
83
C Obsah p°iloºeného CD
85
D Stru£ný návod k pouºití aplikace
87
xiv
OBSAH
Seznam obrázk· 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13
P°ípady uºití - Person management . . . P°ípady uºití - Source management . . . P°ípady uºití - Visualization . . . . . . . P°ípady uºití - Data Import/Export . . P°ípady uºití - Reporting . . . . . . . . Diagram t°íd - Komponenta Model . . . Diagram t°íd - Balí£ek Model.Assertion Diagram t°íd - Balí£ek Model.Person . . Diagram t°íd - Balí£ek Model.Project . . Diagram aktivity - Add Person . . . . . Diagram aktivity - Set up Relationship . Sekven£ní diagram - Add person . . . . Sekven£ní diagram - Remove person . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
18 19 20 20 21 24 26 26 28 29 30 31 32
4.1
Scala XML - hierarchie t°íd . . . . . . . . . . . . . . . . . . . . . . . . . . 34
5.1 5.2
Návrhové t°ídy - komponenta GUI . . . . . . . . . . . . . . . . . . . . . . 49 Návrhové t°ídy - komponenta Controller . . . . . . . . . . . . . . . . . . . 50
D.1 Screenshot - detail osoby . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 D.2 Screenshot - atributy osoby . . . . . . . . . . . . . . . . . . . . . . . . . . 89 D.3 Screenshot - rodinný strom . . . . . . . . . . . . . . . . . . . . . . . . . . 90
xv
xvi
SEZNAM OBRÁZK
Seznam tabulek 2.1
Porovnání genealogických nástroj· . . . . . . . . . . . . . . . . . . . . . . 15
3.1
Mapování poºadavk· na p°ípady uºití . . . . . . . . . . . . . . . . . . . . 22
8.1
Tato práce v £íslech
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
xvii
xviii
SEZNAM TABULEK
ást I
Práce
1
Kapitola 1
Úvod V rámci své diplomové práce jsem se rozhodl v¥novat genealogii, p°esn¥ji °e£eno softwaru pro podporu genealogických projekt·. Genealogie je pomocná v¥da historická1 zabývající se vztahy mezi lidmi na základ¥ jejich p·vodu. Co bylo je²t¥ p°ed pár lety koní£kem n¥kolika nad²enc·, roz²i°uje se v poslední dob¥ mezi stále více lidí. Mnohé dnes zajímá, kdo byli jejich p°edci a odkud pocházeli. N¥kterým znalost minulosti pomáhá ukotvit se ve sloºité p°ítomnosti. Jiní touºí spojit své jméno se slavnými osobnostmi z dob dávno minulých. A mnozí jsou prost¥ jen zv¥daví. Skute£ná genealogická práce se sestává z dlouhých pátrání po archivech, matrikách a zkoumání v¥kem zaºloutlých dokument·. Málokdo má ov²em £as pono°it se do studia takhle hluboko. Mohou si tedy najmout profesionální genealogy, aby provád¥li výzkum za n¥. Hleda£e rodinných historií za²ti´uje na území eské republiky eská genealogická a heraldická spole£nost[2], která vydává vlastní £asopis a po°ádá i vzd¥lávací kurzy. Rady do za£átku a profesionální genealogy nabízející vypracování genealogických studií m·ºete sami najít nap°íklad na stránkách Genea.cz[3]. Mnozí ov²em tak náro£ní nejsou a ke ²t¥stí jim sta£í zmapování n¥kolika pokolení rodinné historie. A do druhé skupiny pat°ím i já poté, co jsem doma p°i jednom úklidu narazil na fotograe lidí mn¥ neznámých a dopisy t¥ch, kte°í zesnuli dlouho p°ed mým narozením. Jakmile se £lov¥k za£ne zabývat studiem minulosti své rodiny, musí si nutn¥ vytvo°it n¥jaký systém pro ukládání a t°íd¥ní získaných informací. Systém, který mu umoºní drºet si p°ehled o pokroku dosaºeném p°i jeho práci. N¥kterým sta£í na toto tuºka a papír, ale tyto prost°edky bývají dosta£ující pro malé sady dat. Kdyº za£ne objem informací nar·stat, primitivní nástroje p°estávají sta£it. A tady p°ichází ke slovu moderní technologie. Spolu s nár·stem zájmu o genealogii se za£aly objevovat i r·zné aplikace podporující správu genealogických dat. N¥kolik z nich jsem vyzkou²el, jejich popis najdete v jedné z následujících kapitol. Byly mezi nimi aplikace ²patné, pr·m¥rné i velice dobré, p°esto jsem se rozhodl vytvo°it aplikaci vlastní. Aplikaci, která by nabídla jednoduchou správu genealogických dat pro b¥ºného uºivatele, který by si rád vytvo°il sv·j soukromý rodinný strom. Cílem této práce nicmén¥ není jen vytvo°ení genealogického software, rád bych touto prací také prov¥°il schopnosti funkcionáln¥ objektového jazyka Scala. Jiº del²í dobu se 1
Do této kategorie pat°í vedle Genealogie i Heraldika nebo Numismatika
3
4
KAPITOLA 1.
ÚVOD
tímto jazykem zabývám, i kdyº prozatím jsem ho pouºil pouze v men²ích projektech (numerické °e²ení diferenciálních rovnic a jejich vizualizace, genetický algoritmus pro °e²ení problému batohu atd.). Aplikace takového rozsahu, jaký p°edpokládám u této práce, bude výborným testem, zda dokáºe tento programovací jazyk nabídnout vývojá°·m n¥jakou p°idanou hodnotu oproti b¥ºn¥ pouºívaným jazyk·m jako Java nebo C++. P°edpokládám, ºe bude nutné potýkat se p°i práci s nedostate£nou dokumentací jazyka a slabou podporou v nástrojích pro vývojá°e. To, jestli nakonec p°eváºí výhody nebo nevýhody, se dozvíte v záv¥ru této práce Kaºdopádn¥ bych rád zdrojové kódy projektu zve°ejnil na jednom z projektových server· jako open source, aby bylo moºné na projektu dále pracovat a roz²í°it ho mezi ostatní zájemce a´ uº z °ad genealog· nebo fanou²k· jazyka Scala. A na záv¥r jedna zajímavost, abychom se naladili na genalogickou notu: Informace o Konfuciovi a jeho rodin¥ se zaznamenává uº p°es 2500 let[1] a graf s rozrodem obsahuje p°es 2 miliony jmen. Jeho poslední kniºní reprezentace má p°es 43 000 stran a je vázaná v 80 knihách
Kapitola 2
Popis °e²eného problému 2.1
Slovní£ek základních genealogických pojm·
rodopis soukromý zájem v oblasti genealogie pokrevenství vztah mezi dv¥ma osobami mající spole£ného p°edka (denuje se i stupe¬ pokrevenství podle po£tu generací od spole£ného p°edka)
p°íbuzenství vztah vzniklý manºelstvím vývod souhrn p°edk· jednotlivce pr·ba neúplná £ást vývodu rozrod potomstvo osoby, po me£i i po p°eslici (tedy potomci obojího pohlaví) rodokmen potomstvo osoby, dále sleduje a rozvádí pouze potomky muºského pohlaví1 rodinný strom gracké zobrazení vývodu, rozrodu £i rodokmenu
Zdroj: genea.cz[3]
1
rozrod a rodokmen se b¥ºn¥ zam¥¬ují, na stránkách genea.cz sice oba pojmy odli²ují, ale mnoho i
profesionálních genealog· pracuje pouze s po jmem rodokmen (viz t°eba http://www.rodopis.info/)
5
6
KAPITOLA 2.
2.2
POPIS EENÉHO PROBLÉMU
Poºadavky na aplikaci
Jak uº nazna£il úvod, hlavní poºadavkem je vytvo°it aplikaci pro správu genealogických dat. Poj¤me si tento obecný poºadavek blíºe prozkoumat. Centrálním prvkem v genealogických datech je osoba, lépe °e£eno mnoºina osob. Kaºdá osoba má sv·j popis denován v p°íslu²ných atributech. Mimo atribut· má kaºdá osoba je²t¥ asociovánu mnoºinu k ní se vztahujících událostí. Jednotlivé osoby jsou pak navzájem svázány p°íbuzenskými svazky. Ov²em genealogická data se neberou jen tak ze vzduchu, p°i správném výzkumu by m¥la být kaºdá informace podloºena n¥jakým zdrojem. Mimo správy osob tak dostáváme i správu zdroj· informací. Samotné ukládání dat by ov²em nesta£ilo. Dokud si nedokáºeme data zobrazit a hledat v nich souvislosti, aplikace nemá valného smyslu. Proto je d·leºité i mít moºnost data si zobrazit, vyhledávat v nich a také generovat výstupy pro jiné programy nap°. do HTML formátu. Denujme si nyní poºadavky formáln¥ji:
2.2.1 Funk£ní poºadavky 1. Správa osob Tento poºadavek zahrnuje p°idávání nových osob, mazání existujících (v£etn¥ bezpe£ného odstran¥ní vazeb na dal²í osoby) a editaci informací o osobách. A také vyhledávání podle d·leºitých parametr· (jméno, období, kdy osoba ºila) 2. Správa zdroj· informací Do této kategorie spadá p°idávání nových zdroj·, mazání a editace stávajících. 3. Podpora genealogického výzkumu Podpora výzkumu úzce souvisí s p°edchozím bodem. Je t°eba, aby bylo jasné, na jakých zdrojích se informace zakládá. 4. Vizualizace dat Seznamy osob a zdroj· je t°eba zobrazovat v p°ehledné form¥ (nejlépe tabulce) s moºností zobrazení detail·. Osoby bude navíc moºné zobrazit je²t¥ ve form¥ stromu. 5. Tvorba report· Aplikace bude um¥t exportovat data do obecn¥ pouºívaného a snadno p°enositelného formátu HTML.
2.2.2 Systémové poºadavky 1. Desktopová aplikace v jazyce Scala P·jde o desktopovou aplikaci napsanou ve funkcionáln¥ objektovém jazyce Scala. Tím získá aplikace moºnost provozu na více platformách.2 2
Protoºe Scala kód se kompiluje do Java byte code a b¥ºí pak na jakémkoliv Java Virtual Machine.
2.3.
FORMÁTY PRO UKLÁDÁNÍ GENEALOGICKÝCH DAT
7
2. Uloºení dat v XML Pro uchovávání dat bude aplikace vyuºívat formát XML, který je p°ehledn¥j²í neº prostý text. Aplikace bude vyuºívat jeden ze stávajících formát· pro ukládání genealogických dat v XML.3 3. Import dat z GEDCOM formátu Formát GEDCOM (více informací o n¥m v dal²í sekci) je de facto standardem v ukládání genealogických dat a aplikace jej bude podporovat. 2.3
Formáty pro ukládání genealogických dat
2.3.1 GEDCOM Formát GEDCOM (GEnealogical Data COMmunication) byl v polovin¥ osmdesátých let vytvo°ený pro pot°eby americké církve The Church of Jesus Christ of Latter-day Saints (znám¥j²í pod názvem Mormonská církev). B¥hem své existence pro²el mnoha úpravami a stal se standardem pro ukládání genealogických dat. Vývoj se v²ak zastavil v roce 1996. Poslední ociální verze (5.5), je tak uº 16 let stará a p°estává vyhovovat sou£asným poºadavk·m. Prob¥hlo sice n¥kolik pokus· o roz²í°ení formátu £i jeho p°evedení do XML, ale ty bez ociální podpory ze strany autor· p·vodního formátu a církve postupn¥ vyzn¥ly do prázdna.
P°ehled pouºívaných verzí: • 5.5 - standard pouºívaný v¥t²inou genealogických program· • 5.5.1 - roz²í°ení modernizující p°edchozí verzi (p°ibývá nap°íklad informace o webu a emailu) Tato verze nebyla ociáln¥ uznána. • 6.0 - pokus o p°evod GEDCOM do XML, existuje pouze draft
Popis formátu: Data se ukládají do souboru s p°íponou ged. Jedná se prostý text uzp·sobený pro strojové £tení. Soubor se skládá ze t°í základní sekcí: • hlavi£ka obsahující informace o verzi, autorovi a pouºitém kódování • £ást se záznamy o jednotlivých osobách (INDI), rodinách (FAM), zdrojích informací (SOUR) a dal²ích • pati£ka Kaºdý °ádek za£íná £íslem zna£ící úrove¬ daného záznamu, p°i£emº záznamy nejvy²²í úrovn¥ HEAD, INDI, SOUR atd. za£ínají na úrovni s ozna£ením nula. Auto°i nezapomn¥li ani na multimediální obsah, pomocí záznamu OBJE tak m·ºeme p°idat multimédia. Máme dv¥ moºnosti. Za prvé zakomponovat audiovizuální obsah p°ímo do .ged souboru, coº je nepraktické a m·ºe vyústit v enormní nár·st velikosti soubor·. Druhou moºností je p°iloºit multimediální obsah jako externí soubory a denovat na n¥ v .ged souboru jen odkazy s popisem obsahu. 3
Aby nedo²lo k b¥ºné situaci v IT, kdy máme deset navzájem si konkurujících formát·. Pak n¥kdo
dostane nápad vytvo°it jeden formát, který je v²echny nahradí, a ve výsledku pak máme jedenáct si konkurujících formát·.
8
KAPITOLA 2.
POPIS EENÉHO PROBLÉMU
Ukázka: V následující ukázce m·ºete vid¥t, jak by vypadal jednoduchý záznam o mé osob¥ v£etn¥ hlavi£ky souboru. 0 HEAD 1 DATE 25 OCT 2011 2 TIME 23:00:25 1 SUBM @U1@ 1 FILE fabry.ged 1 GEDC 2 VERS 5.5 2 FORM Lineage-Linked 1 CHAR ANSI 1 LANG Czech ... 0 @I3@ INDI 1 NAME Martin /Fábry/ 2 GIVN Martin 2 SURN Fábry 1 SEX M 1 BIRT 2 DATE 18 FEB 1987 3 TIME 20:58:58 2 PLAC Jarom¥° 1 FAMC @F1@ ...
Omezení: Uºivatel tohoto formátu d°íve £i pozd¥ji narazí na n¥která z jeho omezení.
Za v²echny jmenujme pon¥kud voln¥j²í specikaci, která poskytuje p°íli²né místo pro p°edstavivost a vznikají tak ob£as problémy s kompatibilitou p°i p°enosu mezi jednotlivými genealogickými programy. Dal²í nesnáze jsou zp·sobené jiº návrhem samotného formátu, který je jako ostatn¥ v²e poplatný své dob¥. GEDCOM specikace tak nap°íklad nepo£ítá se speciálními typy vztah· jako ºití ve spole£né domácnosti nebo homosexuální svazky. Vznikají také redundance dat, kdy kaºdá událost m·ºe být spojena jen s jednou rodinou nebo osobou, pokud na dané události participovalo n¥kolik osob £i rodin, musíme ji mít v souboru vícekrát. P°i zpracování také naráºíme problém s dop°ednými odkazy, kdy jsme nuceni p°i zkoumání vztah· procházet jeden soubor n¥kolikrát.
2.3.2 Gentech Genealogy Data Model (GDM) Datový model[7] vyvinutý rmou Gentech nyní pokra£uje pod zá²titou National Genealogical Society. Jedná se o konceptuální datový model, nikoliv o formát uloºení dat. Ne°e²í tedy, kde a jak p°esn¥ budou data uloºena. Na rozdíl od GEDCOM se zam¥°uje více na podporu genealogického výzkumu. Samotné informace o jednotlivých osobách a rodinách tvo°í pouze malou £ást.
2.4.
POPIS VÍT
ZNÉHO FORMÁTU GENXML
9
Navíc tu máme výzkumné projekty, jejich cíle, postup p°i objevování. Teprve na základ¥ výzkumu se formuluje tvrzení (assertion) o n¥jaké osob¥ nebo události. A£koliv primárn¥ ur£en pro profesionály v oboru genealogie, umoº¬uje práci i amatér·m. Není tedy nutné striktn¥ postupovat od zdroj· a jejich analýzy k tvrzení, ale m·ºeme p°ímo zadávat i výsledná data nepodloºená pat°i£ným výzkumem. Poslední vydanou verzí je 1.1 z roku 2000, dále uº nebyl nijak rozvíjen.
2.3.3 GenXML Formát GenXML implementuje vý²e zmín¥ný (zjednodu²ený) Gentech model, a to ve form¥ XML dokumentu. Ztrácí oproti GDM n¥kolik konstrukt· (nap°. GROUP), ale zjednodu²uje tak implementaci a lépe se hodí pro jednodu²²í modely, které nejsou tolik zacílené na výzkum. Podporuje tak nap°íklad oproti GDM jen jeden genealogický projekt na soubor. Protoºe se jedná o vít¥zný formát, najdete jeho podrobn¥j²í popis v následující sekci.
2.3.4 A dal²í Existuje i bezpo£et dal²ích formát·, s trochou nadsázky by se dalo °íct, ºe co genealogický program, to vlastní formát. Jejich pouºití je tak omezeno pouze na p°íslu²ný software. V¥t²ina jich je v²ak zaloºena bu¤ na GEDCOM (nap°. GRAMPS XML p°ipomínající GEDCOM v XML) nebo na GDM. 2.4
Popis vít¥zného formátu GenXML
Jak uº bylo zmín¥no v p°edchozí kapitole GenXML formát vychází z Genealogy Data Model (GDM). P·vodní datový model byl v n¥kterých aspektech zjednodu²en, p°esto v²ak poskytuje dostate£né moºnosti vyjád°ení v²ech d·leºitých fakt a ve srovnání s jinými formáty p·sobí o poznání sostikovan¥ji. Za vznik tohoto formátu je zodpov¥dný pan Christoer Owe z norské spole£nosti Cosoft. Studiu historie a genealogie se v¥nuje uº p°es 25 let a publikoval nap°íklad v £asopise Norsk Slektshistorisk Tidsskrift zabývající se genealogií na v¥decké bázi. Jde o autora v dal²í kapitole zmín¥ného programu Cognatio, který (ne úpln¥ ne£ekan¥) GenXML také vyuºívá. V sou£asné dob¥ se nachází GenXML ve verzi 3.0[24]. Pro tuto verzi existuje kompletní specikace, nicmén¥ se zdá, ºe po vydání této specikace v £ervnu 2010 se ve²keré aktivity na webu formátu zastavily a vývoj v tichosti skon£il. Za poslední rok nedo²lo k ºádným zm¥nám a program Cognatio pracuje stále jen se starou verzí 2.0. I p°es z°ejm¥ p°eru²ený vývoj nabízí formát v²e, co by mohla genealogická aplikace pot°ebovat. Poj¤me se proto podívat podrobn¥ji na jeho vnit°ní strukturu. Ostatní formáty berou za centrální jednotku osobu a na ni nabalují dal²í informace. V GenXML uº osoba nehraje centrální roli a na stejné úrovni najdeme dal²í d·leºité entity. Osoba (Person) pat°í stále k nejd·leºit¥j²ím, ale v¥t²ina informací o osob¥ (ºivotní události, atributy, . . . ) existují jako samostatné entity na stejné úrovni a jsou k osobám
10
KAPITOLA 2.
POPIS EENÉHO PROBLÉMU
vázány p°es reference. V²echny informace o osob¥ tak prezentujeme ve form¥ Tvrzení (Assertion), které je zase zaloºené na n¥jakém zdroji (Source). Míra detail· a informací obsaºených v entitách se li²í podle úrovn¥ (level) daného GenXML souboru. Základní úrove¬ 1 obsahuje pouze základní informace a ty je²t¥ navíc nemusí být podep°eny ºádnými zdroji. Naproti tomu nejvy²²í úrove¬ 4 umoº¬uje specikovat mnoho up°es¬ujících detail·. Nap°íklad m·ºeme osob¥ p°i°adit i reference na její pravd¥podobné duplicity v projektu a tím umoºnit jejich jednodu²²í slou£ení do jedné.
2.4.1 Struktura formátu Hlavi£ka souboru Kaºdý GenXML soubor obsahuje úvodní £ást s informacemi o souboru, autorovi atd. Do této £ásti pat°í p°edev²ím následující entity: • File - v entit¥ File se nacházejí informace o verzi GenXML a úrovni • Header - obsahuje základní informace o souboru dat jako nap°íklad jméno autora, data vytvo°ení a poslední zm¥ny nebo název programu, který tento soubor dat exportoval
T¥lo souboru V hlavní £ásti se nachází tyto d·leºité entity obsahující informace o osobách:
• Person - entita Person reprezentuje jednu osobu. Oproti jiným formát·m obsahuje jen minimum informací, dal²í podrobnosti o osob¥ jsou denovány pomocí sady tvrzení. • EventType - entita EventType slouºí k denici r·zných druh· událostí. Podrobnosti o typu události jsou denované v atributu "class"(nap°íklad birth, graduation atd.) a její stru£ný popis v podelementu "description". Formát GenXML tak umoº¬uje uºivateli denovat si vlastní sadu událostí, coº p°ijde vhod p°edev²ím u událostí z jiného neº euroamerického kulturního okruhu. • Assertion - tvrzení reprezentují detaily o jednotlivých osobách. Existují tvrzení n¥kolika typ·, v²echna by v²ak m¥la být zaloºena na úryvku (Excerpt) ze zdroje (Source) • Attribute - atribut reprezentuje ur£itou vlastnost, charakteristiku jedné osoby. V modelu existují t°i základní t°ídy atribut·: textová s textovým popisem (nap°. titul, zam¥stnání), £íselná (v¥k) nebo p°íznak (osoba ºije). • Event - tato entita reprezentuje událost v ºivot¥ jedné £i více osob. Osoby mohou být s událostí asociovány bu¤ jako hlavní ú£astník (principal) nebo vedlej²í ú£astník (subordinace). Na rozdíl od atributu událost má ve v¥t²in¥ p°ípad· p°esn¥ stanovené datum £i £asový rozsah. • Relationship - Entita reprezentující vztah mezi rodi£em a dít¥tem. Od GenXML verze 3.0 m·ºe kaºdý vztah asociovat pouze rodi£e a jedno dí´¥. Pro vyjád°ení vztahu v rodin¥ s více d¥tmi musíme vytvo°it vlastní entitu Relationship pro kaºdé dít¥ zvlá²´. GenXML nijak neo²et°uje, zda je osoba uvedená jako otec skute£n¥ muºského pohlaví (a to samé platí i u matek), tato kontrola musí být provedena uºivatelem zadávajícím data nebo programem.
2.5.
SITUACE NA TRHU S GENEALOGICKÝMI PROGRAMY
11
• Info - Informace o osob¥, které nezapadají do jiných druh· tvrzení, jsou ukládány v entitách Info. • Place - Entita reprezentující geograckou lokaci. Pro práci se zdroji slouºí p°edev²ím tyto entity:
• Repository - Tato entita reprezentuje repositá° zdroj· jako nap°íklad knihovna £i archiv. • Source - Zdroj m·ºe p°edstavovat knihu £i dokument. Forma dokumentu se r·zní od audio/video aº po ru£n¥ psané dokumenty. • Excerpt - Entita reprezentující jeden úryvek ze zdroje a jeho lokaci ve zdroji (typicky stránka v knize). GenXML podporuje i výzkum. K tomu slouºí elementy Úkol (Task) a Cíl (Objective):
• Objective - Entita Objective reprezentuje cíl bádání, typicky obsahuje n¥kolik díl£ích úkol·. Popisuje problém i jeho moºné °e²ení. • Task - Tato entita reprezentuje jeden díl£í úkol svázaný s ur£itou osobou a zdrojem. Op¥t obsahuje popis problému a °e²ení. A celý soubor je zakon£en entitou Total s informacemi o celkových po£tech jednotlivých entit. 2.5
Situace na trhu s genealogickými programy
V¥nujme se nyní na chvíli situaci na trhu s genealogickým softwarem. Je²t¥ p°ed vlastní analýzou a návrhem byli prozkoumáni moºní konkurenti projektu. Kritériem pro zkoumané programy byla jejich bezplatnost, protoºe b¥ºný uºivatel si pro domácí tvorbu genealogií t¥ºko bude kupovat drahý software. Nakonec byli vybráni £ty°i zástupci: GenealogyJ, MyHeritage Family Tree Builder, Cognatio, GRAMPS. Jednotlivé aplikace byly hodnoceny podle n¥kolika kritérií:
• jaký formát pouºívají standardn¥ pro ukládání dat • s jakými dal²ími formáty dat dokáºou pracovat • zda podporují reporting ve formátu pochopitelného £lov¥ku a pokud ano, pak do kterého • licen£ní podmínky • intuitivní ovládání (subjektivn¥ hodnoceno) • moºnosti vizualizace dat v aplikaci • podpora zdroj· informací a genealogického výzkumu jako celku
12
KAPITOLA 2.
POPIS EENÉHO PROBLÉMU
2.5.1 GenealogyJ Homepage: http://genj.sourceforge.com Licence: GNU GPL Testovaná verze: 3.0 Podporované formáty: GEDCOM (p°i nahrávání z externího ged souboru i p°es správnou informaci o kódování má problémy na£íst £eské znaky)
První zástupcem genealogického softwaru v na²em malém p°ehledu je program GenealogyJ. Uº samotné jméno indikuje, ºe se jedná o aplikaci napsanou v jazyce Java. GenealogyJ se zam¥°uje p°edev²ím na domácí pouºití (jednoduché projekty s men²ím po£tem osob a událostí). Základní okno je £len¥no na dv¥ poloviny - levá s pohledy (tabulka, strom) a pravá s moºnostmi editace vybrané osoby £i rodiny. Interface v²ak p°i v¥t²ím po£tu osob ztrácí p°ehlednost, tomuto nepomáhá ani fakt, ºe z n¥jakého d·vodu není moºné procházet stromem p°edk·. K tomu slouºí pohled Navigace, který zase zobrazuje informace o osobách aº po najetí kurzoru na ikonu osoby.
Klady • jednoduchost • ²iroké moºnosti exportu dat (grafy, £asové osy, tabulky) • uºivatelsky denovatelné zobrazení v n¥kterých pohledech pomocí HTML tag·
Zápory • chybí pohled umoº¬ující správu zdroj· • nep°ehledné p°i vy²²ím po£tu osob a rodin • moºnost zobrazit si místa na interní map¥ je sice zajímavá, ale prakticky díky neovladatelnosti a velkému m¥°ítku mapy zbyte£ná
Záv¥r Zajímavý software bohat¥ dosta£ující pro domácí vyuºití s místy trochu nep°e-
hledným ovládáním. Auto°i se snaºili o mnoho p°ídavných moºností (mnoho r·zných druh· exportu dat, mapa), které ov²em uºivatel stejn¥ tém¥° nevyuºije.
2.5.2 MyHeritage Family Tree Builder Homepage: http://www.myheritage.com Licence: freeware Testovaná verze: 4.0.0.916 Podporované formáty: vlastní, export/import GEDCOM soubor· Family Tree Builder (FTB) slouºí jako dopln¥k webu MyHeritage.com. Pro jeho pouºívání je t°eba se na tomto webu registrovat, a to i v p°ípad¥, ºe nechceme synchronizovat lokální data s databází MyHeritage.com. Základní £lenství zdarma obsahuje ur£itá omezení (maximáln¥ 250 osob v databázi, 500 MB na fotky), pro v¥t²inu uºivatel· je v²ak
2.5.
SITUACE NA TRHU S GENEALOGICKÝMI PROGRAMY
13
dosta£ující. Za vy²²í verze Premium a Premium Plus je t°eba si p°iplatit 150$ respektive 238$. Uºivatel tím získá v¥t²í prostor, p°ídavné funkce, jako je podpora výzkumu, a zbaví se nep°íjemných reklam. Samotná aplikace je p°ehledn¥ strukturovaná, okno tvo°í li²ta nástroj· p°epínající jednotlivé pohledy v hlavním panelu a postranní li²ta se seznamy osob. Základní pohled Strom umoº¬uje jednodu²e spravovat seznam osob a propojovat jednotlivé osoby pouhým p°etaºením ze seznamu na vhodné místo ve stromu. Za zmínku stojí také párování, které pomáhá vyhledávat totoºné osoby a spojovat je do jednoho záznamu. B¥ºn¥ se totiº m·ºe stát, ºe jednu a tu samou osobu p°idají r·zní uºivatelé MyHeritage, párovací funkce Smart Matches zkontroluje databázi a nahlásí moºné shody. Pro vyuºití této funkce je v²ak nutné exportovat vlastní projekt na web MyHeritage.
Klady • p°ehledné a srozumitelné uºivatelské rozhraní • moºnost synchronizace s databází MyHeritage • ²iroké moºnosti exportu dat ve form¥ graf· a výpis· • propojení s Google mapami
Zápory • omezení ve volné verzi (osekané moºnosti okolo výzkumu) • nutnost registrovat se na MyHeritage.com • velká volnost v nakládání s formátem GEDCOM, kdy si FTB p°idává vlastní tagy a kombinuje prostý text s XML • ob£asné pády aplikace
Záv¥r Profesionáln¥ vytvo°ený software se snadným ovládáním a mnoha uºite£nými
funkcemi. Nutnost registrace na webu MyHeritage je vyváºena moºností synchronizace dat s online databází a propojování vlastního rodokmenu s jiº existujícími rodokmeny jiných uºivatel·.
2.5.3 Cognatio Homepage: http://cosoft.org Licence: základní verze zdarma, standardní verze za 25$ Testovaná verze: 1.4.3 Podporované formáty: GEDCOM a GenXML, export navíc i GedML Aplikace od autora formátu GenXML popsaného vý²e se ukázala jako zklamání. Díky ²patn¥ navrºenému uºivatelskému rozhraní stává se jakýkoliv úkon zkou²kou trp¥livosti uºivatele. Ani funkcionalitou Cognatio nijak neosl¬uje a oproti svým konkurent·m v tomto testu nenabízí ºádnou podstatnou výhodu.
14
KAPITOLA 2.
POPIS EENÉHO PROBLÉMU
Klady • podpora formátu GenXML
Zápory • omezená základní verze • komplikované ovládání • nep°ehledné uºivatelské rozhraní
Záv¥r Prakticky nepouºitelný software, kterému láme vaz p°edev²ím komplikované ovládání. Výzva k zakoupení placené verze p°i kaºdém startu se tak stává výsm¥chem uºivateli, protoºe platit za podobný software 25$ by byl naprostý nesmysl.
2.5.4 GRAMPS Homepage: http://gramps.sourceforge.net/ Licence: GNU GPL Testovaná verze: 3.2.4 Podporované formáty: vlastní (GRAMPS XML), GEDCOM Open source projekt napsaný v jazyce Python p°íjemn¥ p°ekvapil. P°ehledný design a intuitivní ovládání umoº¬uje vytvo°it vlastní genealogii b¥hem krátké chvíle. Pokud by snad n¥kdo zaváhal, s pomocí kvalitn¥ zpracované nápov¥dy na GRAMPS Wiki si snadno poradí. K ukládání dat vyuºívá vlastní formát GRAMPS XML. Jedná se o XML soubor, pro úsporu místa navíc komprimovaný. Svojí strukturou p°ipomíná spí²e GEDCOM neº GDM a zam¥°uje se tak primárn¥ na osoby a jejich vztahy. Problém nastal p°i pokusu o spu²t¥ní aplikace na systému Windows. Program totiº vyuºívá speciální knihovny (pygtk, pygobject, pycairo), které nejsou na systémech Windows p°íli² udrºované. Existují tak pro Python 2.6, ale pro nov¥j²í verze uº ne.
Klady • p°ehledné uºivatelské rozhraní • ²iroké moºnosti • roz²i°itelnost pomocí widget· • sympatická gracká stylizace
Zápory • problémy se spu²t¥ním na systémech Windows
2.5.
15
SITUACE NA TRHU S GENEALOGICKÝMI PROGRAMY
Cognatio
GenealogyJ
GRAMPS
licence
MyHeritage FTB
Shareware
GNU GPL
GNU GPL
podporované formáty
GEDCOM
GRAMPS XML (nativní), GEDCOM
reporty
GenXML (nativní), GEDCOM, GedML (jen export) rtf
Základní Freeware Vlastní, COM
pdf, svg, png, tex
rtf, pdf, html
vizualizace
seznam
strom, seznam, £asová osa velmi omezená
pdf, latex, rtf, open dokument, html, svg strom, seznam ano
ano
ne
omezená
omezená
2
1
2
roz²i°itelné reporty, úprava vzhledu pomocí html ²ablon
roz²i°itelnost pomocí widget·
propojení s potálem myheritage.com
podpora zdroj· ano informací podpora vý- ano zkumu ovládání 1 je nej- 4
lep²í
jiné p°ednosti
x
verze GED-
strom, seznam
Tabulka 2.1: Porovnání genealogických nástroj·
Záv¥r ikovný software umoº¬ující snadnou tvorbu genealogií s aktivní komunitou autor· pracujících na dal²ích roz²í°eních.
2.5.5 Pou£ení z testovaných program· Jak by tedy m¥la vypadat budoucí aplikace GENiE? Na základ¥ testování vý²e zmín¥ného softwaru máme nyní p°esn¥j²í p°edstavu, £eho se snaºit dosáhnout a £eho se vyvarovat. P°edev²ím není t°eba p°idávat co nejvíc funkcí, jak se o to snaºí GenealogyJ a Cognatio. Nep°ispívá to p°ehlednosti a zbyte£n¥ by to komplikovalo vývoj. Jako vzor (pravda nejspí² nedosaºitelný, ale p°esto inspirující) slouºí aplikace Gramps, protoºe nabízí v²e pot°ebné a navrch v p°íjemném balení.
16
KAPITOLA 2.
POPIS EENÉHO PROBLÉMU
Kapitola 3
Analýza Vzhledem k tomu, ºe je v plánu zdrojový kód a dokumentaci projektu uvolnit jako opensource na jednom z k tomuto ú£elu vytvo°ených portálu, byly v aplikaci pouºity anglické názvy. To se týká názv· t°íd, metod i popisk· v UML diagramech. Proto se v samotném textu práce vyskytují názvy £ástí aplikací v angli£tin¥ (protoºe tak se skute£n¥ jmenují), kdeºto názvy nesouvisející p°ímo s aplikací jsou p°eloºeny do £e²tiny.
3.1
P°ípady uºití
P°ípady uºití vycházejí z poºadavk· denovaných v p°edchozí sekci. Uºivatelské role jsou v tomto p°ípad¥ vy°e²eny jednodu²e. Denujeme si totiº jen jednu roli, která má právo na plné nakládání se systémem. Byla zvaºována i moºnost vytvo°ení více uºivatelských rolí (právo na £tení informací/úpravu informací), ale vzhledem k orientaci aplikace na domácí desktopové pouºití se to ukázalo jako zbyte£ná komplikace. Sp°ízn¥né p°ípady uºití jsou rozd¥leny do p¥ti balí£k·: 1. Person management Základní balí£ek obsahující p°ípady uºití týkající se správy osob. Tedy v podstat¥ jádro celé aplikace. (a) Add Person P°idání nové osoby do kolekce osob. Tento krok vyºaduje vypln¥ní základních údaj· o osob¥. Nov¥ vytvo°ená osoba musí mít dopln¥né alespo¬ jméno a pohlaví. (b) Edit Person Editace údaj· o dané osob¥. Bude moºné editovat údaje o osob¥ (pohlaví, jméno, vzd¥lání atd.). Také bude moºné p°idat k osob¥ nové ºivotní události. Zárove¬ bude moºné p°i°adit osob¥ p°íbuzného (viz 1.c) (c) Add relative P°i°azení p°íbuzného (rodi£ nebo potomek) dané osob¥. P°i°adit m·ºeme jen existující osobu. 17
18
KAPITOLA 3.
ANALÝZA
(d) Remove person Odstran¥ní osoby z kolekce. Zárove¬ je t°eba odstranit vazby na odstran¥nou osobu. (e) Associate Information with Source Spojení informace (atributu, události) o osob¥ se zdrojem informace. Protoºe kaºdá informace by m¥la být v ideálním p°ípad¥ potvrzena n¥jakým zdrojem.
Obrázek 3.1: P°ípady uºití - Person management
2. Source Management Správa zdroj· informací. V genealogii hrají zdroje d·leºitou roli, protoºe jednotlivé informace pocházejí z r·zných zdroj· s r·znou mírou p°esnosti údaj·. (a) Add source P°idání nového zdroje do kolekce. Je t°eba vyplnit základní údaje o zdroji. (b) Edit Source Details Upravení informací o zdroji (c) Remove Source Odebrání zdroje z kolekce. Zárove¬ je t°eba odstranit vazby na odstran¥ný zdroj informací. 3. Visualization Balí£ek obsahující p°ípady uºití spojené s vizualizací dat. Jako vhodnou formu p°edpokládáme panel se záloºkami pro p°epínání mezi r·znými druhy zobrazení.
3.1.
PÍPADY UITÍ
19
Obrázek 3.2: P°ípady uºití - Source management
(a) Show genealogy tree Zobrazení genealogického stromu pro danou osobu. Hloubka stromu bude nastavitelná uºivatelem. (b) Show List of Persons Zobrazení osob v tabulce. V tabulce bude moºné vyhledávat podle jména. Vedle tabulky se pak pro ozna£enou osobu zobrazí její detaily (viz 3.c) (c) Show Person Details Zobrazení detail· konkrétní osoby. Detailní zobrazení obsahuje jméno, p°íjmení, data narození a úmrtí, partnery, potomky a rodi£e. Po stisknutí tla£ítka "Události"je moºné prohlédnout si dal²í události z ºivota dané osoby. Po stisknutí tla£ítka "Atributy"je moºné zjistit dal²í detaily o dané osob¥. (d) Show List of Sources Zobrazení seznamu zdroj· v tabulce. V tabulce bude moºné vyhledávat podle názvu zdroje. Vedle se pro daný zdroj zobrazí jeho detaily. (viz 3.5) (e) Show Source Details Zobrazení podrobných informací o zdroji. 4. Data Import/Export Balí£ek související s importem a exportem dat z a do soubor· £i jiných externích zdroj·. Systém by m¥l pracovat s daty ve formátu GenXML. Podporovat by m¥l také nepsaný standard genealogických dat a to GEDCOM ve verzi 5. (a) Load data from gml le Na£tení dat v GenXML formátu ze souboru. Aplikace si zkontroluje, zda se jedná skute£n¥ o XML dokument v GenXML formátu (podle hlavi£ky dokumentu) a p°evede data do reprezentace uvnit° systému.
20
KAPITOLA 3.
ANALÝZA
Obrázek 3.3: P°ípady uºití - Visualization
(b) Save data to gml le Uloºení dat do souboru ve formátu GenXML. Aplikace p°evede data z jejich vnit°ní reprezentace do GenXML formátu a uloºí je na disk. (c) Load data from GEDCOM le Na£tení dat ze soubor· ve formátu GEDCOM.
Obrázek 3.4: P°ípady uºití - Data Import/Export
5. Reporting (a) Generate HTML report Vygenerování HTML stránky s hypertextovými odkazy mezi p°íbuznými.
3.1.
21
PÍPADY UITÍ
Obrázek 3.5: P°ípady uºití - Reporting
3.1.1 Mapování poºadavk· na p°ípady uºití Kdyº máme nyní denované jednotlivé p°ípady uºití, m·ºeme se podívat do tabulky 3.1, jak jsou poºadavky na aplikaci z p°edchozí sekce pokryty t¥mito p°ípady uºití. Pro p°ehlednost je²t¥ zopakujme vý£et poºadavk·:
1.1 Správa osob 1.2 Správa zdroj· informací 1.3 Podpora genealogického výzkumu 1.4 Vizualizace dat 1.5 Tvorba report· 2.1 Desktopová aplikace v jazyce Scala 2.2 Uloºení dat v XML 2.3 Import a export dat v GEDCOM formátu
22
KAPITOLA 3.
poºadavek /p°ípad uºití 1.a Add person 1.b Edit person 1.c Add relative 1.3 1.d Remove person 1.4 1.e Associate source 2.a Add source 2.b Edit source 2.c Remove source 3.a Show tree 3.b Show persons 3.c Show person 3.d Show sources 3.e Show source 4.a Load GenXML 4.b Save GenXML 4.c Load GEDCOM 5.a HTML report
1.1
1.2
1.3
X
X X X X
1.4
1.5
2.1
2.2
2.3
X X X X
X X
X X
X X
X X X X X X X X
Tabulka 3.1: Mapování poºadavk· na p°ípady uºití
ANALÝZA
X
3.2.
3.2
ARCHITEKTURA APLIKACE
23
Architektura aplikace
Nyní se dostáváme k zásadní fázi celého projektu a to k návrhu architektury systému. Výb¥rem architektury se otvírá (£i ve ²patném p°ípad¥ zavírá) cesta k efektivní tvorb¥ systému, který bude jednoduchý na pochopení a jeho následné modikace a údrºba se nestanou no£ní m·rou vývojá°e. Pro aplikaci GENiE byl zvolen osv¥d£ený architektonický vzor Model-View-Controller. Samotná lozoe architektury MVC je pom¥rn¥ jednoduchá a v²eobecn¥ známá, proto tedy jen ve zkratce:
• T°i komponenty Model, View a Controller
Model obsahuje datové t°ídy a business logiku View obsahuje t°ídy pro zobrazení modelu Controller se stará o provázání mezi View a Controller • V principu existují jen dv¥ p°ímé vazby: Controller ⇒ Model a View ⇒ Model • asto se ov²em vyskytuje i jedno £i oboustranná vazba Controller ⇔ View Jak ukazuje popis, samotný princip MVC je velmi obecný a práv¥ proto se m·ºeme setkat s mnoha variantami této architektury. A tím samoz°ejm¥ £asto vznikají i chyby. Z takových t¥ch b¥ºn¥ se vyskytujících jmenujme nap°íklad tu, ºe Model drºí p°ímý odkaz na Controller nebo View a manipuluje s nimi (jedinou p°ípustnou variantou pro komunikaci Model -> View je notikování o zm¥nách v datech podle návrhového vzoru Observer[16]). Martin Fowler zmi¬uje jako £astou chybu také Model bez business logiky tzv. Anemic Domain Model[15]. Návrh architektury pro aplikaci GENiE se drºí t¥chto obecných zásad a vypadá takto:
• GUI (View)
vytvá°í uºivatelské rozhraní pomocí dat z modelu p°eposílá akce uºivatele controlleru • Controller (Controller)
mapuje uºivatelské akce na metody v modelu pracuje s exportéry a importéry • Model (Model)
obsahuje data obsahuje logiku upravující data V této variant¥ MVC, tak Controller slouºí jako Mediator[16], podobn¥ jako je tomu nap°íklad v knihovn¥ Java Swing [11]. K trojici MVC p°ibyly v p°ípad¥ systému GENiE je²t¥ komponenty Export a Import. Tyto dv¥ komponenty jsou vyuºívány komponentou Controller pro import respektive export dat z modelu. Cílem bylo dosahnout co moºná nejmen²í závislosti mezi jednotlivými komponentami, snaºit se tedy o tzv. volnou vazbu (loose coupling). Proto kaºdá komponenta skrývá své nitro, svou skute£nou implementaci a dal²ím komponentám umoº¬uje komunikaci p°es p°edem denované rozhraní.
24
KAPITOLA 3.
3.3
ANALÝZA
Konceptuální datový model
V minulé sekci jsme si nastínili rozvrºení aplikace do jednotlivých komponent a °ekli jsme si, ºe komunikace mezi komponentami bude probíhat p°es denovaná rozhraní. Poj¤me se tedy podívat na t°ídy v jednotlivých komponentách a na to, jak jsou denovaná rozhraní k jednotlivým komponentám.
3.3.1 Komponenta Model Komponenta model obsahuje datové t°ídy pouºívané v aplikaci a p°idruºenou logiku. Datové t°ídy vycházejí z formátu GenXML, kaºdý element vy²²í úrovn¥ denovaný ve standardu má svou reprezentaci jako t°ída. Oproti element·m z GenXML obsahují t°ídy je²t¥ dopl¬kové informace pro vnit°ní pot°eby aplikace, které se kv·li kompatibilit¥ s formátem neexportují do výsledných soubor· s daty. P°ímo v komponent¥ model se nalézají dal²í balí£ky, kde jsou t°ídy sdruºené podle logické p°íbuznosti. T°ídy v kaºdém z t¥chto balí£k· jsou spravovány správcovskou t°ídou (Manager).
Obrázek 3.6: Diagram t°íd - Komponenta Model
Balí£ek Model.Assertion Balí£ek obsahuje tyto t°ídy: • Assertion - t°ída reprezentující tvrzení, sama o sob¥ se nevyuºívá a slouºí jen jako nadt°ída pro r·zné typy tvrzení. Obsahuje unikátní identikátor ve tvaru "Axxx",
3.3.
KONCEPTUÁLNÍ DATOVÝ MODEL
25
kde xxx zna£í kladné celé £íslo. Dále nese v t°ídní prom¥nné "datatype"informace o tom, zda by m¥lo být tvrzení ve°ejn¥ dostupné £i zve°ejn¥né jen rodin¥. Tvrzení m·ºe být zaloºeno bu¤ na úryvku z n¥jakého zdroje nebo na jiném tvrzení.
• Attribute - t°ída s jednou konkrétní informací o osob¥ nap°. dosaºené vzd¥lání, barva vlas· atd. Atributy se li²í podle typu p°edávané informace na textov¥ zaloºené (obsahující prom¥nné text a textclass), £íselné (s prom¥nnými number a numberclass) a p°íznakové (agclass). as se u n¥ho nespecikuje, pro takové p°ípady je tu t°ída Event. • Event - t°ída s obsahující informace o události v ºivot¥ osoby. Událost se váºe k n¥jakému konkrétnímu datu £i £asov¥ omezenému úseku ºivota. Událostí existují r·zné druhy (eventtype). Událost má hlavní aktéry (principals), coº mohou být t°eba manºelé u události typu s¬atek, a pak dal²í osoby, které se také ú£astnily, ale nehrály jiº tak zásadní roli (subordinates). • EventType - t°ída pro denování r·zných druh· událostí. Obsahuje sv·j jednozna£ný identikátor ve tvaru "Exxx", kde xxx zna£í kladné celé £íslo. Zatímco druh události (classOfEvent) by m¥l být podstatné jméno nap°. narození, popis (description) uº by m¥lo být sloveso tedy narozen/a. Dále m·ºeme specikovat, jaký GEDCOM tag má být pouºit p°i exportu dat do GEDCOM formátu nebo po£et osob, které mohou na tomto typu události participovat. • Info - pro uloºení informace, které se nehodí do jiného typu tvrzení, slouºí t°ída Info. Informace se tu specikuje jako oby£ejný text. • Relationship - tato t°ída obsahuje informace o vztahu osob. T°ída v tomto p°ípad¥ z·stává v¥rná své p°edloze z formátu GenXML, která také nedovoluje specikovat vztahy více osob v jedné instanci. Je moºné doplnit, o jaký typ vztahu se jedná, zda biologický £i jiný, pomocí prom¥nné relation. T°ídy z balí£ku Assertions jsou spravovány pomocí t°ídy AssertionManager, kde jsou denovány klasické CRUD funkce. Pro ukládání jednotlivých objekt· jsou pouºity kolekce, které umoº¬ují p°ístup k objekt·m v konstatním £ase, tedy hashovací mapy. Volba klí£e pro vyhledávání v map¥ byla jednoduchá, protoºe v²em t°ídám z tohoto balí£ku je p°i°azen unikátní identikátor. AssertionManager si také drºí informace o pouºitých identikátorech a p°i p°idání nového objektu, se automaticky generuje identikátor nový.
Bali£ek Model.Person Balí£ek obsahuje tyto t°ídy: • Person - t°ída reprezentující jednu osobu. Kaºdá osoba je jednozna£n¥ identikována pomocí °et¥zce "Pxxx", kde xxx je nahrazeno kladným celým £íslem. Jména osoby jsou reprezentována pomocí t°ídy PersonalName. Dále je zde informace o pohlaví dané osoby a reference na p°íbuzné osoby (rodi£e a p°ímí potomci). • PersonalName - tato t°ída p°edstavuje datovou strukturu pro uchování informací o v²ech jménech osoby. et¥zec se jménem je vºdy sdruºen v páru s informací o jaké jméno se jedná, tedy zda jde o k°estní jméno, p°íjmení £i jen alias. Dív£í jméno u ºen je z n¥jakého d·vodu v GenXML 3.0 ukládáno odd¥len¥, proto se tohoto úzu drºíme i v aplikaci.
26
KAPITOLA 3.
ANALÝZA
Obrázek 3.7: Diagram t°íd - Balí£ek Model.Assertion
• Personref - t°ída reprezentující vazbu mezi tvrzením a osobou. Samostatná t°ída místo p°ímé reference je pouºita kv·li moºnosti p°i°azovat vazb¥ i sekven£ní £íslo, to ur£uje po°adí tvrzení o osob¥. • PersonrefRole - roz²í°ení t°ídy Personref o informaci o roli, v jaké je daná osoba v·£i tvrzení.
Obrázek 3.8: Diagram t°íd - Balí£ek Model.Person Za práci s objekty z tohoto balí£ku je zodpov¥dná t°ída PersonManager. Podobn¥ jako vý²e zmín¥ná t°ída AssertionManager si i PersonManager uchovává objekty v hashovací map¥, kde jako klí£ slouºí id osoby. T°ída má op¥t i zodpov¥dnost za CRUD operace v£etn¥ generování nových unikátních identikátor·.
3.3.
KONCEPTUÁLNÍ DATOVÝ MODEL
27
Balí£ek Model.Project Balí£ek obsahuje tyto t°ídy: • Address - t°ída reprezentující adresu. Skládá se ze seznamu obsahující místopisné ur£ení (aps), telefonu a faxu. • DateTemplate - t°ída reprezentující n¥jaké £asové vymezení. Podle podt°ídy se pak ur£í, zda jde o jedno konkrétní datum £i del²í £asový úsek. • FileInfo - t°ída s informacemi o pouºité verzi formátu GenXML a jeho úrovni detailu (viz popis formátu). • FromToDate - t°ída reprezentující del²í £asový úsek. Obsahuje po£áte£ní a ukon£ující datum. Jedná se o podt°ídu DateTemplate, je tedy moºné ji pouºít kdekoliv je vyºadována rodi£ovská t°ída. • Header - t°ída se základními informacemi o projektu a souboru v GenXML formátu. Najdeme zde identikaci aplikace (exportingsystem, version) i data vytvo°ení (create) a modikace (change) souboru s daty. D·leºitou sou£ástí jsou i údaje o jazyce (language) a autorovi (owner). • Owner - t°ída obsahující informace o vlastníkovi/autorovi projektu. • SimpleDate - tato t°ída reprezentuje jedno datum. Umoº¬uje specikovat nejen p°esné datum, ale i pouºitý kalendá° a éru (p°ed £i po na²em letopo£tu). Její rodi£ovksou t°ídou je DateTemplate. • Total - t°ída slouºící pro sumarizaci. Obsahuje celkové po£ty jednotlivých element· a umoº¬uje tak nejen rychlý p°ehled, ale i kontrolu, zda nedo²lo p°i na£ítání £i ukládání dat k n¥jakým problém·m. Zodpov¥dnost za práci s t°ídami z Project balí£ku má t°ída ProjectManager. Op¥t se stará o CRUD operace jako je t°eba vytvá°ení nového projektu.
Balí£ek Model.Source V tomto balí£ku se nacházejí tyto t°ídy: • Repository - t°ída reprezentující repozitá° zdroj·. M·ºe jít o knihovnu £i n¥jaký archiv. Obsahuje jméno a kontaktní údaje. • Source - tato t°ída slouºí k zachycení informací o zdroji informací. Specikuje se zde název zdroje, typ (kniha, audio, fotograe,...), autor a podobn¥. Je asociována s repozitá°em. • Excerpt - tato t°ída p°edstavuje datovou reprezentaci úryvku. Úryvek má textovou £ást a referenci na zdroj z n¥hoº pochází. Za práci s t°ídami z tohoto balí£ku je zodpov¥dná t°ída SourceManager.
28
KAPITOLA 3.
ANALÝZA
Obrázek 3.9: Diagram t°íd - Balí£ek Model.Project
3.4
Konceptuální dynamický model
Nyní máme p°edstavu o struktu°e aplikace, poj¤me se podrobn¥ji podívat na chování systému. K popisu dynamiky systému se nej£ast¥ji vyuºívají diagramy aktivit, interakce, stavové a sekven£ní. Nicmén¥ vzhledem k povaze na²í aplikace nás ne v²echny zajímají. Nap°íklad stavové diagramy by nám neprozradily nic nového. ádné entity, se kterými se v aplikaci pracuje, nem¥ní za b¥hu sv·j stav. Projekt, Osoba £i Zdroj prost¥ v jednu chvíli vzniknou a existují aº do jejich smazání £i do konce aplikace. M¥ní se samoz°ejm¥ jejich atributy, ale o skute£né zm¥n¥ stavu není moºné hovo°it.
3.4.1 Diagramy aktivit Podívejme se na n¥jaké typické akce, které mohou p°i práci s aplikací uºivatelé vykonávat. Tyto akce podrobn¥ zmapujeme práv¥ pomocí diagram· aktivit.
P°idání nové osoby: 1. Uºivatel zvolí poloºku menu "Add new Person" 2. Uºivatel vyplní jméno a pohlaví osoby 3. Aplikace vygeneruje nový univerzální identikátor, který osob¥ p°i°adí 4. Aplikace uloºí záznam o nové osob¥ do kolekce
3.4.
KONCEPTUÁLNÍ DYNAMICKÝ MODEL
29
5. Uºivateli se zobrazí v okn¥ detaily nov¥ vytvo°ené osoby 6. Konec aktivity
Obrázek 3.10: Diagram aktivity - Add Person Obdobn¥ probíhají i dal²í aktivity zabývající se p°idáváním zdroj·, událostí atd., proto se jim nebudeme dále v¥novat.
Propojení osob do vztahu 1. Uºivatel zvolí osobu, u které chce vztahem asociovat s jinou osobou 2. Uºivatel zvolí v sekci vztahy zvolí, který vztah chce upravit
• otec • matka • potomci 3. Uºivatel vybere osobu ze seznamu
30
KAPITOLA 3.
ANALÝZA
4. Prob¥hne kontrola, zda se vybraná osoba m·ºe v takovém vztahu k p·vodní osob¥ nacházet
• pokud ano, vztah je vytvo°en • pokud ne, objeví se dialog s upozorn¥ním a úprava vztahu skon£í
Obrázek 3.11: Diagram aktivity - Set up Relationship
3.4.2 Sekven£ní diagramy Pro specikaci interakce v aplikace nelépe poslouºí sekven£ní diagramy. Nejen podle autor· knihy UML2[8] jsou sekven£ní diagramy nejbohat²í a nejpruºn¥j²í formou diagram· interakce. Op¥t se podívejme na p°idávání osoby, tentokrát z pohledu interakce mezi jednotlivými £ástmi systému.
P°idání osoby 1. Uºivatel vyplní základní údaje o osob¥ 2. adi£ komponenty GUI (GUIController) ode²le zprávu s t¥mito údaji na rozhraní komponenty Controller
3.4.
KONCEPTUÁLNÍ DYNAMICKÝ MODEL
31
3. Controller namapuje p°ichozí akci na metodu ve t°íd¥ mající zodpov¥dnost za správu osob (PersonManager) 4. PersonManager vytvo°í novou osobu 5. PersonManager p°idá novou osobu do kolekce 6. Reference na novou osobu putuje zp¥t k objektu GUIController a ta aktualizuje data z modelu a zobrazí uºivateli detail nové osoby
Obrázek 3.12: Sekven£ní diagram - Add person P°edchozí diagram ukazuje interakci typickou i pro ostatní aktivity. Pro ilustraci se podívejme je²t¥ na smazání osoby ze systému. 1. Uºivatel vybere Smazat v detailu osoby 2. GUIController p°epo²le tuto zprávu o akci Controlleru 3. Controller namapuje tuto zprávu na vhodnou metodu v t°íd¥ PersonManager 4. Osoba je z aplikace odstan¥na 5. Jsou smazány v²echny atributy související s osobou 6. Jsou smazány v²echny události související s osobou 7. Jsou smazány v²echny odkazy na osobu ve vztazích, pokud vztah bez dané soby nemá smysl, je vymazán i vztah 8. Zpátky putuje zpráva o úsp¥chu £i neúsp¥chu akce Jak m·ºete vid¥t, logika komunikace v systému z·stává stejná jako u p°edchozího p°íkladu.
32
KAPITOLA 3.
Obrázek 3.13: Sekven£ní diagram - Remove person
ANALÝZA
Kapitola 4
Pouºité technologie Pro implementaci aplikace GENiE byl zvolen funkcionáln¥ objektový jazyk Scala. Jiº n¥kolik let se tomuto jazyku v¥nuji. Protoºe v²ak jazyk Scala nepat°í mezi nejznám¥j²í, dovolte malé shrnutí p°evzaté z mojí bakalá°ské práce[14]: Jak uº bylo zmín¥no v úvodu, p°edstavuje Scala kombinaci objektov¥ orientovaného a funkcionálního programovacího jazyka. Nejedná se o jazyk £ist¥ funkcionální spí²e o hybrid mezi funkcionálním a imperativním, který funkcionální styl nevynucuje, ale pouze doporu£uje. Syntaxe se podobá jazyku Java. Vyniká ov²em stru£ností, podle osobních zku²eností odhaduji zkrácení kódu asi o polovinu. Charakteristiky jazyka Scala jakoºto objektov¥ orientovaného jazyka:
• Staticky typovaný jazyk (s inteligentním odvozováním typ·, které umoº¬uje typovou informaci ve v¥t²in¥ denic vynechat a zkrátit tak kód) • Podpora generických t°íd • Vícenásobná d¥di£nost (s ur£itými omezeními) • Pokro£ilé rozpoznávání vzor· (pattern matching) pomocí case t°íd Charakteristiky jazyka Scala jakoºto funkcionálního jazyka:
• Immutable objekty (jejich pouºívání je doporu£eno, ale ne vynucováno) • S funkcemi je moºné nakládat jako s objekty (posílat jako parametr, vracet jako výsledek jiné funkce) • Anonymní funkce • Podpora uzáv¥r· (closure) • Currying a £áste£n¥ aplikované funkce • Práce se seznamy nejen pomocí funkcí vy²²ího °ádu 33
34
KAPITOLA 4.
POUITÉ TECHNOLOGIE
Seq[Node]
NodeSeq
Node
Elem
Group
Atom
SpecialNode
Comment
Text
Obrázek 4.1: Scala XML - hierarchie t°íd
4.1
Zpracování XML dokument· v jazyce Scala
Podpora práce s formátem XML je v jazyce Scala °e²ena pomocí knihovny scala.xml[13]. Díky snadné roz²i°itelnosti jazyka Scala v²ak p°i práci p°iná²í pocit, jako by ²lo integrální sou£ást jazyka. Obrázek 4.1 ukazuje základní hierarchii t°íd v knihovn¥. Existují i dal²í t°ídy, které se ale pro b¥ºnou práci p°íli² nevyuºijí. Za£n¥me u t°ídy NodeSeq reprezentující jak uº název napovídá sekvenci uzl·. Práv¥ zde je denována v¥t²ina zásadních funkcí pro zpracování XML. Konkrétn¥ nap°íklad "\\" a "\" pro XPath vyhledávání. NodeSeq navíc d¥dí z Seq[_], takºe máme k dispozici i v²echny funkce pro práci s kolekcemi jako t°eba "map". Pon¥kud p°ekvapiv¥ d¥dí z t°ída Node z NodeSeq, takºe se na uzel m·ºeme dívat jako na kolekci obsahující práv¥ jeden prvek - sebe. T°ída Node se pak dále specializuje. Elem reprezentuje jeden XML element. Nový element m·ºeme denovat bu¤ klasicky pomocí konstruktoru
val element = new Elem(null, //prefix "phone", //název new UnprefixedAttribute("where","work", Null), //atributy TopScope, //namespace binding
4.1.
35
ZPRACOVÁNÍ XML DOKUMENT V JAZYCE SCALA
Text("+41 21 693 68 67") )
//potomci elementu
nebo budeme chyt°í a vyuºijeme moºnosti, které nám Scala nabízí
val element =
+41 21 693 68 67 Jednoduché, rychlé, elegantní. Navíc i s kontrolou správnosti. Pokud nebude literál wellformed, kompilátor zahlásí chybu. P°i tomto zp·sobu denování m·ºeme XML tagy snadno kombinovat s ostatním kódem:
def newPhone (where:String, nr:Int) =
{nr} Ve sloºených závorkách m·ºeme mít i rozsáhlý blok kódu s logikou. Výsledná hodnota bloku (v¥t²inou hodnota posledního p°íkazu) se pak doplní do XML elementu. Pokud v dal²í ukázce dostaneme nedenované umíst¥ní telefonu, bude automaticky místo pojmenováno jako "unknown".
def newPhone (where:String, nr:Int) = {nr}
4.1.1 Dotazování nad XML dokumentem Vytvá°ení XML element· máme jiº tedy zmapováno, poj¤me se podívat na zpracování jiº existujících XML dokument·. K dotazování nám slouºí bu¤ jazyk XPath nebo Pattern matching.
XPath Jak je jiº zmín¥no vý²e, máme ve t°íd¥ NodeSeq denované základní funkce
"\\" a "\" pro dotazování nad XML dokumentem. Nejprve dokument na£teme pomocí
funkce denované na objektu (singletonu) XML. Jako p°íklad k dotazování si vezm¥me Shakespearovu hru Hamlet.
val source = XML.loadFile("hamlet.xml") K zji²t¥ní názvu nám sta£í napsat
val title = source\"TITLE" println (title.text ) £ímº získáme textovou hodnotu uzlu vráceného XPath dotazem. Ve skute£nosti se pak vý²e uvedené p°eloºí na oby£ejné volání funkce s parametrem typu String. Podobn¥ je moºné dotazovat se i na atributy, sta£í nám k tomu prex "@".
36
KAPITOLA 4.
POUITÉ TECHNOLOGIE
val personId = source\\"@id" Dotazy m·ºeme potom koncipovat t°eba i podobn¥ jako v XQuery
def findPersonasSpeeches(source:Elem ,name:String) = { for {speech <- (source\\"SPEECH") if ((speech\"SPEAKER").text == name) } yield speech } Není bohuºel v²echno zlato, co se t°pytí. Plnou funkcionalitu XPath bohuºel k dispozici nemáme. Pokud se pokusíme vyhledávat podle hodnoty atributu, narazíme na problémy. P°i pokusu dotazovat se na atribut konkrétního elementu také neusp¥jeme. Nefungují ani pokro£ilej²í konstrukty XPath, u kterých by bylo nutné argument typu String dále parsovat. Nic z následujících dotaz· se tedy nevyhodnotí správn¥.
val person = source\\ "@id=2" val personId = source \\ person \ "@id" val speeches = "//SPEECH[SPEAKER='BERNARDO']" M·ºeme si sice dotázat jinými prost°edky, ale je to prost¥ ²koda. První dotaz na hodnotu atributu m·ºeme realizovat nap°íklad takto
val person = source \\ "@id" find { _.text == "2" }
Pattern Matching Druhou moºností, jak zpracovávat XML p°iná²í rozpoznávání
vzor· neboli Pattern Matching[12]. T°ída Node je totiº case t°ídou a je tedy moºné na její instance pattern matching aplikovat. Ukaºme si na h°e Hamlet jednoduchý p°íklad po£ítající po£et proslov· dané osoby
def matchingPersonasSpeeches(source:NodeSeq, name:String) = { var count = 0 for (speaker <- source\\"SPEAKER") speaker match { case <SPEAKER>{ns} if (ns.text == name) => count = count + 1 case _ => null } count } Kv·li omezené funkcionalit¥ XPath dotaz· v knihovn¥ scala.xml nem·ºeme pouºít jednodu²²í XPath dotaz
count(//SPEECH[SPEAKER='{name}']) a musíme si pomáhat oklikami. Na dal²í problémy narazí zvídavý programátor záhy. Nap°íklad kostrbaté testování na p°ítomnost atribut·
4.1.
ZPRACOVÁNÍ XML DOKUMENT V JAZYCE SCALA
37
x match { case n @ _* if (n \ "@where" text) == "home" => println("home") } P°es zmín¥né nedostatky a zvlá²tní kli£ky, které musí programátor ob£as pouºít, p°iná²í zpracování XML p°íjemný pocit jednoduchosti a intuitivnosti. V naprosté v¥t²in¥ p°ípad· nám tyto základní moºnosti dotazování sta£í. Stejn¥ se ob£as ale nelze ubránit povzdechu, pro£ nedotáhli auto°i implementaci XPath do konce a z·stali n¥kde na p·li cesty.
Svérázné stránky knihovny N¥které proh°e²ky a problémy byly jiº zmín¥ny vý²e, zastavme se ale je²t¥ u jedné nep¥kné vlastnosti knihovny scala.xml. Na rozdíl od zbytku jazyka vykazuje totiº zvlá²tní typovou nekonzistenci. A£koliv se auto°i podle svých slov snaºili vytvo°it immutable reprezentaci XML dokumentu, narazíme v n¥kolika p°ípadech na pouºití mutable ArrayBuer[_]. S pouºitím mutable kolekce ztrácíme vláknovou bezpe£nost. Tato chyba byla na²t¥stí ve verzi 2.8 jiº odstran¥na. P°ekvapivý rozdíl najdeme i mezi dv¥ma denicemi elementu:
val e1 = Elem(null,"elm",Null,TopScope, 4) val e2 = <elm>4 na první pohled stejné elementy se p°i bliº²ím zkoumání v jednom detailu li²í - zatímco v prvním p°ípad¥ je text "4"typu scala.xml.Text v druhém p°ípad¥ je text "4"instancí t°ídy scala.xml.Atom[String]. Tato nekonzistence nám m·ºe vadit nap°íklad pokud máme denovanou n¥jakou vlastní implicitní konverzi
implicit def IntText(i:Int):Text = Text("My int '" + i + "' as text") která se nám tak provede jen v prvním p°ípad¥. Ale to jen tak na okraj jako zajímavost pro zvídavého £tená°e.
4.1.2 Porovnání zpracování XML v jazyce Java a Scala Uº jsme si °ekli, jak jazyk Scala p°istupuje ke zpracování XML dokument·. Poj¤m¥ si podívat,jak si povede ve srovnání s jazykem Java.
Vytvá°ení XML dokumentu Pomi¬me moºnost, ºe budeme ukládat XML dokument
jako prostý text, coº je sice nejrychlej²í, ale nejedná se o skute£nou práci s XML. Jazyk Java nabízí n¥kolik model· pro práci XML daty - SAX, DOM nebo StAX. Zam¥°me se na poslední pravd¥podobn¥ nejmodern¥j²í moºnost, tedy Streaming API for XML[19]. Jedná se o tzv. Pull-Parsing model, kdy si aplikace bere (pull) události od Parseru. Porovnejme si tedy StAX a Scala p°ístup na vygenerování jednoduchého XML dokumentu. Máme na na²í fakult¥ nedostatek d¥v£at, tak si jich pár vygenerujeme. V jazyce Scala to m·ºe vypadat t°eba následovn¥:
38
KAPITOLA 4.
POUITÉ TECHNOLOGIE
def main(args: Array[String]): Unit = { XML.save("example.xml",genXML, "CP1250", true) } def genXML = { <example> {for {i <- 1 to 20000 girl = genGirl(i)} yield girl} } def genGirl(nr : Int) = { Girl{nr} {nr*0.01+20} Town{nr} <street>St.{60000-2*nr} {if (nr/2 == 0) <married>true} } Dvacet tisíc d¥v£at nám trvalo vygenerovat a uloºit 1875 ms (pr·m¥r ze t°í m¥°ení). Kód se nám ve²el na n¥jakých cca 30 °ádk·. A nyní verze pro StAX za pouºití event API:
public static void saveXML() { XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); XMLEventWriter eventWriter = outputFactory .createXMLEventWriter(new FileOutputStream("example.xml")); XMLEventFactory eventFactory = XMLEventFactory.newInstance(); XMLEvent end = eventFactory.createDTD("\n"); StartDocument startDocument = eventFactory.createStartDocument(); eventWriter.add(startDocument); //vytvorim korenovy element StartElement configStartElement = eventFactory .createStartElement("", "", "example"); eventWriter.add(configStartElement); eventWriter.add(end); for (int i = 0; i <= 20000; i++) { createGirl(eventWriter, i); } eventWriter.add(eventFactory.createEndElement("", "", "example")); eventWriter.add(end);
4.1.
ZPRACOVÁNÍ XML DOKUMENT V JAZYCE SCALA
39
eventWriter.add(eventFactory.createEndDocument()); eventWriter.close();
}
public static void createGirl(XMLEventWriter eventWriter, int nr) { XMLEventFactory eventFactory = XMLEventFactory.newInstance(); XMLEvent end = eventFactory.createDTD("\n"); XMLEvent tab = eventFactory.createDTD("\t"); ... // sElement = eventFactory.createStartElement("", "", "girl"); attrib = eventFactory.createAttribute("id", "" + nr); eventWriter.add(tab); eventWriter.add(sElement); eventWriter.add(attrib); eventWriter.add(end); ... } Mnohem rozsáhlej²í kód (cca 130 °ádek) se spoustou zbyte£ného opakování. I doba pot°ebná pro vytvo°ení XML dokumentu byla °ádov¥ vy²²í, celých 21906 ms. V jednoduchosti i rychlosti, tak v tomto p°ípad¥ vít¥zí jazyk Scala.
Dotazování na XML dokumentem Z·sta¬me u na²e známého Shakespearova dra-
matu Hamlet
1
a stanovme si t°i úkoly:
• najít titul • spo£ítat kolikrát promluví postava jménem BERNARDO • vypsat v²echny proslovy této postavy Za£n¥me op¥t p°íkladem v jazyce Scala:
def main(args: Array[String]): Unit = { val source = XML.loadFile("hamlet.xml") println (getTitle(source)) println ("Bernardo has "+matchingPersonasSpeeches(source, "BERNARDO")+" speeches") println ("And his lines are:") // dostaneme zpet List[String], pro vypsani je lepsi zavolat println na kazdy prvek seznamu (findPersonasSpeeches(source, "BERNARDO")).map(println) } def findPersonasSpeeches(source:Elem ,name:String) = { for {speech <- (source\\"SPEECH") 1
velikost souboru 280kB
40
KAPITOLA 4.
}
POUITÉ TECHNOLOGIE
if ((speech\"SPEAKER").text == name) } yield speech.text
def matchingPersonasSpeeches(source:NodeSeq, name:String) = { var count = 0 for (speaker <- source\\"SPEAKER") speaker match { case <SPEAKER>{ns} if (ns.text == name) => count = count + 1 case _ => null } count } def getTitle(source:Elem) = { (source\"TITLE").text } Omezené moºnosti XPath nás nutí pouºívat postupy, které by bylo moºné jinak vyjád°it jedním XPath dotazem. Kód tím ztrácí na srozumitelnosti, p°esto v²ak z·stává pom¥rn¥ stru£ný. Výsledky dostaneme pr·m¥rn¥ za 859 ms. Za pov²imnutí stojí i fakt, ºe jsme se neubránili a pouºili mutable prom¥nnou count ve funkci matchingPersonasSpeeches. V jazyce Java máme k dispozici plnou sílu XPath a m·ºeme toho náleºit¥ vyuºít.
public static void main(String[] args) { DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance(); DocumentBuilder builder; Document doc = null; builder = factory.newDocumentBuilder(); doc = builder.parse("hamlet.xml"); XPathFactory xFactory = XPathFactory.newInstance(); XPath xpath = xFactory.newXPath(); System.out.println(getTitle(doc, xpath)); System.out.println("Bernardo has " +countOfPersonasSpeeches(doc, xpath, "BERNARDO")+" speeches"); System.out.println("And his lines are:"); findPersonasSpeeches(doc, xpath, "BERNARDO"); } public static String getTitle(Document d, XPath xp) throws XPathExpressionException { XPathExpression expr = null; expr = xp.compile("/PLAY/TITLE/text()"); String result = (String) expr.evaluate(d, XPathConstants.STRING); return result; }
4.2.
GUI V JAZYCE SCALA
41
public static int countOfPersonasSpeeches(Document d, XPath xp, String name) throws XPathExpressionException { XPathExpression expr = null; String xpathQuery = "count(//SPEECH[SPEAKER='"+name+"'])"; expr = xp.compile(xpathQuery); Double result = (Double) expr.evaluate(d, XPathConstants.NUMBER); return result.intValue(); } public static void findPersonasSpeeches(Document d, XPath xp, String name) throws XPathExpressionException { XPathExpression expr = null; String xpathQuery = "//SPEECH[SPEAKER='"+name+"']"; expr = xp.compile(xpathQuery); Object result = expr.evaluate(d, XPathConstants.NODESET); NodeList nodes = (NodeList) result; for (int i=0; i<nodes.getLength();i++) System.out.println(nodes.item(i).getTextContent()); } Op¥t znateln¥ del²í kód, v n¥mº se ov²em nemusíme uchylovat k r·zným trik·m a pí²eme £ist¥ XPath dotazy. Doba zpracování oproti p°edchozí verzi je zhruba polovi£ní (468 ms). 4.2
GUI v jazyce Scala
Cílem této práce bylo prozkoumat moºnosti jazyka Scala p°i vývoji pokro£ilé aplikace. Proto i uºivatelské rozhraní, které, p°iznejme si, tvo°í nadpolovi£ní v¥t²inu kódu, musí být naprogramováno v jazyce Scala. V zásadním rozhodnutí, zda aplikaci pojmout jako webovou £i desktopovou, zvít¥zila desktopová varianta. Ne, ºe by jazyk Scala nenabízel podporu pro tvorbu webových aplikací, pro tyto p°ípady existuje framework Lift(http://liftweb.net/). Nicmén¥ p·vodním zám¥rem bylo p°inést lidem aplikaci, kterou by mohli pouºívat sami doma. Webová aplikace by znamenala bu¤ vytvo°it si vlastní server, coº není p°íli² uºivatelsky p°ív¥tivé, nebo zprovozn¥ní na serveru t°etí strany, coº je také v rozporu s domácím pouºitím. Proto padlo kone£né rozhodnutí na desktopovou aplikaci. Pro desktopové aplikace nabízí jazyk Scala vlastní nadstavbu nad knihovnu Swing v jazyce Java. Jmenuje se neorigináln¥ také Swing a v následující kapitole si ji trochu p°iblíºíme.
4.2.1 Filozoe vytvá°ení GUI v jazyce Scala Vytvo°it jednoduchou aplikaci je skute£n¥ snadné. O to sloºit¥j²í je potom p°izp·sobení si standardních prvk· pro vlastní pot°eby, ale o tom aº pozd¥ji. Pro vytvo°ení klasického HelloWorld p°íkladu sta£í denovat nový objekt (singleton) jeº d¥dí z t°ídy SimpleSwingApplication viz následující p°íklad:
42
KAPITOLA 4.
POUITÉ TECHNOLOGIE
import swing._ object HelloWorld extends SimpleSwingApplication { def top = new MainFrame { title = "Hello, World!" contents = new Label("Hello, World!") } } Na²e t°ída musí mít implementovánu metodu top, která vrací Frame, standardní okno s p°ípadnými dekoracemi. V tomto p°ípad¥ vrací top objekt typu Mainframe, který obsahuje tla£ítka pro uzav°ení, minimalizaci a maximalizaci okna. P°i zav°ení tohoto okna se také aplikace automaticky ukon£í. Ve skute£nosti tak vytvá°íme anonymní t°ídu. V bloku uzav°eném v hranatých závorkách pak p°istupujeme k £lenským prom¥nným na²í anonymní t°ídy. P°i spu²t¥ní aplikace je pak zavolána metoda startup, která jako parametr bere výstup metody top. P°idejme nyní oknu i smysluplný obsah. ekn¥me, ºe pot°ebujeme jednoduché dialogové okno, kam budeme zadávat informace o nové osob¥ - jméno, p°íjmení a pohlaví. Po zadání informací m·ºeme informace zahodit (tla£ítko "Cancel") nebo dále vyuºít (tla£ítko "Save"). Pro uspo°ádání prvk· m·ºeme vyuºít panel s vhodným layoutem. Jazyk Scala v tomto p°ípad¥ nabízí standardní sadu známou z jazyka Java. Mimo BorderPanel, který je pouºitý v následující ukázce, m·ºe programátor sáhnout je²t¥ k
• BoxPanel s moºností °adit obsaºené prvky horizontáln¥ £i vertikáln¥ • FlowPanel kde je obsah °azen vertikáln¥ • GridBagPanel s prvky uspo°ádanými v m°íºce, p°i£emº prvky mohou zabírat i více polí£ek m°íºky • GridPanel s prvky uspo°ádanými v m°íºce, p°i£emº kaºdý prvek zabírá práv¥ jedno pole V na²í ukázce sáhneme po BorderPanel:
class val val val
AddPersonWindow(val parent:Component) extends Dialog { firstName = new TextField surName = new TextField sexCombo = new ComboBox(List("Male", "Female", "Unknown"))
val saveBtn("Save") val cancelBtn("Cancel") modal = true title = "Add new Person"
4.2.
GUI V JAZYCE SCALA
43
contents = new BorderPanel { layout(new BoxPanel(Orientation.Vertical) { border = Swing.EmptyBorder(5,5,5,5)
}
contents += new Label("First name:") contents += firstName contents += new Label("Family name:") contents += surName contents += new Label("Sex") contents += sexCombo }) = Center layout(new BoxPanel(Orientation.Horizontal){ border = Swing.EmptyBorder(5,5,5,5) contents += saveBtn contents += cancelBtn }) = South
centerOnScreen open } Aplikace uº vypadá k sv¥tu. Marn¥ by v²ak naivní uºivatel klikal na tla£ítka, funkcionalita prozatím chybí. Pro£ ji tedy zrovna nedodat? V jazyce Java denujeme Listener p°ímo na komponent¥ a °íkáme komponent¥, jak reagovat p°i ur£itých událostech. V jazyce Scala nastavíme dané komponent¥ nejprve, jaké dal²í komponenty má pozorovat. Pozorované komponenty musí být potomky t°ídy Publisher2 . V na²em konkrétním p°ípad¥ p°ikáºeme hlavnímu objektu (oknu) p°íkazem listenTo (component1, component2, . . . ), aby se zajímal o události generované zmín¥nými komponentami. Pokud nastane n¥jaká událost v komponent¥, zavolá se metoda publish(e: Event) a jsou notikovány v²echny naslouchající objekty. Metodu publish mají v²echny t°ídy d¥dící z t°ídy Publisher. Naopak t°ídy naslouchající d¥dí metody listenTo pro naslouchání a deafTo pro ukon£ení naslouchání ze t°ídy Reactor. Objekty d¥dící z t°ídy Reactor obsahují rovn¥º seznam reakcí (potomk· t°ídy Reactions.Reactor). Ony reakce jsou ve skute£nosti pouze alias pro £áste£n¥ aplikovanou funkci typu PartialFunction[Event, Unit]. Nicmén¥ dost teorie, poj¤me si ukázat nastavení reakcí v praxi. Nejd°íve nastavíme naslouchání komponentám:
... listenTo(`saveBtn`, `cancelBtn`) ... 2
Op¥t naráºíme na jazykovou hranici, nejp°esn¥j²ím p°ekladem by snad mohl být vydavatel
44
KAPITOLA 4.
POUITÉ TECHNOLOGIE
a nyní denujeme reakce na události podobn¥ jako denujeme pattern matching:
reactions += { case ButtonClicked(`saveBtn`) => { if (surName.text.isEmpty || firstName.text.isEmpty) { Dialog.showMessage(this, "Fill all fields", "Cannot save person", Dialog.Message.Error) } else { close }
} case ButtonClicked(`cancelBtn`) => { var s = Dialog.showConfirmation(this, "Close window", "Do you want to close window without saving data?", Dialog.Options.OkCancel) if (s == Dialog.Result.Yes) { close } } } Po klí£ovém slov¥ case vkládáme typ události a do závorky komponentu, na které má být tento typ události o£ekáván. Vývojá° m·ºe vyuºít sadu vestav¥ných typ· událostí z balí£ku scala.swing.event nebo si jednodu²e denovat typ vlastní:
case class DataLoadedEvent extends scala.swing.event.Event{ ... } Je pot°eba mít na pam¥ti, ºe musí jít o case t°ídu, aby bylo moºné p°i pattern matching událostí p°istupovat k vnit°ním prom¥nným. Pokud by £tená°e problematika case t°íd a pattern matching zajímala £tená°e detailn¥ji, doporu£uji podívat se na práci Object-oriented pattern matching[12].
4.2.2 Provázání s knihovnou Java Swing Na první pohled se tedy p°ístup k vytvá°ení GUI v jazycích Scala a Java diametráln¥ odli²uje. Zdání ov²em, jak uº tomu £asto bývá, klame. Ve skute£nosti se jedná pouze o pouºití návrhového vzoru Adapter[16] neboli Wrapper v praxi. T°ídy z knihovny scala.swing pouze obalují (proto wrapper) exitující t°ídy z knihovny java.swing a poskytují k nim n¥kdy více n¥kdy mén¥ intuitivní (kaºdopádn¥ v²ak odli²né) rozhraní. P°ímo k tomuto obalenému objektu se m·ºeme dostat p°es prom¥nnou peer. Pokud nap°íklad Wrapper
4.2.
GUI V JAZYCE SCALA
45
t°ída neumoº¬uje p°ístup k n¥jaké d·leºité metod¥ £i atributu ukryté t°ídy, dostaneme se k ní práv¥ p°es prom¥nnou peer. Podívejme se na p°íklad vytvá°ení vlastních dialog·. Na úvod n¥co jednodu²²ího, °ekn¥me, ºe chceme vytvo°it jednoduchý dialog, kam budeme zadávat název nového projektu
val res = Dialog.showInput(newProjectButton, "Enter name of new project", "Start new project", Message.Question,null,List[String](),"") K vytvá°ení dialog· Slouºí singleton Dialog, kde jsou mimo metod dialogy p°ímo vytvá°ejících denované i typy zpráv, odpov¥dí atd. Pouºíváme v tomto p°ípad¥ metodu showInput, protoºe nám jde o získání vstupu od uºivatele. Metod¥ dodáme informace o rodi£ovké komponent¥ (newProjectButton), zprávu (Enter name . . . ), titulek dialogového okna (Start. . . ), typ dialogu (Message.Question), ikonu (v tomto p°ípad¥ máme dialog bez ikony), obsah dialogového okna (k tomuto parametru se je²t¥ vrátíme) a nakonec defaultní hodnotu. Poj¤me se podívat, jak je metoda Dialog.showInput implementovaná. Zdrojový kód objektu Dialog najdeme spolu s implementací t°ídy RichWindow ve stejnojmenném souboru.
def showInput[A](parent: Component = null, message: Any, title: String = uiString("OptionPane.inputDialogTitle"), messageType: Message.Value = Message.Question, icon: Icon = EmptyIcon, entries: Seq[A] = Nil, initial: A): Option[A] = { val e = if (entries.isEmpty) null else entries map toAnyRef toArray val r = JOptionPane.showInputDialog(nullPeer(parent), message, title, messageType.id, Swing.wrapIcon(icon), e, initial) }
toOption[A](r)
V²imn¥te si prosím, jak je nakládáno s p°edposledním parametrem. Pokud voláme v jazyce Java metodu JOptionPane.showInputDialog, dodáváme jí jako p°edposlední parametr bu¤ null - v tom p°ípad¥ se v dialogovém okn¥ vytvo°í pole pro textový vstup - nebo pole hodnot typu String - a v tom p°ípad¥ se v dialogu objeví ComboBox s výb¥rem moºností. V jazyce Scala naproti tomu musíme pro vygenerování textového pole volat metodu Dialog.showInput s prázdným seznamem (£i jinou kolekcí). Pokud zavoláme metodu s parametrem null, jak je zvykem v jazyce Java, skon£í aplikace vyhozením výjimky NullPointerException, kdyº se pokusí v p°íkazu if (entries.isEmpty) zjistit délku
46
KAPITOLA 4.
POUITÉ TECHNOLOGIE
seznamu. Drobný rozdíl, ale pokud se £lov¥k nepodívá p°ímo na implementaci objektu Dialog, bude °e²ení kv·li absenci kvalitní dokumentace hledat jen t¥ºko. Tím se dostáváme ke kameni úrazu nejen jazyka Scala, ale celkov¥ v²ech technologií, které se pohybují mimo hlavní proud zájmu, a tím je práv¥ nedostatek kvalitních zdroj· informací. Nap°íklad p°i vytvá°ení GUI pomocí knihovny scala.swing m·ºe zvídavý £tená° £erpat (mimo odpov¥dí na dotazy na serveru stackoverow) z knih Programming in Scala[23], Steps in Scala[21] a n¥kolika dal²ích £lánk· a tutoriál· (nap° [22] a [10]). Ob¥ knihy se v²ak tvorbou GUI zabývají jen v rámci jedné kapitoly. A knihy i webové tutoriály opakují stále stejné základní v¥ci. P°i °e²ení pokro£ilých problém· nepom·ºe v¥t²inou ani dokumentace API (tzv. scaladoc), protoºe v¥t²in¥ metod a t°íd chybí by´ i základní popis. Vývoj aplikace se tak stává dlouhou sérií nezda°ených pokus· a cest do slepých uli£ek a jen jednou za £as m·ºe vývojá° zvolat "Heuréka, p°i²el jsem na to".3
3
Na rozdíl od pane Archimeda, programáto°i málokdy pracují ve van¥, takºe pobu°ování ve°ejnosti
na²t¥stí v tomto p°ípad¥ nehrozí
Kapitola 5
Návrh V kapitole Analýza jsme se snaºili o vytvo°ení model· nezávislých na platform¥ (PIM - platform independent model). Nyní je na £ase sestoupit níº a p°iblíºit se skute£né platform¥. V tomto p°ípad¥ se chystáme vyvíjet aplikaci v jazyce Scala. Proto budeme analytický model zp°es¬ovat práv¥ tak, aby výsledek odpovídal tomuto jazyku. Samotný návrh vznikal nikoliv najednou ale postupn¥ podle jednotlivých iterací (viz kapitolu Implementace). Díky rozd¥lení do men²ích celk·, bylo moºné navrhnout pom¥rn¥ p°esný model, který jiº v pr·b¥hu implementace nebylo nutné p°íli² m¥nit. Následující sekce tedy p°edstavuje pohled na celek, který iterativn¥ vznikal v pr·b¥hu práce na aplikaci. 5.1
Logický datový model
Jazyk Scala je jazykem objektov¥ orientovaným, a proto m·ºeme p°eklopit konceptuální datový model do modelu t°íd. Co se rozhraní tý£e, nabízí Scala mnohem univerzáln¥j²í konstrukt a to Trait. Trait je pádnou odpov¥dí na problémy se znovupouºíváním kódu (onen známý princip DRY1 ). Ve zkratce se jedná o konstrukt podobný rozhraní, tak jak ho známe z prost°edí jazyka Java, který m·ºe obsahovat i funkcionalitu. T°ídy navíc mohou d¥dit od n¥kolika konstrukt· Trait pomocí tzv. mix-in skládání[17]. Máme tak p°i vývoji k dispozici vícenásobnou d¥di£nost. T°ídy v následujících diagramech budou tedy standardní t°ídy jazyka Scala a místo rozhraní budou pouºity konstrukty Trait.
5.1.1 Komponenta Model Oproti analytickým t°ídám do²lo k dopln¥ní metod v£etn¥ parametr· a návratových typ·. Názvy v¥t²iny metod jsou sestaveny tak, aby co nejlépe odpovídaly tomu, co metoda skute£n¥ d¥lá. V¥t²ina metod obstarává CRUD operace. Uve¤me si p°íklad pro t°ídu Attribute:
getNewAttributeId - vygeneruje nový unikátní identikátor pro nov¥ vytvá°ený atribut addAttribute(attribute) - p°idá instanci t°ídy Attribute do kolekce 1
Don't repeat yourself
47
48
KAPITOLA 5.
NÁVRH
createNewAttribute(description, typeOfAttribute, classOfAttribute, text, personId) vytvo°í novou instanci t°ídy Attribute podle zadaných parametr· a vrátí na ni referenci ndAttributesByPerson(personId) - vrátí seznam instancí t°ídy Attribute asociovaných s osobou s daným id nebo prázdný seznam deleteAttribute(attributeId) - odstraní z kolekce instanci t°ídy Attribute s daným
identikátorem
Pak jsou tu ov²em ur£ité specické metody, které mezi p°edchozí nezapadají a o nichº bychom se m¥li zmínit blíºe.
generateBasicEventTypes - protoºe formát GenXML nemá standardn¥ denované ºádné druhy událostí, je t°eba je p°i vytvo°ení nového projektu nebo importu z GEDCOM formátu vygenerovat. Metoda generateBasicEventTypes tedy p°ipraví tyto základní druhy událostí: narození, úmrtí, svatba, ostatní. createFamilyRelationships(fatherId,motherId, List[childrenId]) - vytvo°í vztahy (in-
stance t°ídy Relationship) mezi kaºdým z d¥tí a jeho rodi£i
5.1.2 Komponenta GUI Obecn¥ hovo°í o tvorb¥ uºivatelského rozhraní v jazyce Scala p°edchozí kapitola. Nyní se poj¤me podívat, jak konkrétn¥ se v aplikaci GENiE s vý²e popsanými konstrukty pracuje. Uºivatelské rozhraní bude vyuºívat okno se t°emi základním záloºkami - osoby (Persons), rodinný strom (Family tree) a zdroje (Sources). Na záloºce Persons najdou uºivatelé seznam osob a detail jednotlivých osob. Na dal²í záloºce Family Tree se bude nacházet seznam osob a rodinný strom dané osoby. Poslední záloºka pak bude slouºit k práci s repozitá°i, zdroji a úryvky. Základní uspo°ádání t°íd znázor¬uje diagram 5.1. Ve²keré komponenty2 jsou vkládány do objektu MainWindow typu SimpleSwingApplication, díky tomu se nemusíme starat o podrobnou inicializaci grackého rozhraní a sta£í pouze implementovat metodu top. Tento objekt také p°i startu inicializuje Controller (t°ída Core) a °adi£ uºivatelského rozhraní (t°ída GUIController). Hlavní okno je dekorováno menu vytvo°eným pomocí instance t°ídy typu MenuBar. D·leºitou t°ídou komponenty GUI je °adi£ (controller) GUIController. Tato t°ída jako jediná komunikuje s komponentami Model a Core. Poskytuje tak pro ostatní t°ídy rozhraní pro získávání a úpravy dat. GUIController je ve skute£nosti pouze trait s abstraktními metodami, slouºí tedy jen jako rozhraní. Implementaci t¥chto metod najdeme aº ve t°íd¥ GenieGuiController. 2
Op¥t tu máme malé zmatení pojm·. Komponenta v tomto p°ípad¥ znamená komponentu uºivatel-
ského rozhraní. P°edtím jsme tento pojem pouºívali pro komponentu jakoºto £ást aplikace obsahující jednotlivé t°ídy. Podle kontextu v textu by m¥lo být vºdy jasné, o který význam se jedná.
5.1.
49
LOGICKÝ DATOVÝ MODEL
Obrázek 5.1: Návrhové t°ídy - komponenta GUI
Komunikace mezi komponentami Jedním z nejv¥t²ích problém· p°i tvorb¥ uºivatelského rozhraní bývá vy°e²ení komunikace mezi jednotlivými komponentami uºivatelského rozhraní. První pokusy s p°edáváním referencí na rodi£ovskou komponentu nebo dokonce na hlavní okno se ukázaly jako neexibilní. V jednodu²²ích p°ípadech pouhé p°edávání reference sta£í, ale pokud pot°ebujeme volat instanci t°ídy jeº není p°ímým rodi£em, je t°eba sáhnout k jinému mechanismu, který jazyk Scala nabízí - publikování událostí. Balí£ek genie.gui.events tak obsahuje uºivatelské události typu m·ºeme v aplikaci dále pracovat.
Event, se kterými
• Po na£tení dat je publikována v instanci t°ídy GUIController událost typu DataLoadedEvent a naslouchající hlavní okno zareaguje vytvo°ením nových komponent. • P°i p°idání nebo smazání osoby se publikuje událost PersonAddedEvent respektive PersonDeletedEvent, aby do²lo k obnovení seznamu osob. • Se zpracováním vyjímek má zase pomáhat událost
ExceptionEvent.
50
KAPITOLA 5.
NÁVRH
Aplikace také £asto vyuºívá modálních dialog·, které najdete v balí£ku gui.dialogs. Tyto uºivatelské dialogy slouºí k zadání dat pro vytvo°ení nové instance (AddAttributeWindow, AddPersonWindow,...) nebo k výb¥ru n¥jaké instance ze seznamu (ChooseExcerpt,...).
Gracká reprezentace rodinných strom· T°ídy pro tvorbu rodinných strom· se nalézají v balí£ku gui.tree. Jako plátno pro zobrazení stromu poslouºí vlastní komponenta s vhodn¥ implementovanou metodou paintComponet. Jednotlivé uzly stromu budou tvo°eny pomocí instancí t°ídy PersonNode, které budou obsahovat odkaz na své potomky.
5.1.3 Komponenta Controller Komponenta Controller slouºí p°edev²ím pro komunikaci mezi komponentami Model a GUI. V n¥kterých p°ípadech p°edává dotazy p°ímo do komponenty Model, v jiných p°ípadech obsahuje i p°idanou hodnotu v podob¥ up°esn¥ní a dal²ích instrukcí.
Obrázek 5.2: Návrhové t°ídy - komponenta Controller
Filtry Dobrý p°ípadem takovéto p°idané hodnoty jsou ltry. Vyhledávání a ltrování záznam· na vy²²í úrovni neº jen podle unikátního identikátoru má na starosti mechanismus ltr·. V balí£ku core.lters existuje generický trait GenericFilter[T] s jedinou metodou applyFilter(list: List[T]):List[T]. Tato metoda bere jako parametr seznam objekt· typu T a vrací op¥t seznam typu T, nyní uº ov²em proltrovaný podle konkrétní implementace této metody.
5.1.
LOGICKÝ DATOVÝ MODEL
51
Jako p°íklad si uve¤me typické vyhledávání osob podle jména. Tuto funk£nost poskytuje PersonNameFilter. Implementace metody applyFilter vyuºívá existující t°ídní metody ze t°ídy Person hasSimiliarName. Takto vytvo°ené ltry je moºné za sebou °et¥zit a dosáhnout tak kýºených výsledk·.
Import/Export Komponenta Controller vyuºívá t°ídy pro import a export dat a slouºí tak k nahrávání respektive ukládání dat. T°ídy pro import dat najdeme v balí£ku loader. Kaºdá komponenta pro nahrávání dat komunikuje s Controllerem pomocí rozhraní s dv¥ základními metodami: checkFile, která zkontroluje, zda soubor skute£n¥ obsahuje vhodná data, a loadData, která vrátí nahraná data ve form¥ instance t°ídy ProjectData. Prozatím se p°edpokládají dv¥ moºnosti nahrání dat do aplikace a to:
• ze souboru ve formátu GenXML Aplikace je s formátem GenXML úzce svázána. Zdrojový XML soubor je prohledáván pomocí XPath výraz· a nalezená data p°etavena do instancí t°íd z komponenty Model k tomuto ú£elu ur£ených.
• ze souboru ve formátu GEDCOM Provázání s formátem GEDCOM se p°edpokládá voln¥j²í. Protoºe se jedná o textov¥ orientovaný formát, bude pro jeho zpracování nejlep²í vyuºít regulárních výraz·. Také kontruktory t°íd z komponenty Model uº by se nem¥ly volat p°ímo, ale pomocí konstruk£ních metod denovaných v managerech. Export dat pak budou zaji²´ovat t°ídy z balí£ku exporters. Op¥t tu máme rozhraní s n¥kolika základními metodami: getOutputFile a setOutputFile se starají o nastavení respektive vrácení aktuálního výstupního souboru a metoda exportToFile obstará samotný výstup do souboru. Také v tomto p°ípad¥ po£ítáme s dv¥ma r·znými moºnostmi exportu dat a to:
• export do souboru v GenXML formátu T°ídy v komponent¥ Model obsahující data budou mít implementovánu metodu toXML, která p°evede data uschovaná v dané t°íd¥ do XML struktury odpovídající formátu GenXML.
• export do HTML souboru Podobn¥ jako v p°edchozím p°ípad¥ budou mít n¥které t°ídy z komponenty Model implementovánu funkci pro export do XHTML. Principieln¥ jde op¥t o generování XML element·. Tentokrát se v²ak snaºíme dosáhnout toho, aby byl výstup p°izp·sobený lidskému vnímání. Proto vynecháme zbyte£né podrobnosti a budeme informace kumulovat okolo osob s nimiº jsou provázány.
52
KAPITOLA 5.
NÁVRH
Kapitola 6
Implementace Samotná implementa£ní fáze je rozloºena na n¥kolik men²ích iterací. Kaºdá iterace má své jasn¥ denované cíle, které se na konci ov¥°ují testováním. Vyuºívají se tu výhody iterativního a inkrementa£ního modelu[20], který nabízí moºnost vytvá°et aplikaci po men²ích stavebních blocích. Ani vý²e popsaná analýza a návrh nevznikly najednou, jak by tomu m¥lo být nap°. u metodiky Vodopád[9], ale byly postupn¥ p°idávány (p°ípadn¥ up°es¬ovány), jakmile zapo£aly práce na dané funkcionalit¥. Plán implementace vznikl hned po první £ásti analýzy tedy po prozkoumání dané problematiky a ur£ení p°ípad· uºití. Rozd¥lení aplikace do komponent napomohlo zdárné aplikaci iterativního modelu. Proto pozorného £tená°e jist¥ nep°ekvapí, ºe funkcionalita komponent a funkcionalita implementovaná v jednotlivých iteracích se £asto p°ekrývá. Pro implementaci bylo vyuºito vývojové prost°edí Netbeans dopln¥né o Scala plug-in podporující zvýrazn¥ní Scala syntaxe a dal²í uºite£né úkony.
6.1
První iterace
6.1.1 Cíle • vytvo°ení projektu v jazyce Java
p°íprava entit (t°íd s daty) na£ítání dat pomocí StAX testování • vytvo°ení projektu v jazyce Scala
p°íprava entit na£ítání dat pomocí Scala XML knihovny testování 53
54
KAPITOLA 6.
IMPLEMENTACE
6.1.2 Pr·b¥h iterace První iterace za£ala drobným úkrokem stranou a to tvorbou komponenty, jeº se nestane sou£ástí nální aplikace. Implementace na£ítání formátu GenXML v jazyce Java pomocí rozhraní StAX nebyla ztrátou £asu, p°estoºe se ukázala jako velmi £asov¥ náro£ná. lo p°edev²ím o srovnání tohoto p°ístupu s p°ístupem, jaký p°iná²í knihovna scala.xml. Na rozdíl od popisu StAX v kapitole o technologiích, kde se testuje na smy²leném p°íkladu, tady ²lo o reálné pouºití. Práce na StAX verzi na£ítání GenXML poslouºila i pro vytvá°ení nahrávání v jazyce Scala, protoºe jsem byl podrobn¥ seznámen s formátem GenXML a vytvo°ení sady entit ²lo mnohem rychleji. P°i vytvá°ení entit v jazyce Scala bylo bohuºel uº nutné obejít se bez automatického generování kódu, jeº by urychlilo nap°. vytvá°ení metod toString a equals. Bohuºel plug-in pro Netbeans nic podobného neumoº¬uje a, jak bylo v pr·b¥hu práce zji²t¥no, trpí vysokou chybovostí. P°i del²í práci je tedy nutné parser (analyzátor)1 kontrolující syntaxi n¥kolikrát restartovat, aby se vývojá° vyhnul chybné interpretaci psaného kódu.
6.1.3 Zajímavé poznatky P°íjemných p°ekvapením byl p°ístup, jakým se jazyk Scala potýká s tvorbou konstruktor·. Podívejme se na následující p°íklad:
class Person(var id:String,var sex:String,var personalName:PersonalName, var excerptref:List[String] = null, var objects:List[String] = null, var note:String = "",var change:Date = null) Taková to denice t°ídy nám °íká, ºe
• t°ída Person obsahuje sedm atribut· • hodnoty t¥chto atribut· se dají zm¥nit (to má na sv¥domí klí£ové slovo var, pokud bychom cht¥li atributy nem¥nné, pouºili bychom val ). Pro v²echny tyto atributy uvozené klí£ovým slovem var budou vytvo°eny metody pro zji²t¥ní hodnoty (stejného jména jako atribut) a metody pro zm¥nu hodnoty (ve tvaru jmeno_atributu_=). Ru£ní £i poloautomatické generování tzv. getter a setter metod tak odpadá. • tyto atributy budou mít viditelnost public • ty atributy, které mají po své denici i p°i°azenou n¥jakou hodnotu, dostanou, pokud nebude p°i volání konstruktoru up°esn¥no jinak, p°i°azenou práv¥ tuto hodnotu Takto denovanou t°ídu je moºné zavolat: 1. zadáním v²ech poºadovaných parametr· (tedy id, sex a personalName) 1
tento odborný termín op¥t není snadné p°eloºit do na²eho mate°ského jazyka, termín analyzátor se
asi nejvíce blíºí významu tohoto slova
6.2.
DRUHÁ ITERACE
55
new Person("P1","male",new PersonalName( List(("surn","Jara"),("unkw","Cimrman")))) 2. konkrétním vý£tem parametr· a jejich hodnot
new Persons(id ="P1", sex = "male", personalName = new PersonalName( List(("surn","Jara"),("unkw","Cimrman"))), change = new Date )
Pokud pot°ebujeme denovat sekundární konstruktor, ani v tomto p°ípad¥ nenarazíme na problém:
def this() = this("P1","male",new PersonalName( List(("surn","Jara"),("unkw","Cimrman"))))
6.1.4 Záv¥r první iterace I kdyº se StAX v rámci jazyka Java prezentuje jako jednoduché API [6], oproti jazyku Scala se stále jedná o neintuitivní °e²ení. Vestav¥ná podpora XPath a dal²í funkce d¥lají z knihovny Scala.xml efektivní nástroj ke zpracování XML dokument·.
6.2
Druhá iterace
6.2.1 Cíle • p°íprava základních t°íd v komponentách Controller a GUI • vytvo°ení hlavního okna, menu, panelu se záloºkami, tabulky s osobami a panelu pro detaily o osob¥
6.2.2 Pr·b¥h iterace V této iteraci do²lo k prvnímu velkému setkání s tvorbou uºivatelského rozhraní v jazyce Scala. Podrobn¥ji se této problematice v¥nuje sekce GUI v jazyce Scala, proto se na rozdíl od p°edchozí iterace omezíme jen na pár stru£ných poznámek. Cílem této iterace bylo vytvo°it okno, kde se po na£tení dat z .gml souboru (na£ítání jsme si p°ipravili v minulé iteraci) zobrazí tabulka osob, a po kliknutí na jméno osoby se vedle ukáºí detaily této osoby. Bylo zapot°ebí vytvo°it vlastní podt°ídu TableModel, která se stará o provázání dat s grackým rozhraním tabulky. Na²t¥stí bylo moºné tohoto dosáhnout analogicky jako v jazyce Java implementací základních metod (getValueAt, addRow, . . . ).
56
KAPITOLA 6.
IMPLEMENTACE
6.2.3 Zajímavé poznatky Tentokrát se nejedná ani tolik o poznatek jako o malou ukázku, jak je moºné p°istupovat k programování v jazyce Scala funkcionáln¥. Zastavme se nejd°íve u metody fold(Left|Right), která je vyuºita pro zkoumání podobnosti jmen v metod¥ isSimiliar t°ídy PersonalName:
var nps:List[Pair[String,String]] def isSimiliar(_name:String):Boolean = { val name = _name.toLowerCase if (name.contains(" ")) { val arr = name.split(" ") // (false) is first parametr, it's supplied for a in first iteration // b is the current head item in list // a is parameter passed during folding arr.foldLeft(false)( (a,b) => (a || nps.foldLeft(false)( _ || _._2.toLowerCase.contains(b)))) } else nps.foldLeft(false)( _ || _._2.toLowerCase.contains(name)) } Parametr (hledané jméno), který dostáváme, m·ºe obsahovat jedno jméno nebo n¥kolik jmen odd¥lených mezerou, proto ta podmínka a p°ípadné rozd¥lení na n¥kolik díl£ích jmen. Pokud hledáme jen jedno jméno, zavoláme na seznamu pár· [druh jména, jméno] metodu foldLeft s po£áte£ní hodnotou "false"(jméno neodpovídá) a funkcí, která zjistí, zda práv¥ testovaná poloºka seznamu odpovídá hledanému jménu. Metoda foldLeft bere zleva prvky seznamu, aplikuje na n¥ tuto funkci a její výsledek p°edá dál. Tuto metodu pak vyuºívá nap°íklad vyhledávání osob ve t°íd¥ PersonManager. Tady p°ijde ke slovu pro zm¥nu koncová rekurze. Nejd°íve zavoláme vyhledávající funkci, která p°ipraví data a p°epo²le je své rekurzivní variant¥.
def findPersonByName(name:String):List[Person] = { val pList = persons.values.toList findPersonByNameRecursion(name, pList, List[Person]()) } private def findPersonByNameRecursion(name:String, persons:List[Person], result:List[Person]):List[Person] = { persons match { case Nil => result case List(a,_*) => if (a.hasSimiliarName(name)) findPersonByNameRecursion(name, persons.tail, a::result) else findPersonByNameRecursion(name, persons.tail, result) } }
6.3.
TETÍ ITERACE
57
Metoda hasSimiliarName t°ídy Person pouze p°epo²le dotaz instanci t°ídy PersonalName, aby nedocházelo k proh°e²k·m proti zapouzd°ení t°íd.
6.2.4 Záv¥r druhé iterace Druhá iterace se nesla p°edev²ím v duchu potýkání se s nep°íli² dob°e dokumentovanou knihovnou Scala Swing. Po del²í dob¥ £lov¥k sice dokáºe proniknout do funkcí této knihovny, ale i tak se stále potýká s drobnými syntaktickými odli²nostmi od knihovny Java Swing, které zdrºují a demotivují. 6.3
T°etí iterace
6.3.1 Cíle • export dat zp¥t do GenXML formátu
implementace toXML funkcí u v²ech entit v modelu vytvo°ení t°ídy zodpov¥dné za export testování
6.3.2 Pr·b¥h iterace Ve t°etí iteraci se op¥t vracíme k práci s knihovnou Scala.xml. V kaºdé entit¥ byla implementována metoda toXML, jejímº výstupem byl objekt typu Node reprezentující XML element. Díky moºnosti kombinovat XML elementy a Scala kód postupovaly práce na této iteraci rychle kup°edu.
6.3.3 Zajímavé poznatky Problém nastal aº p°i pokusu denovat XML element, který obsahuje nepovinný atribut nap°íklad tak, jak je to denováno podle následujícího fragmentu DTD.
Pokud by ²lo a atribut povinný, sta£ilo by vytvo°it nový element takto (v£etn¥ p°i°azení hodnot z t°ídních atribut·):
def toXML = { {this.personid} }
58
KAPITOLA 6.
IMPLEMENTACE
P°i nepovinném atributu se logicky nabízí takovýto zápis:
def toXML = { ... } nebo je²t¥:
def toXML = { {if (pref != "") else } ... } Bohuºel ani jedna z t¥chto moºností nefunguje, vývojá° se v tomto p°ípad¥ musí obejít bez syntaktických zjednodu²ení a zavoláním konstruktoru vytvo°it novou instanci t°ídy Elem, která je potomkem t°ídy Node. (pozn. p°edchozí metody toXML by místo objektu typu Node mohly vracet i objekty typu Elem, ale pro v¥t²í obecnost byl zvolen Node).
def toXML = if (pref != "") new Elem (null, "personref", new UnprefixedAttribute("pref", Text(this.pref), Null), TopScope, Elem(null, "personid", Null, TopScope, Text(this.personid))) else new Elem (null, "personref", null, TopScope, Elem(null, "personid", Null, TopScope, Text(this.personid)))
6.3.4 Záv¥r t°etí iterace Knihovna scala.xml nabízí p°i vytvá°ení nových XML element· intuitivní p°ístup, i p°es vý²e zmín¥né omezení a n¥které dal²í drobnosti, které najdete popsány v kapitole Zpracování XML dokument· v jazyce Scala. 6.4
tvrtá iterace
6.4.1 Cíle • Vytvá°ení entit pomocí továrních metod
vytvo°ení nového projektu vytvá°ení nových osob vytvá°ení vztah·, událostí a atribut· testování
6.4.
TVRTÁ ITERACE
59
6.4.2 Pr·b¥h iterace V první iteraci vytvo°ené t°ídy pro nahrávání dat v GenXML formátu vyuºívají p°ímo volání konstruktoru pro vytvá°ení nových instancí entit. Tento postup je pochopitelný díky t¥snému propojení aplikace GENiE s GenXML formátem. Aby v²ak byl napln¥n poºadavek na volné vazby mezi moduly, bylo t°eba vytvo°it rozhraní a implementovat metody, které by umoº¬ovaly provád¥t CRUD (Create, Read, Update, Delete) operace.
6.4.3 Zajímavé poznatky P°i operacemi s mapami narazí vývojá° na zajímavý fakt - metody pro vyhledávání vracejí objekt typu Option[(A,B)]. Tento mechanismus má za úkol chránit systém proti problém·m s Null Pointer Exception (zajímav¥ o tom pojednává tento blog[18]). Kdyº hledáme prvek v map¥, nem·ºeme si být jisti, zda tam bude nalezen, proto funkce get(key : A) :Option[B] (vrací hodnotu asociovanou s klí£em, pokud existuje) a nd (p: ((A, B)) ⇒ Boolean) :Option[(A,B)] (vrací první prvek mapy spl¬ující predikát, pokud existuje) mají uvedenou návratovou hodnotu typu Option ,Moºnost. Výsledek typu Option[A] se dá zpracovat pomocí pattern matching:
val etype = eventTypes.find(_._2.classOfEvent == "birth") val eId = etype match { case Some(x) => x._2.id case None => "E0" }
V p°íkladu naho°e hledáme v map¥ id objektu typu EventType, u n¥hoº je atribut classOfEvent roven "birth", tedy lid²t¥ji °e£eno id typu události zna£ící narození osoby. Funkce nd nám vrátí bu¤ nalezený prvek, v tom p°ípad¥ nám vrátila n¥co (Some) s £ím m·ºeme pracovat nebo se nevrátilo nic d·leºitého (None) a my podle toho musíme pat°i£n¥ zareagovat. Jak správn¥ upozor¬uje tento blog[5], bylo by moºné zápis zkrátit takto:
val eId = eventTypes.find(_._2.classOfEvent == "birth").map(_.2).getOr("E0") Coº vede ke krat²ímu kódu, jeº ov²em uº není tak jednoduché pochopit na první pohled.
6.4.4 Záv¥r £tvrté iterace Nyní máme p°ipravené metody pro práci s daty, aniº bychom znali podrobnosti o uloºení dat. K modulu Model tak m·ºeme p°istupovat jako k £erné sk°í¬ce, jeº nám poskytuje viditelné rozhraní, ale implementa£ní detaily z·stávají skryty. Plody £tvrté iterace hned vyuºijeme pro import dat z GEDCOM formátu, o kterém pojednává iterace následující.
60
KAPITOLA 6.
6.5
IMPLEMENTACE
Pátá iterace
6.5.1 Cíle • na£ítání dat ze souboru ve formátu GEDCOM
na£tení základních informací o osobách na£tení informací o vztazích testování
6.5.2 Pr·b¥h iterace Formát GEDCOM p°edstavuje standard pro vým¥nu genealogických dat, nem·ºeme si ho proto dovolit pominout. Protoºe se jedná o formát vyuºívající prostý text, vyuºijí se pro jeho zpracování regulární výrazy. GEDCOM obsahuje mnoho rozli£ných moºností, implementovat kompletní p°evod by bylo £asov¥ velmi náro£né. as strávený na implementaci detailního konvertoru GEDCOM/GENiE by poté chyb¥l u tvorby jiné funkcionality. Proto se omezíme pouze na na£ítání základních informací o osobách (jméno, pohlaví, narození, úmrtí) a vztah· mezi nimi. T°ída GedcomLoader tak pokrývá klí£ové oblasti dat, o které jde b¥ºnému uºivateli p°edev²ím.
6.5.3 Zajímavé poznatky Pro zpracování textových soubor· s daty ve formátu GEDCOM poslouºily regulární výrazy. Jazyk Scala umoº¬uje pracovat s regulárními výrazy op¥t pomocí pattern matching. Nejd°íve je t°eba denovat si poºadovaný regulární výraz. ekn¥me, ºe hledáme identikátor osoby, který má v GEDCOM formátu tvar:
0 INDI Jednoduchý regulární výraz, který by nám umoºnil získat onen identikátor, by mohl vypadat nap°íklad takto:
0 (.*) INDI Nyní pot°ebujeme vytvo°it prom¥nnou s tímto regulárním výrazem, abychom s ní mohli dále pracovat. K tomu nám sta£í na n¥jaké instanci t°ídy String zavolat metodu r (z t°ídy RichString ), která textový °et¥zec p°etvo°í na regulární výraz (instanci t°ídy scala.util.matching.Regex).
val personReg = "0 (.*) INDI.*".r Takto získanou instanci m·ºeme poté vyuºít p°i hledání v textovém souboru. Nejd°íve na£teme soubor a jeho °ádky uloºíme do seznamu. Poté budeme procházet soubor, a pokud narazíme na °ádek odpovídající na²emu regulárnímu výrazu, vypí²eme identikátor osoby.
6.5.
PÁTÁ ITERACE
61
var source = Source.fromFile(file).getLines.toList while (!source.isEmpty) { source.head match { case personReg(x) => { println("Person wit id"+x) source = source.tail } case _ => source = source.tail } P°íklad naho°e p°edstavuje velmi jednoduchý dotaz, m·ºeme samoz°ejm¥ zkusit i n¥co sloºit¥j²ího. Dal²í regulární výraz uº vyuºívá i metaznaky a omezení rozsahu hodnot.
val sexReg = "^([0-9]) SEX ([A-Z]).*".r M·ºeme pouºít i t°ídy pro rozpoznávání £ísel (\d), slov(\w) a mezer(\s).
val sexReg = "^(\\d+\\s)SEX(\\s\\w+).*".r Abychom se zbavili dvojitého zp¥tného lomítka, pouºijeme "raw string", °et¥zec, ve kterém se neberou v úvahu speciální znaky jako od°ádkování a podobné. Pro vytvo°ení "raw string"nám sta£í pouºít trojité uvozovky jako v následujícím p°ípad¥.
val sexReg = """^(\d+\s)SEX(\s\w+).*""".r Na záv¥r se pro porovnání podívejme, jak by se s podobným regulárním výrazem pracovalo v jazyce Java:
Pattern sexParser = Pattern.compile("^(\\d+\\s)SEX(\\s\\w+).*"); String s = "1 SEX MALE"; Matcher m = sexParser.matcher(s); if (m.matches()) { String level = m.group(1); String sex = m.group(2); ... }
6.5.4 Záv¥r páté iterace Pátá iterace nás seznámila s pouºíváním regulárních výraz· v jazyce Scala. Díky nim byla aplikace obohacena o jednoduché na£ítání informací ze soubor· ve formátu GEDCOM.
62
KAPITOLA 6.
6.6
IMPLEMENTACE
está iterace
6.6.1 Cíle • roz²í°ení uºivatelského rozhraní o
panel s atributy asociovanými s vybranou osobou panel s událostmi asociovanými s vybranou osobou dialogová okna pro p°idávání atribut· a událostí moºnost mazat atributy a události okno pro p°idání nové osoby
6.6.2 Pr·b¥h iterace V ²esté iteraci se op¥t zam¥°íme na práci s uºivatelským rozhraním. Doposud jsme byli schopní pouze pasivn¥ zobrazit seznam osob. Nyní p°i²el £as roz²í°it aplikaci o moºnost editace osob p°es gracké rozhraní aplikace.
6.6.3 Zajímavé poznatky Uºivatelské komponenty Jazyk Scala poskytuje základní konstrukty pro tvorbu uºi-
vatelského rozhraní, nicmén¥ tyto se hodí pouze pro základní situace a pro pot°eby na²í aplikace bylo t°eba je roz²í°it. Na²t¥stí mechanismus p°izp·sobení komponent uºivatelského rozhraní se velmi podobá známým postup·m z jazyka Java. Pokud tedy chceme p°izp·sobit model tabulky pro konkrétní t°ídu, v na²em p°ípad¥ nap°íklad pro objekty t°ídy Attribute, sta£í vytvo°it novou komponentu roz²í°ením t°ídy AbstractTableModel. V ní pak implemtujeme p°edev²ím základní metodu getValueAt :
class AtributeTableModel(var rowData: Array[Attribute], val columnNames: Seq[String] ) extends AbstractTableModel { ... def getValueAt( row: Int, col: Int): String = rowData(row).getAttributeType match { case "text" => rowData(row).textclass + ": "+rowData(row).text case "number" => rowData(row).numberclass + ": " + rowData(row).number case "flag" => rowData(row).flagclass } ... }
Publikování událostí Dal²ím d·leºitým aspektem práce s uºivatelským rozhraním je komunikace mezi jednotlivými komponentami. P°ímé vazby a volání metod by komponenty zbyte£n¥ pevn¥ svazovaly k sob¥. Jazyk Scala proto obsahuje svoji odpov¥¤ na Listener známý z jazyka Java, a to publikování událostí. V následujícím p°ípad¥ si ukáºeme, jak rodi£ovská komponenta (panel se t°emi dal²ími panely uvnit°), naslouchá uºivatelsky denované události TableSelectionChangedEvent. Nejprve denujeme case t°ídu (aby ji bylo moºné pouºít v pattern matching):
6.6.
ESTÁ ITERACE
63
case class TableSelectionChangedEvent(source:Component, obj:Any) extends scala.swing.event.Event{ override def toString = "Table selection changed event" } Metoda toString by ani znovu implementována být nemusela (u case t°íd se základní implementace vytvo°í automaticky), ale dáváme v tomto p°ípad¥ p°ednost vlastní podob¥. Nyní m·ºeme v tabulce publikovat událost:
publish (new TableSelectionChangedEvent(this,repositories.head)) a v rodi£ovské komponent¥ ji odchytnout a pat°i£n¥ zareagovat:
listenTo(`repoPanel`,`srcPanel`) reactions+= { case TableSelectionChangedEvent(s:RepoPanel,a:Repository) => { srcPanel.changeRepository(a)
}
} case TableSelectionChangedEvent(s:RepoPanel,null) => { srcPanel.hidePanel }
Vyuºití externích komponent Pro jazyk Java existuje nep°eberné mnoºství r·zných
roz²í°ení a p°edem vytvo°ených komponent, bylo by proto výhodné je vyuºít. Toho dosáhneme vytvo°ením vlastní obalovací (Wrapper) t°ídy. V aplikaci Genie se kup°íkladu pouºívá komponenta pro výb¥r data JDateChooser. Pouºít ji p°ímo není v jazyce Scala moºné, proto si vypom·ºeme malým trikem - vytvo°íme vlastní t°ídu SDateChooser s atributem peer typu JDateChooser :
class SDateChooser(private val d: JDateChooser) extends Component { override lazy val peer:JDateChooser = d def getDate:Date = peer.getDate } Novou instanci této t°ídy pak vytvo°íme a zobrazíme v n¥jaké rodi£ovské komponent¥ následovn¥:
contents + = new SDateChooser(new JDateChooser(new Date))
6.6.4 Záv¥r ²esté iterace Nyní tedy aplikace uº skute£n¥ umoº¬uje ovládání pomocí uºivatelského rozhraní, i kdyº moºnosti modelu jsou stále je²t¥ v¥t²í, neº dokáºe uºivatelské rozhraní vyuºít. Základní práce s osobami a jejich detaily p°esto v²ak uº funguje.
64
KAPITOLA 6.
6.7
IMPLEMENTACE
Sedmá iterace
6.7.1 Cíle • vytvo°ení HTML reportu
report bude obsahovat údaje o osobách jednotlivé osoby budou mít odkazy na své p°íbuzné
6.7.2 Pr·b¥h iterace Pro vytvo°ení reportu ve form¥ webových stránek m·ºeme op¥t vyuºít snadné práce s XML elementy v jazyce Scala. Budeme tedy generovat XHTML dokument. Protoºe jde o report - výstup nabízející zjednodu²ený a uºivatelsky p°ív¥tivý náhled na data - budeme se soust°edit p°edev²ím na osoby. Ve²keré zobrazované informace týkající se konkrétní osoby, budou uvedené na jednom míst¥ na rozdíl od GenXML formátu, kde elementy osob, událostí i atribut· leºí na stejné úrovni.
6.7.3 Zajímavé poznatky Vytvá°ení XML dokumentu bylo d·kladn¥ popsáno uº v p°edchozích kapitolách. Proto, zmi¬me pouze p°ipojení denice typu dokumentu (DOCTYPE) k XML dokumentu. Denice DOCTYPE nepat°í mezi skute£né XML elementy, nejde o uzav°ený tag. Proto ji není moºné vloºit jako jiné elementy. Na²t¥stí obsahuje object scala.xml.XML metodu save, která m·ºe p°ijímat jako jeden z parametr· i objekt typu xml.dtd.DocType. Výsledné volání funkce save, která p°idá DOCTYPE do stránky pak m·ºe vypadat nap°íklad takto:
XML.save(filename, trim(genHTML(data)), "utf-8", true, doctype = xml.dtd.DocType( "html", xml.dtd.PublicID( "-//W3C//DTD XHTML 1.0 Strict//EN","http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" ), Nil )) Konstruktor t°ídy DocType dostává v tomto p°ípad¥ jako parametr jméno denice a instanci t°ídy ExternalID, která obsahuje identikátor denice. V tutorialech byste zmínku o p°ipojení denice k XML dokumentu hledali marn¥, kód naho°e je op¥t výsledkem experimentování.
6.7.4 Záv¥r sedmé iterace Aplikace nyní podporuje výstup do XHTML formátu, takºe si výstup m·ºe zobrazit kaºdý ve svém prohlíºe£i. K výstupu není p°ipojen ºádný kaskádový styl. Elementy ve výsledném XHTML dokumentu ov²em t°ídy pro kaskádové styly p°i°azeny mají, takºe stylování pomocí vlastního .css souboru je moºné.
6.8.
6.8
OSMÁ ITERACE
65
Osmá iterace
6.8.1 Cíle • vytvo°ení gracké reprezentace dat v podob¥ rodinného stromu
pro kaºdou osobu bude moºné zobrazit rozrod i vývod obrázek bude moºné exportovat do souboru
6.8.2 Pr·b¥h iterace Gracká reprezentace dat v podob¥ genealogického stromu p°edstavuje £ást aplikace, která je pro uºivatele nejzajímav¥j²í. Po výb¥ru osoby, která bude ko°enem rodinného stromu, si aplikace nejd°íve p°ipraví stromovou strukturu z instancí t°ídy PersonNode. Kaºdá instance obsahuje seznam svých potomk·. P°i vytvá°ení gracké reprezentace (jako BueredImage ) se, pokud obsahuje uzel potomky, zavolá rekurzivn¥ tvorba gracké reprezentace v potomcích a výsledné obrázky se sloºí a doplní orientovanými hranami (²ipkami). Pokud vybraná osoba ºádné potomky respektive rodi£e nemá, vytvo°í se pouze obrázek s grackou reprezentací dané osoby.
6.8.3 Zajímavé poznatky Jazyk Scala nenabízí pro práci s grakou ºádné zvlá²tní knihovny, bylo tedy t°eba sáhnout ke knihovn¥ java.awt. Pro kaºdou osobu je vytvo°ený BueredImage se jménem (barva textu závisí na pohlaví) a rolí oproti nad°azené osob¥ (potomek, otec, matka). Ve²keré údaje o velikosti generovaných prvk·, barv¥ text· atd. se nacházejí v objektu TreeDrawHelper, aby byly v²echny denovány na jednom míst¥ a dostupné pro více t°íd, pokud by do²lo k roz²í°ení grackých report·.
def drawLeafNode:BufferedImage = { val img:BufferedImage = new BufferedImage(TreeDrawHelper.IMG_WIDTH, TreeDrawHelper.IMG_HEIGHT, BufferedImage.TYPE_INT_RGB) val g:Graphics2D
= img.createGraphics
/* Fill backgroud with background color */ g.setColor(TreeDrawHelper.COLOR_BACKGROUND) g.fillRect(0, 0, TreeDrawHelper.IMG_WIDTH, TreeDrawHelper.IMG_HEIGHT) /* Draw box*/ g.setColor(TreeDrawHelper.COLOR_BORDER) g.drawRect(1, 1, TreeDrawHelper.IMG_WIDTH - TreeDrawHelper.BORDER_SIZE, TreeDrawHelper.IMG_HEIGHT - TreeDrawHelper.BORDER_SIZE)
66
KAPITOLA 6.
IMPLEMENTACE
/* Draw person name*/ g.setColor(TreeDrawHelper.getSexColor(sex)) if (personName.size > TreeDrawHelper.MAX_NAME_LENGTH) personName = personName.substring(0, TreeDrawHelper.MAX_NAME_LENGTH) + "..." g.drawString(personName, TreeDrawHelper.LABEL_POSITION.x, TreeDrawHelper.LABEL_POSITION.y) g.setColor(TreeDrawHelper.COLOR_TEXT) g.drawString(role,TreeDrawHelper.LABEL_POSITION.x, TreeDrawHelper.LABEL_POSITION.y+15) g.dispose img } Pokud obsahuje uzel osoby potomky, jsou nejprve vytvo°eny a poskládány obrázky potomk·. Zobrazení samotného obrázku v dané komponent¥ uº bylo snadné, sta£í implementovat metodu paintComponent :
override def paintComponent(g: Graphics2D) { if (descendants) { image = preparePersonNodesDescendants(person,levels).createImage } else { image = preparePersonNodesPredecessors(person,levels).createImage } g.drawImage(image, 20, 20, null) }
6.8.4 Záv¥r osmé iterace Aplikace nyní podporuje tvorbu grackých report·. Protoºe se jedná o instance t°ídy BueredImage, je moºné tyto obrázky snadno exportovat do png formátu. V aplikaci je moºné nastavit, zda chceme vid¥t strom potomk· nebo strom rodi£· i ur£it, jak hluboký strom chceme zobrazit.
Kapitola 7
Testování D·kladné testování aplikace by m¥lo být nedílnou sou£ástí vývoje kaºdé aplikace. Samoz°ejm¥, ºe neºijeme v ideálním sv¥t¥, takºe testování bývá £asto odsunuto na druhou kolej. V tak d·leºité práci jako je práce diplomová si ov²em nem·ºeme dovolit testování vynechat. Kdyº uº je aplikace psána ve funkcionáln¥ objektovém jazyce Scala i pro testování bude vyuºit nástroj Scalatest ur£ený p°ímo pro tento jazyk. Díky úzkému propojení s jazykem Java by mohla podobn¥ poslouºit i knihovna JUnit, která se stala standardem pro testování program· v jazyce Java. Scalatest v²ak nabízí víc a seznámení s novou technologií sebou vºdy nese vzru²ující nádech neznáma. Navíc v na²em oboru platí tato parafráze slavného výroku: Kolik technologií zná², tolikrát jsi £lov¥kem. Nebo´ jak °ekl Staislaw Lem: "Specialista je barbar, jehoº ignorantství není zcela v²eobecné". Tolik tedy k lozockému úvodu, poj¤me se nyní blíºe seznámit s frameworkem Scalatest.
7.1
Framework Scalatest
Tento framework byl vytvo°en speciáln¥ za ú£elem snadného testování zdrojového kódu napsaného v jazyce Scala. Cílí ov²em vý². Scalatest totiº umoº¬uje testovat i Java kód. Zárove¬ se snaºí stát se univerzálním frameworkem, který dob°e poslouºí pro ²irokou ²kálu ú£el· od psaní jednoduchých unit test· p°es podporu Behaviour-driven vývoje aº po integra£ní testování. Scalatest se totiº mimo jiné skládá z ²iroké °ady Trait pro vý²e zmín¥né ú£ely.
7.1.1 Test-driven vývoj (TDD) Pro podporu TDD se vyuºívá Trait FunSuite 1 a AfterBefore. Základní testy mají jednoduchou konstrukci pomocí klí£ového slova test("jméno testu") a jsou zakon£ené pomocí klí£ového slova assert. P°íklad jednoduchého testu, který kontroluje, zda po£et atribut· na£tených ze souboru "adams.gml"odpovídá skute£nosti: 1
Fun v tomto p°ípad¥ nemá nic spole£ného se zábavou, ale pochází ze slova Functional.
67
68
KAPITOLA 7.
TESTOVÁNÍ
class ScalaLoaderTest extends FunSuite{ val source = XML.load("adams.gml") test ("test of loading all attributes, checks if the number of loaded attributes is right") { val expResult = 4 val result = dSLoaderAssertions.loadAllAttributes(source).size assert(result === expResult) } } Trojnásobné rovnítko místo dvojnásobného slouºí k vypsání porovnávaných objekt· (pomocí toString funkce) v p°ípad¥, ºe se objekty nerovnají. Tester tak okamºit¥ vidí, kde se stala chyba. Pokud chceme otestovat, zda dojde k vyhození ur£ité výjimky, sta£í nám k tomu pouºít klí£ové slovo intercept.
test ("trying to load data without from non-existeing datafile") { val fileName = "someStrangeFile.gml" val instance = new DefaultScalaLoader(fileName) val thrown = intercept[java.io.FileNotFoundException] { instance.checkFile } assert(thrown.getMessage === "File not found") } Tento test kontroluje, zda je b¥hem pokusu o na£tení neexistujícího souboru vyhozena FileNotFoundException a zda tato výjimka obsahuje správnou chybovou zprávu.
7.1.2 Behaviour-driven vývoj (BDD) Pokud chceme specikovat jaké chování má být testováno, poslouºí nám Trait Spec. V tomto p°ípad¥ kombinujeme testy s jejich textovým popisem. Pouºíváme pro to klí£ová slova describe a it. Ukaºme si specikaci test· z minulého oddílu:
import org.scalatest.Spec class ExampleSpec extends Spec { describe("A ScalaLoader") { it("should check if the number of loaded Attributes equals to the real number of Attributes in "adams.gml" file") (pending) it("should throw FileNotFound Ecxeption and Cheb if the message is "File Not found" ") (pending) } }
7.1.
FRAMEWORK SCALATEST
69
Kdyº chceme p°ejít ze specikace test· k jejich implementaci, tak jednodu²e místo klí£ového slova pending vloºíme stejný blok kódu jako v oddíle TDD.
7.1.3 Akcepta£ní testy Nejmocn¥j²í moºností, kterou Scalatest nabízí je pouºít Trait FeatureSpec a GiveWhenThen. Nyní máme k dispozici kompletní sadu po popis poºadavku a jeho chování. Malá ukázka, která snad osv¥tlí koncept nejlépe:
feature("The application can load Attributes from provided XML file in GenXML format") { info("I want the application to load all Atributes from file ") scenario("loading Attributes from .gml file") {
}
given("the adams.gml file with GenXML data") when("when loadAttributes function is called") then("then the list with stored Attributes is no longer empty") and("the total number of loaded Attribues should be 4") pending
Takto p°esn¥ krok za krokem specikujeme své poºadavky na danou vlastnost (feature) aplikace. Potom uº není nic jednodu²²ího neº dodat jednotlivým krok·m i implmentaci:
feature("The application can load Attributes from provided XML file in GenXML format") { info("I want the application to load all Atributes from file ") scenario("loading Attributes from .gml file") { given("the adams.gml file with GenXML data") val source = XML.load("adams.gml") when("loadAttributes function is called") val result = dSLoaderAssertions.loadAllAttributes(source) then("the list with stored Attributes is no longer empty ") assert(result.isEmpty === false) and("the total number of loaded Attribues should be 4") assert(result.size === 4) } Výsledek po spu²t¥ní testu bude vypadat následovn¥:
70
KAPITOLA 7.
TESTOVÁNÍ
$ scala -cp scalatest-1.5.jar org.scalatest.tools.Runner -p . -o -s ScalaLoaderTest Run starting. Expected test count is: 1 ExampleSpec: Feature: The application can load Attributes from provided XML file in GenXML format I want the application to load all Atributes from file Scenario: loading Attributes from .gml file Given a the adams.gml file with GenXML data When loadAttributes function is called Thenthe list with stored Attributes is no longer empty And the total number of loaded Attribues should be 4 Run completed in 45 milliseconds. Total number of tests run: 1 Suites: completed 1, aborted 0 Tests: succeeded 1, failed 0, ignored 0, pending 0 All tests passed. Tolik tedy k popisu frameworku Scalatest, poj¤me se nyní podívat na jeho reálné pouºití p°i vývoji aplikace GENiE. 7.2
Vyuºití frameworku Scalatest pro vývoj aplikace GENiE
Scalatest umoº¬uje integraci s buildovacím systémem Ant, který vyuºívá i tento projekt. Sta£ilo tedy p°íslu²n¥ upravit soubor build.xml - p°idat cíle pro zkompilování test· a jejich spu²t¥ní. Testy byly psány hned po dokon£ení dané funkcionality. Kdyº v první iteraci vznikly t°ídy pro nahrávání dat z xml, byly je²t¥ v té samé iteraci otestovány. V projektu se dodrºují jmenné konvence pro testování z jazyka Java, tzn. testovací t°ídy nesou stejný název jako t°ídy testované, jen jsou dopn¥né o koncovku -Test. Bliº²í informace k testování jsou pro p°ehlednost rozd¥lené podle jednotlivých balí£k·.
7.2.1 Loader K prvotnímu testování na£ítání dat z .gml souboru pomocí standardního GenXML Loaderu byl pouºit soubor "adams.gml"získaný ze stránek projektu GenXML. Bohuºel obsahoval tento soubor data ve formátu GenXML verze 2.0, které se od v aplikaci pouºité pouºité verze 3.0 v n¥kterých detailech li²í. Jmenujme jen dva nejd·leºit¥j²í rozdíly:
• Relationship uº propojuje pouze dv¥ osoby (viz kapitolu s popisem formátu GenXML) • z Place se stala samostatná entita na nejvy²²í úrovni. Ve verzi 2.0 byla tato entita je²t¥ zano°ena v n¥jaké rodi£ovské entit¥, nyní se tam nachází pouze reference. Úplný seznam zm¥n m·ºete najít ve specikace formátu GenXML 3.0[24]. Testy této komponenty m·ºete najít v balí£ku defaultScalaLoaderTest. Testy jsou dále d¥leny do £ty° soubor· podle p°íbuznosti.
7.2.
VYUITÍ FRAMEWORKU SCALATEST PRO VÝVOJ APLIKACE GENIE
71
defaultScalaLoaderTest Obsahuje testy kontrolující na£ítání obecných informací. Testuje se zde:
• chování aplikace p°i otevírání neexistujícího souboru • na£tení entit FileInfo a Total ze souboru "adams.gml"a porovnání se skute£ností
dSLoaderAssertionsTest Obsahuje testy týkající se na£ítáníAssertion (Event, Relationship, . . . ). Testuje se zde:
• na£tení v²ech entit typu Attribute, Event, Info a Relationship ze souboru "adams.gml"a kontrola, zda byl entit daného typu na£ten správný po£et • na£tení první entity typuAttribute, Event, Info a Relationship a porovnání s objektem vzniklým p°ímým zadáním skute£ných parametr· enity
dSLoaderPersonTest Obsahuje testy týkající se na£ítání osob. Testuje se zde:
• na£tení v²ech entit typu Person ze souboru "adams.gml"a kontrola, zda po£et odpovídá skute£nosti • na£tení první entity typuPerson a porovnání s objektem vzniklým p°ímým zadáním skute£ných parametr· enity
dSLoaderProjectTest Obsahuje testy týkající se informací o projektu a pomocných entit. Testuje se zde:
• na£tení hlavi£ky (Header) ze souboru "adams.gml"a porovnání se skute£ností • na£tení první entity typu Address, ExactDate a jejich porovnání se skute£ností
7.2.2 Exporter Testy exportu dat zp¥t do GenXML formátu obsahuje t°ída DefaultScalaExporterTest. Nejlep²ím test v²ak p°edstavuje uloºení dat a jejich op¥tovné na£tení. Pak se nejlépe pozná, zda Loader i Exporter fungují správn¥.
72
KAPITOLA 7.
TESTOVÁNÍ
7.2.3 Model V této sekci jsou sdruºené testy komponenty model. Testují se operace v managerech (PersonManager, AssertionManager,...). Testy k jednotlivým manager·m se nacházejí v souborech se stejným jménem jako testovaný manager. Testují se p°edev²ím CRUD operace:
• p°idání jedné nové instance (uloºí se do kolekce? odpovídají uloºená data t¥m zadaným?) • p°idání více instancí • vyhledání nov¥ p°idané instance v kolekci • smazání instance
Kapitola 8
Záv¥r 8.1
Shrnutí
Poj¤me si nyní zrekapitulovat tuto práci a podívejme se na výsledek p·lro£ní práce. Poºadavky stanovené na za£átku práce byly spln¥ny:
• aplikace slouºí ke správ¥ genealogických údaj· o osobách • aplikace umoº¬uje i evidenci zdroj· • aplikace umoº¬uje výzkumník·m v oboru genealogie propojit informace o osobách se zdroji pomocí tvrzení • aplikace umoº¬uje vizualizaci dat ve form¥ seznam· i v gracké podob¥ jako rodinný strom • aplikace umoº¬uje vytvá°et reporty ve form¥ HTML stránek • aplikace je napsána v jazyce Scala • aplikace ukládá i na£ítá data z XML formátu GenXML • aplikace umoº¬uje import základních údaj· z formátu GEDCOM Následující odstavce budou obsahovat osobní hodnocení a názory autora, proto mi dovolte, abych vyuºil v textu první osobu jednotného £ísla neboli ich formu. P°i práci bylo t°eba £elit mnoha výzvám. P°edn¥ se jednalo o doposud nejv¥t²í projekt, na kterém jsem jako autor pracoval, proto zvlá²t¥ p°i prvotní analýze a návrhu architektury systému docházelo k £astým zm¥nám, které vývoj zpomalovaly. Nakonec se velmi osv¥d£ilo rozd¥lení vývoje na jednotlivé p°esn¥ ur£ené iterace, jimº p°edcházela pouze obecná analýza. Takto bylo moºné pracovat vºdy jen na malé £ásti celku, kterou byl £lov¥k schopen jednodu²e obsáhnout. Výstupem kaºdé takové iterace byl navíc funk£ní prototyp, který bylo moºné otestovat 1 . Dal²í výzvou bylo p°evedení pom¥rn¥ rozsáhlého formátu GenXML do aplikace, práv¥ p°íprava nahrávání dat (první iterace) zabrala ze v²ech iterací nejvíce £asu. Bylo t°eba stanovit si ur£ité hranice, jak daleko aº s podporou formátu zajít. Na²t¥stí °e²ení poskytl samotný formát svým rozd¥lením na úrovn¥ detailu (level). Jako ideální kompromis se ukázala úrove¬ t°etí. 1
P°i komer£ním vývoji by takový prototyp dostal k otestování i zákazník
73
74 8.2
KAPITOLA 8.
ZÁV
R
Zhodnocení práce s jazykem Scala
A jak se osv¥d£il jazyk Scala na projektu m¥°ítka ne úpln¥ malého? Po prvotním nad²ení z knihovny scala.xml p°i²lo vyst°ízliv¥ní p°i tvorb¥ uºivatelského rozhraní. Nové rozhraní pro práci se starou známou knihovnou java.swing p·sobí zpo£átku matoucím dojmem. Ve skute£nosti má práce s knihovnou scala.swing svoji dob°e fungující logiku, do které je ov²em nejprve pot°eba proniknout. Základní úkony jsou popsány v n¥kolika návodech [23] [21], ale p°i práci s pokro£ilej²ími aspekty knihovny je vývojá° nucen k metod¥ pokus omyl, coº v p°ípad¥ uºivatelského rozhraní, které není snadné automaticky testovat, velmi zamrzí. Po n¥jaké dob¥ jsem si v²ak vytvo°il postupy a návyky, které m¥ provedly bezpe£n¥ mezi Skyllou a Charybdou tvorby uºivatelského rozhraní. Celkov¥ v²ak musím vyjád°it s jazykem Scala svou spokojenost. Oproti jazyku Java ²et°í svou stru£ností vývojá°ovy ruce. Podle mých pozorování dochází ke zestru£n¥ní kódu aº na polovi£ní mnoºství oproti jazyku Java. Mnoho zabudovaných funkcí pro práci se seznamy (ltrování, skládání, ...), které p°ijímají funkce jako argumenty, p°edstavuje zna£né uleh£ení práce. Bohuºel podpora ve vývojovém prost°edí Netbeans nedosahuje vysoké úrovn¥. Jak uº jsem zmínil vý²e, Parser se vyzna£uje velkou chybovostí a je zapot°ebí ho £asto restartovat. Kontextová nápov¥da funguje také pouze omezen¥ a na pokro£ilej²í funkce jako generování metod nebo jejich automatický refactoring m·ºe vývojá° zapomenout úpln¥. 8.3
Tato práce v £íslech
Na záv¥r dovolte malé shrnutí o rozsahu této práce. Jsme technici, nechme proto mluvit £ísla:
Textová £ást Po£et stran Po£et obrázk· Po£et tabulek
107 19 3
Praktická £ást Po£et t°íd Po£et °ádk· kódu Po£et test·
79 7739 52
Tabulka 8.1: Tato práce v £íslech
8.4
A co dál?
Práce sice na tomto míst¥ kon£í, ale rád bych v projektu pokra£oval i nadále, jak jen to £asové moºnosti dovolí. Zdrojové kódy jsem publikoval na serveru Github.com[4] a z projektu se tak stal Open Source. Mluvil jsem s n¥kolika lidmi, kte°í projevili o aplikaci zájem, mimo jiné s mým bývalým profesorem z gymnázia, kde se sestavováním genealogií za£ali také zabývat. Mým cílem je tedy dotáhnout projekt do fáze, kdy bude moci slouºit
8.4.
A CO DÁL?
75
dal²ím lidem. Dal²í zm¥ny proto £ekají p°edev²ím uºivatelské rozhraní, které bude muset být dopln¥no o dal²í moºnosti, které model sice uº umoº¬uje (nebo bude umoº¬ovat po drobných úpravách), ale uºivatelskému rozhraní chybí. Kapitoly popisující jazyk Scala bych také rád postupn¥ zp°ístupnil na blogu. Myslím, ºe obsahují pom¥rn¥ dost zajímavých informací pro dal²í vývojá°e. Pokud bude £as, p°idám i kapitolu o paralelních výpo£tech pomocí knihovny Actors, ke které jsem se v rámci této práce bohuºel nedostal.
76
KAPITOLA 8.
ZÁV
R
ást II
Dodatek
77
Literatura [1] China daily: China celebrates fth revision of Confucius' family tree. http://www.chinadaily.com.cn/china/2009-09/24/content_8733356.htm. [2] eská genealogická a heraldická spole£nost v Praze. http://www.genealogie.cz/. [3] Genea: Genealogické stránky. http://www.genea.cz/. [4] Github genie - repository. https://github.com/martin-fabry/GENiE. [5] Scala option cheat sheet. http://blog.tmorris.net/scalaoption-cheat-sheet/. [6] Streaming API for XML. http://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/1.6/ tutorial/doc//. [7] R. C. Anderson, P. Barkley, et al. GENTECH Genealogical Data Model. GENTECH, 2000. [8] J. Arlow and I. Neustadt. UML2 and The unied process: Practical object-oriented analysis and design, volume 1. Pearson Education, 2th edition, 2005. [9] H. D. Benington. Production of large computer programs. History of Computing, 1983.
IEEE Annals of the
[10] F. Berthold. Virtuous programmer: A month with Scala. http://www.virtuousprogrammer.com/?page_id=185. [11] R. Eckstein. Java SE application design with MVC. http://www.oracle.com/technetwork/articles/javase/index-142890.html. [12] B. Emir.
Object-oriented pattern matching. EPFL, 2007.
[13] B. Emir, S. Maneth, and M. Odersky. Services. EPFL, 2007.
Scalable Programming Abstractions for XML
[14] M. Fábry. Návrhové vzory ve funkcionálním programovacím jazyce scala, 2009. [15] M. Fowler. Anemic domain model. http://www.martinfowler.com/bliki/AnemicDomainModel.html. 79
80
LITERATURA
[16] E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software, volume 1. AddisonWesley Professional, 1th edition, 1995.
[17] M. Gleichmann. Scala in practice: Traits as mixins motivation, 2009. http://gleichmann.wordpress.com/2009/07/19/scala-in-practice-traits-as-mixins-m [18] J. Iry. Why scala's option and haskell's maybe types will save you from null. http://james-iry.blogspot.com/2010/08/why-scalas-and-haskells-types-will-save. html. [19] J. Jenkov. Java StAX. http://tutorials.jenkov.com/java-xml/stax.html. [20] C. Larman and V. R. Basili. Iterative and incremental development: A brief history. IEEE Computer, 2003. [21] C. K. Loverdos and A. Syropoulos. Steps in Scala: An introduction Functional Programming. Cambridge University Press, 2010. [22] I. Maier.
The scala.swing package. EPFL, 2009.
[23] M. Odersky, L. Spoon, and B. Venners. Press, 1th edition, 2007. [24] C. Owe.
to Object-
GenXML 3.0, 2010.
Programming in Scala, volume 1. Artima
P°íloha A
Slovní£ek Assertion Tvrzení Controller adi£ Design pattern Návrhový vzor Excerpt Úryvek Family tree Rodinný strom Flag P°íznak Objective Cíl Package Balí£ek Parser Analyzátor Pattern matching Rozpoznávání vzor· Publisher Vydavatel Task Úkol
81
82
PÍLOHA A.
SLOVNÍEK
P°íloha B
Seznam zkratek API Application Programming Interface BDD Behaviour Driven Development CRUD Create, read, update, delete DRY Don't repeat yourself DTD Document Type Denition GDM Genealogy Data Model GEDCOM GEnealogical Data COMmunication GNU GPL GNU General Public License GUI Graphical User Interface HTML HyperText Markup Language JRE Java Runtime Enviroment JVM Java Virtual Machine MVC Model-View-Controller PIM Platform Independent Model PSM Platform Specic Model StAX Streaming API for XML TDD Test Driven Development XHTML eXtensible HyperText Markup Language XML eXtensible Markup Language
83
84
PÍLOHA B.
SEZNAM ZKRATEK
P°íloha C
Obsah p°iloºeného CD CD | |- analyza | |- genie.eap
(návrh aplikace vytvo°ený pomocí Enterprise Architect)
| | - aplikace | | - prace s XML | | | | | | | | | | | |
(zkompilovaná aplikace GENiE) (zdrojové kódy projekt· testujících moºnosti zpracování XML, viz sekce Technologické okénko)
- geniejava
(zpracování souboru GenXML pomocí StAX)
- javaHamletParse (zpracování vzorového XML pomocí StAX) - javaXMLgen
(generování XML dat pomocí StAX)
- practiceXML
(obsahuje vzorový XML soubor)
- scalaHamletParse (zpracování XML v jazyce Scala) - scalaXMLGen
| | - text | | - zdrojove kody
(generování XML dat v jazyce Scala)
(text této práce v pdf a LaTeX) (projekt aplikace GENiE se zdrojovými kódy a build souborem pro ant)
85
86
PÍLOHA C.
OBSAH PILOENÉHO CD
P°íloha D
Stru£ný návod k pouºití aplikace V následující sekci si ukáºeme, jak se s aplikací pracuje a °e²í typické úkoly. 1. Vytvo°ení nového projektu Pomocí menu File >New, do okna pak sta£í zadat název nového projektu. Po vytvo°ení se zobrazí panel se záloºkami. 2. Otev°ení projektu v GenXML formátu Pomocí menu File >Open, data se na£tou a zobrazí se panel se záloºkami. 3. Import dat z GEDCOM formátu Pomocí menu File >Import GEDCOM, data se na£tou a zobrazí se panel se záloºkami. 4. Uloºení projektu Pomocí menu File >Save. 5. Vytvo°ení nové osoby Pomocí menu Person >New Person, uºivatel vyplní jméno, p°íjmení a pohlaví nové osoby. 6. Úprava údaj· o osob¥ Vyberte osobu v seznamu na záloºce Person a klikn¥te v pravé £ásti na tla£ítko Edit. Pole se stanou aktivními a m·ºete zm¥nit poºadované údaje. Po skon£ení úprav klikn¥te na tla£ítko Save. 7. Úprava atribut· osoby Na záloºce Person vyberte osobu ze seznamu a p°epn¥te pravý panel na záloºku Attributes. Tady vidíte seznam atribut· dané osoby. Po vybrání atributu ze seznamu se ve spodní £ásti zobrazí detail atributu a tla£ítko pro smazání. (a) P°idání nového atributu Nad seznamem atribut· stiskn¥te tla£ítko Add. Objeví se dialogové okno, kde m·ºete vyplnit detaily atributu. Existují t°i t°ídy atributu, textové, £íselné a p°íznaky. Atributy je moºné propojit se zdrojem pomocí tvrzení, tvrzení m·ºete zvolit po kliknutí na tla£ítko Choose výb¥rem ze seznamu tvrzení. 87
88
PÍLOHA D.
STRUNÝ NÁVOD K POUITÍ APLIKACE
Obrázek D.1: Screenshot - detail osoby
(b) Smazání atributu Vyberte atribut ze seznamu a dole klikn¥te na tla£ítko Delete. 8. Úprava událostí v ºivot¥ osoby Na záloºce Person vyberte osobu ze seznamu a p°epn¥te pravý panel na záloºku Events. Tady vidíte seznam událostí dané osoby. Pokud na události participuje více osob (typicky t°eba svatba), objeví se událost u v²ech. Po vybrání události ze seznamu se ve spodní £ásti zobrazí detail události a tla£ítko pro smazání. (a) P°idání nové události Nad seznamem událostí klikn¥te na tla£ítko Add. Objeví se dialogové kno, kde m·ºete vyplnit detaily události - o jaký typ události se jedná, datum konání, tvrzení podporující tuto událost i dal²ího ú£astníka události. (b) Smazání události Vyberte událost ze seznamu a klikn¥te na tla£ítko Delete. 9. Práce s rodinným stromem Pro zobrazení rodinného stromu se p°epn¥te na záloºku Family Tree. Tady si m·ºete ze seznamu vybrat osobu, která bude ko°enem stromu, hloubku stromu a zda se bude jednat o strom s p°edky nebo s potomky. Zobrazený strom je moºné uloºit do souboru PNG pomocí tla£ítka Save. 10. Práce s repozitá°i Seznam repositá°· najdete na záloºce Sources. Pracuje se s nimi podobn¥ jako s atributy nebo událostmi zmín¥nými vý²e. 11. Práce se zdroji Pokud máme vybraný repozitá°, zobrazí se ve vedlej²ím panelu seznam zdroj·.
89
Obrázek D.2: Screenshot - atributy osoby
12. Práce s tvrzeními Pokud máme vybraný zdroj, zobrazí se ve vedlej²ím panelu seznam tvrzení vycházejících z tohoto zdroje. 13. Vytvo°ení reportu ve formátu HMTL Pomocí menu Reports >HTML Report. HTML stránka bude uloºena do vybraného souboru.
90
PÍLOHA D.
STRUNÝ NÁVOD K POUITÍ APLIKACE
Obrázek D.3: Screenshot - rodinný strom
91