12. Cvičenie - Generovanie kódov IV. (Úloha 8)

Ciele
  1. Oboznámiť sa s návrhom použivateľského rozhrania.
  2. Vytvoriť generátory UI.
  3. Otestovať vygenerovanú aplikáciu.
Úvod

    Na dnešnom cvičení dokončíme finálny produkt - naša vygenerovaná aplikácia bude mať konzolové rozhranie, ktoré bude mať dialógy, podobné tým v bežných CRUD grafických aplikáciách. Tiež budeme generovať hlavnú triedu generovanej aplikácie, ktorá nahradí doteraz používanú testovaciu triedu Main.

    Navrhnutá implementácia používateľského rozhrania pozostáva z menu a dialógových obrazoviek. Dialógové obrazovky sú dvoch typov:
    • TableDialog - predstavuje tabuľku zobrazujúcu položky daného typu v databáze a jej úlohou je sprostredkovať CRUD operácie používateľovi. Operácie create a edit sú riešené pomocou formulárov.
    • FormDialog - úlohou jednotlivých formulárov je zapezpečiť vytvorenie novej, resp. zmenu vlastností existujúcej entity na základe vstupu používateľa.

    Úlohou dnešného cvičenia bude teda upraviť šablóny tabuliek, formulárov a hlavnej triedy tak, aby finálne riešenie predstavovalo plne funkčnú konzolovú aplikáciu. Využijeme pri tom aj triedy z univerzálneho kódu v magsa-framework a definíciu používateľského rozhrania v XML dokumente, ktorú sme vytvárali na minulom cvičení.

Postup
  1. Triedy, ktoré budú generované v cieľovej aplikácii, súvisia s definíciou používateľského rozhrania v zdrojovom dokumente ui.xml vytvorenom na predchádzajúcom cvičení nasledovne:
    • Pre každý záznam <table> v zdrojovom XML dokumente bude vygenerovaná trieda dediaca od triedy TableDialog.
    • Pre každý záznam <form> v zdrojovom XML dokumente bude vygenerovaná trieda dediaca od triedy FormDialog.
    Úloha: Preštudujte si zdrojový kód tried univerzálneho kódu v magsa-framework TableDialog a FormDialog.


    Pošli
  2. Menu bude súčasťou hlavnej triedy generovanej aplikácie Application. Výpis menu bude zaisťovať metóda private void menu(). Trieda Application bude implementovať návrhový vzor singleton. K dispozícii je generátor hlavnej triedy generovanej aplikácie ApplicationGenerator a kostry šablón appgenerator.zip. Oboznámte sa s pokytnutým balíkom.
    Úloha: Pre každú entitu, ktorá je definovaná v modeli, zabezpečte vygenerovanie členskej premennej v triede Application, ktorá bude sprístupňovať DAO objekt pre danú entitu. Zabezpečte aj inicializáciu tejto premennej a prístupovú metódu get pre získanie hodnoty členskej premennej ("getter").


    Pošli
  3. V tomto kroku budú vytvorené šablóny pre generovanie tabuliek a formulárov.
    Úloha: Upravte šablónu ui_table.java.vm tak, aby pre každú inštanciu triedy Table v modeli používateľského rozhrania UI generovala triedy tabuliek. Generované triedy tabuliek budú rozširovať triedu TableDialog.
    • V konštruktore vygenerovanej triedy použite príslušný getter pre DAO objekty v singleton objekte triedy Application.
    • Metóda protected T createFormDialogForInsert() vracia nový prázdny formulár pre vytvorenie novej príslušnej entity.
    • Metóda protected T createFormDialogForEdit(T entity) vracia formulár s hodnotami vlastností editovanej entity pre umožnenie editácie príslušnej entity.
    • Metóda protected void printHeader() má za úlohu vypísať hlavičku tabuľky.
    • Metóda protected void printRow(Entity entity) na základe argumentu vypisuje riadok tabuľky.


    Pošli
    Poznámka: Nezabudnite, že pri generovaní používateľského rozhrania vychádzame z vety z jazyka UI (ui.xml), ktorá je spracovaná triedou UIProcessor. Kľúčovou triedou metamodelu pre generovanie používateľského rozhrania je trieda UI, kde sa po spracovaní nachádzajú zoznamy dialógov a ich polí a stĺpcov. V šablóne pre tabuľku teda vychádzajte z modelu UI a nie z modelu entít. Týmto spôsobom je zabezpečené oddelenie používateľského rozhrania od jadra aplikácie. Používateľské rozhranie je takto možné kedykoľvek meniť nezávisle od jadra aplikácie, či kompletne zameniť za iné bez nutnosti vykonania zmien v jadre aplikácie.
    Poznámka: Pri referenciách potrebujete zabezpečiť, aby sa v bunke daného stĺpca (LookupColumn) zobrazila vlastnosť z referovanej entity. V príkladovom ui.xml je v tabuľke pre entitu Zamestnanec stĺpec odkazujúci sa na vlastnosť nazov entity Oddelenie. Teda v danom stĺpci sa má zobraziť názov oddelenia, ku ktorému zamestnanec patrí, a nie identifikátor, ktorý je uložený v pamäti. Na realizáciu tejto funkcionality použite metódu public T find(int id) z DAO implementácie pre referovanú entitu. Na rozlíšenie medzi objektmi typu Column a LookupColumn možete porovnať meno triedy objektu typu Column ($column.getClass().getSimpleName()).
    Poznámka: Vo volaní metódy String.format() pri výpise tabuľky sa definuje dĺžka stĺpca, tú je potrebné nastaviť podľa voliteľného parametra length v triede Column, ak ten nie je inicializovaný, použite hodnotu 10.
    Poznámka: Ako názvy položiek menu, formulárov, stĺpcov tabuľky alebo riadkov formulárov používajte primárne hodnotu atribútu label.
    Vygenerovaná trieda môže vyzerať nasledovne:
    
    public class OddelenieTable extends TableDialog<Oddelenie>{
        public OddelenieTable() {
            super(Application.getInstance().getOddelenieDao());
        }
    
        protected OddelenieForm createFormDialogForInsert() {
            return new OddelenieForm();
        }
    
        protected OddelenieForm createFormDialogForEdit(Oddelenie entity) {
            return new OddelenieForm(entity);
        }
    
        protected void printHeader() {
            System.out.print(String.format("|%5s", "ID"));
            System.out.print(String.format("|%10s", "nazov"));
            System.out.print(String.format("|%10s", "poschodie"));
            System.out.println();
        }
    
        protected void printRow(Oddelenie entity) {
            System.out.print(String.format("|%5d", entity.getIdent()));
            System.out.print(String.format("|%10s", entity.getNazov()));
            System.out.print(String.format("|%10s", entity.getPoschodie()));
            System.out.println();
        }
    }
    
    Úloha: Upravte šablónu ui_form.java.vm tak, aby pre každú inštanciu triedy Form v modeli používateľského rozhrania UI generovala triedy formulárov. Generované triedy budú rozširovať triedu FormDialog.
    Je potrebné vytvoriť 2 konštruktory:
    • Bezparametrický sa použije v prípade operácie create.
    • V prípade operácie edit bude parametrom entita určená na editáciu. Pri editácii sa danej entity sa vo formulári zobrazia hodnoty vlastností editovanej entity.
    Metóda public T show() vyžiada postupne pre každú vlastnosť entity od použivateľa zadanie hodnoty. V prípade, že použivateľ nič nezadal, použije sa aktuálna hodnota. Zadaná hodnota musí zodpovedať obmedzeniam danej vlastnosti.
    Poznámka: Pri editácii zobrazte vždy aj aktuálnu hodnotu editovanej entity (napr. v hranatých zátvorkách []). Ak sa vytvára nová entita, aktuálna hodnota je prázdna (hodnota null). V prípade referencií a komponente typu (LookupField) nezabudnite, že ako aktuálnu hodnotu je potrebné zobraziť nie identifikátor referovanej entity, ale vlastnosť, ktorú identifikuje model z ui.xml.


    Pošli
    Vygenerovaná metóda show() pre entitu Oddelenie môže vyzerať nasledovne:
    
    public Oddelenie show() {
        String input;
    
        String nazov = entity.getNazov();
        do {
            try {
                System.out.print(String.format("Enter nazov [%s]: ", entity.getNazov()));
                input = Utilities.readLine();
                if(!"".equals(input)) {
                    nazov = input;
                }
                if(nazov == null) {
                    throw new ValidatorException("Property 'nazov' is required!");
                }
                if(nazov != null) {
                    if(nazov.length() < 2 || nazov.length() > 30) {
                        throw new ValidatorException("Property 'nazov' has length out of range!");
                    }
                }
                break;
            } catch (NumberFormatException e) {
                System.out.println("Cannot parse the entered value!");
            } catch (ValidatorException e) {
                System.out.println(e.getMessage());
            }
        } while (true);
        entity.setNazov(nazov);
    
    
        Double poschodie = entity.getPoschodie();
        do {
            try {
                System.out.print(String.format("Enter poschodie [%s]: ", entity.getPoschodie()));
                input = Utilities.readLine();
                if(!"".equals(input)) {
                    poschodie = Double.valueOf(input);
                }
                break;
            } catch (NumberFormatException e) {
                System.out.println("Cannot parse the entered value!");
            } catch (ValidatorException e) {
                System.out.println(e.getMessage());
            }
        } while (true);
        entity.setPoschodie(poschodie);
    
        return entity;
    }
    
    Poznámka: Na načítanie vstupu používateľa je možné použiť statickú metódu Utilities.readLine(). Pre mnohé volania metód, ktoré potrebujete vygenerovať, je možné v šablóne použiť pomocné metódy poskytované generátorom. Príkladom môže byť metóda formatQualifiedName triedy JavaTemplateGenerator, ktorá vám môže pomôcť vytvoriť celé meno generovanej triedy, ktorú práve potrebujete (viď. kostry šablón). Alebo parseStringMethod tej istej triedy, ktorá pre vás pripraví spracovanie reťazca do typu, ktorý je v danom kontexte potrebný.
  4. V poslednom kroku bude potrebné dokončiť implementáciu triedy Application pre zabezpečenie funkcionality a zobrazenia hlavného menu aplikácie.
    Úloha: Doplňte pre triedu Application implementáciu metódy private void menu() v šablóne app.java.vm. Jej úlohou je pre každú entitu, ktorá má v UI definovanú tabuľku, vytvoriť položku menu umožňujúcu zobraziť podmenu danej entity. Ako názov položky menu pre zobrazenie podmenu entity použite hodnotu label z jej tabuľky definovanej v ui.xml. Zobrazenie podmenu realizujte vytvorením nového objektu príslušnej podtriedy TableDialog (napr. ZamestnanecTable) a na ňom zavolaním metódy menu() (každá tabuľka implementuje tiež metódu menu, ktorá sa stará o vypísanie podmenu).
    Poznámka: Pri implementácii menu sa môžete inšpirovať implementáciou podmenu v triede TableDialog.


    Pošli
  5. Úloha: Otestujte generátor pomocou nasledujúceho kódu:
    
    public class Make {
        public static void main(String[] args) throws Exception {
            //... povôdný obsah spracovanie viet jazyka entít
        
            //... povôdný obsah generovania
    
            //TODO: doplňte generovanie tabuliek a formulárov
        
            new ApplicationGenerator(model).generate();
        }
        //... pôvodný obsah
    }        
    
    Otestujte vygenerovaný projekt jeho spustením a overením všetkých operácií.


    Pošli
  6. Vymažte súbory z disku a zrušte všetky vami vytvorené nastavenia vo vývojovom prostredí!
Zdroje
  1. Zdrojové kódy:
    • appgenerator.zip - generátor hlavnej triedy generovanej aplikácie ApplicationGenerator a kostry šablón
Doplňujúce úlohy
    Úloha: Implementujte podporu vypísania výskytov entít, ktoré referejú na zvolenú entitu. V našom príklade by sa jednalo o vypísanie všetkých zamestnancov patriacich niektorému oddeleniu.
    UkončenáPoznámka


    Pošli
    Úloha: Implementujte vyhľadávanie podľa zvolenej vlastnosti. Napr. pri zamestnancoch by malo byť možné nielen zobraziť všetkých zamestnancov, ale ukázať napr. zamestnancov ktorí majú meno "Milan", alebo vek 25.
    UkončenáPoznámka


    Pošli
    Úloha: Odchytajte všetky výnimky (try {...} catch(..) {}), ktoré môžu počas behu aplikácie nastať tak, aby počas jej behu nebolo možné, aby aplikácia neštandardne skončila. Napríklad, aby aplikácia nespadla ak nie je sputená databáza, alebo aby sa nestalo, že sa ukončí po zadaní údajov, ktoré porušujú definované obmedzenia.
    UkončenáPoznámka


    Pošli
comments powered by Disqus