Stránka sa načítava, prosím čakajte…
© 2005 – 2024 Roman Horváth, všetky práva vyhradené. Dnes je 19. 4. 2024.
Dátum: 4. 8. 2020, pred štyrmi rokmi
Balíček kvíz
obsahuje základnú implementáciu kvízového rozhrania s otázkami s jednoduchou voľbou. V balíčku je definovaných päť tried: ChybaKvízu
, Odpoveď
, Otázka
, VýberOtázky
a Kvíz
. Z nich je z pohľadu programátora, ktorý balíček používa najdôležitejšia trieda Kvíz
(ktorá prepája ostatné triedy a automaticky používa ich inštancie). Trieda VýberOtázky
je dôležitá len ako údajový typ, do ktorého programátor číta obsahu otázok a odpovedí, ktoré zobrazuje riešiteľovi kvízu. Triedu ChybaKvízu
môže programátor použiť v štruktúre try-catch na zachytenie výnimiek špecifických pre tento balíček.
Definícia kvízu (označovaná v tomto projekte aj ako konfigurácia kvízu) sa musí nachádzať v textovom súbore (dostupnom pre aplikáciu). Ústredná trieda balíčka Kvíz
prijíma vo svojom konštruktore názov tohto súboru. Po konštrukcii kvízu musí programátor zabezpečiť obojsmernú komunikáciu s používateľom – riešiteľom: výpis alebo vykreslenie obsahu otázok a k nim prislúchajúcich možností, zobrazenie potrebných ovládacích prvkov na pohyb po kurze a vykonanie voľby z ponúkaných možností odpovedí a obslúženie reakcií na používateľské voľby týchto ovládacích prvkov.
Ukážku jedného z možných prístupov obsahuje HlavnáTrieda
nižšie.
Ukážka rozhrania po spustení.
Balíček kviz.7z
na prevzatie 19,70 kB (19,24 KiB), 5. 8. 2020
~
import kvíz.ChybaKvízu; import kvíz.Kvíz; import kvíz.VýberOtázky; import knižnica.GRobot; import knižnica.ObsluhaUdalostí; import knižnica.Tlačidlo; import static knižnica.Svet.*; /** * Táto trieda je ukážkou použitia kvízového rozhrania. Dôležité časti ukážky * sú celý konštruktor (samozrejme, že i s ním bezprostredne súvisiace * deklarácie) a riadky: * * kvíz = new Kvíz(názovSúboru, podrobnosti); * new HlavnáTrieda(); * * ktoré sú umiestnené v hlavnej metóde (main). * * (Ostatné riadky hlavnej metódy sú bonusom, ktorý iba zvyšuje možnosti * spôsobov spustenia a/alebo konfigurovania aplikácie.) * * Názov súboru a argument podrobnosti je rovnako možné zadať napevno. * Napríklad: * * kvíz = new Kvíz("mojkviz.txt", false); */ public class HlavnáTrieda extends GRobot { /** Inštancia kvízu. */ private static Kvíz kvíz; /** Tlačidlo späť. */ private final Tlačidlo späť; /** Tlačidlo ďalej. */ private final Tlačidlo ďalej; /** Tlačidlo opakuj. */ private final Tlačidlo opakuj; /** Tlačidlo koniec. */ private final Tlačidlo koniec; /** Súkromný konštruktor. */ private HlavnáTrieda() { skry(); /** * Najskôr sú vytvorené všetky inštancie tlačidiel: */ späť = new Tlačidlo("Späť"); ďalej = new Tlačidlo("Ďalej"); opakuj = new Tlačidlo("Opakuj"); koniec = new Tlačidlo("Koniec"); /** * Vzápätí sú nastavené ich vlastnosti: */ späť.prilepDole(); späť.prilepVľavo(); späť.skočNa(10, 10); ďalej.prilepDole(); ďalej.prilepVpravo(); ďalej.skočNa(-10, 10); koniec.prilepDole(); koniec.skočNa(0, 10); /** * Potom je definovaná obsluha udalostí: */ new ObsluhaUdalostí() { /** * V tejto reakcii sú obslúžené (takmer) všetky ovládacie prvky * kvízu. Konkrétne všetky, ktoré sú realizované prostredníctvom * tlačidiel. Voľba každého tlačidla má jednoducho za následok * spustenie konkrétnej metódy kvízu, ktoré sú pripravené presne * na tento účel. (V prípade potreby by programátor kvízu mohol * tieto metódy volať pri iných príležitostiach. Napríklad ako * reakciu na kliknutie do nejakého tvaru. Niektoré môžu byť * spúšťané i automaticky – ako to je s metódou ďalšiaOtázka * v tejto ukážke – pozri reakciu potvrdenieÚdajov.) */ @Override public void voľbaTlačidla() { // Keďže metódy kvízu môžu produkovať výnimky, sú všetky // volania uzavreté v štruktúre try-catch: try { // Reakcie na všetky tlačidlá sú relatívne logické, // v tejto ukáže ide predovšetkým o to ukázať, ktorá // metóda sa s čím spája: if (späť.zvolené()) { kvíz.predchádzajúcaOtázka(); } else if (ďalej.zvolené()) { kvíz.ďalšiaOtázka(); } else if (opakuj.zvolené()) { kvíz.opakujKvíz(); } else if (koniec.zvolené()) { kvíz.koniec(); } // Obnovenie obrazovky: obnovTlačidlá(); kresliOtázku(); } catch (ChybaKvízu ch) { // Reakciou na vznik výnimky je vypísanie hlásenia // riešiteľovi, ale v skutočnosti by výnimky mali // vznikať len v prípadoch, keď niečo zle premyslí // programátor alebo je niečo zle zadefinované // v definičnom súbore kvízu, takže teoreticky by // chybové hlásenia nemali zaujímať riešiteľa, ale // programátora a zadávateľa kvízu. Napriek tomu sú // hlásenia formulované tak, aby boli jednoduché // a informovali skôr riešiteľa než uvedené osoby, // pretože sa nedá zaručiť, že ich riešiteľ kvízu nikdy // neuvidí. Ide napríklad o správy typu „Ďalšia otázka // kvízu nejestvuje,“ „Voľbu nie je možné vykonať“ // a podobne. správa(ch.getMessage()); } } /** * V tejto reakcii sa vyhodnotí odpoveď riešiteľa na základe * písmena, ktoré zadal do vstupného riadka. Podstatný je riadok: * * otázka.voľba(…); * * a všetky riadky, ktoré s ním nevyhnutne súvisia. Uvedený * riadok pošle voľbu do aktuálnej otázky, voľba sa zaznamená * a na základe toho môže byť neskôr vykonané vyhodnotenie kvízu. * * Rovnako dobre by mohla byť voľba odpovede vykonaná iným * spôsobom, napríklad klikaním myšou na jednotlivé ponúknuté * možnosti (zobrazené na plátne) alebo na iné oblasti vykreslené * na plátne (odpoveď by mohla byť napríklad výber nejakého prvku * na obrázku) a podobne. Závisí od preferencií (alebo požiadaviek * na) programátora kvízu. */ @Override public void potvrdenieÚdajov() { /** * Táto premenná je predvolene nastavená na logickú hodnotu * lož (false). Logická hodnota pravda (true) sa do nej * vloží ako dôsledok neplatnej voľby, čo môže byť buď * odoslanie prázdneho riadka, alebo vznik výnimky v rámci * niektorej z metód volaných v štruktúre try-catch. * * Cieľom zaznamenania a spracovania neplatnej voľby je * zabezpečiť opätovné zobrazenie vstupného riadka. Bez tejto * akcie by riešiteľ stratil kontakt s aplikáciou – nemal by * možnosť komfortne doriešiť túto otázku (možnosť by dostal * jedine v prípade, že bol kvíz v režime Ľ – ľubovoľne alebo * O – s opakovaním, kedy by mal možnosť sa opätovne k otázke * vrátiť). */ boolean neplatnáVoľba = false; // Pre štruktúru try-catch platia rovnaké informácie ako sú // uvedené v reakcii voľbaTlačidla. try { String voľba = prevezmiReťazec(); if (voľba.isEmpty()) { pípni(); neplatnáVoľba = true; } else { // Prevezme aktuálnu otázku z kvízu: VýberOtázky otázka = kvíz.aktuálnaOtázka(); // Pošle otázke voľbu vypočítanú z ASCII hodnoty // zadaného písmena (prevedeného na malé písmeno). // Písmeno a je nula, b je jednotka a tak ďalej: otázka.voľba(voľba.toLowerCase(). charAt(0) - 'a'); // Testovací výpis (všetky testovacie výpisy by mal // programátor v konečnej verzii svojho kvízu // vymazať; všetky tu bolo ponechané hlavne preto, // lebo je veľká šanca, že budú v priebehu vývoja // kvízu využité): System.out.println("\nVoľba: " + (voľba.toLowerCase().charAt(0) - 'a')); // Automatický prechod na nasledujúcu otázku // a obnovenie obrazovky: kvíz.ďalšiaOtázka(); obnovTlačidlá(); kresliOtázku(); } } catch (ChybaKvízu ch) { // Vypísanie chybového hlásenia riešiteľovi – platí to // isté, čo je uvedené v reakcii voľbaTlačidla. pípni(); správa(ch.getMessage()); neplatnáVoľba = true; } if (neplatnáVoľba) { // Opätovné zobrazenie vstupného riadka: vykonaťNeskôr(() -> začniVstup()); } } /** * V tejto reakcii sa obnovia tlačidlá a prekreslí otázka. Je to * bonusová funkcia, aby boli tlačidlá aj text otázky viditeľné * v širšom spektre veľkostí okna. * * (Na zvýšenie tohto „spektra“ by bolo vhodné upraviť kreslenie * otázky tak, aby sa automaticky zisťovala šírka textu, porovnala * sa so šírkou okna a na základe toho sa otázka vykreslila na * viacerých riadkoch.) */ @Override public void zmenaVeľkostiOkna() { obnovTlačidlá(); kresliOtázku(); } }; /** * Dokončenie inicializácie kvízu – generovanie výberu otázok, * premiešanie otázok aj možností. Tieto akcie môžu byť presunuté do * samostatnej metódy, ktorá by bola volaná ako reakcia na stlačenie * nejakého tlačidla „Nový kvíz,“ prípadne iného prvku, prípadne by * mohla byť volaná automaticky v určitej situácii. */ try { kvíz.generujVýberOtázok(); kvíz.premiešajOtázky(); kvíz.premiešajOdpovedeOtázok(); } catch (ChybaKvízu ch) { správa(ch.getMessage()); } /** * Nasledujúca dvojica príkazov obnoví/prekreslí ovládacie prvky tohto * kvízu a aktuálnu otázku. Je to rovnaká dvojica príkazov, ktorá sa * nachádza v reakcii zmenaVeľkostiOkna. */ obnovTlačidlá(); kresliOtázku(); } // Metóda slúžiaca na „nakreslenie“ aktuálnej otázky, súhrnu pred // ukončením alebo vyhodnotenia kvízu. private void kresliOtázku() { podlaha.vymažGrafiku(); VýberOtázky otázka = kvíz.aktuálnaOtázka(); skočNa(0, hornýOkraj() - 30); if (null == otázka) { if (kvíz.jeKoniec()) { domov(); skoč(0, 50); text("Vyhodnotenie kvízu"); skoč(0, -40); int body = kvíz.početBodov(); int minimum = kvíz.minimumBodov(); int maximum = kvíz.maximumBodov(); text("Počet bodov: " + ("" + body).replace('-', '−') + " z " + maximum); skoč(0, -30); if (body >= 0) text("Úspešnosť: " + ((body * 100) / maximum) + " %"); else text("(Ne)úspešnosť: −" + ((body * 100) / minimum) + " %"); } else { text("Prešli ste všetky otázky."); skoč(0, -40); text("Teraz môžete kvíz ukončiť alebo si ešte raz " + "skontrolovať svoje odpovede."); // Testovacie výpisy: System.out.println(); System.out.println("body: " + kvíz.početBodov()); System.out.println("min.: " + kvíz.minimumBodov()); System.out.println("max.: " + kvíz.maximumBodov()); } } else { text(otázka.textOtázky()); skoč(0, -30); int voľba = otázka.voľba(); int počet = otázka.početOdpovedí(); for (int i = 0; i < počet; ++i) { skoč(0, -30); text((i == voľba ? '•' : '○') + " " + // keď na tomto mieste // namiesto reťazca " " použijeme znak ' ', tak je celá // časť od odrážky (vrátane) po zátvorku (vynímajúc) // chybne sčítaná ako celé čísla a pripojená k zátvorke // a zvyšnému reťazcu, čiže napr.: 9804) … ((char)('a' + i)) + ") " + otázka.textOdpovede(i)); } // Testovacie výpisy: System.out.println(); System.out.println("Voľba: " + otázka.voľba()); System.out.println("body: " + otázka.body()); System.out.println("min.: " + otázka.minimum()); System.out.println("max.: " + otázka.maximum()); } } // Toto je pomocná metóda na aktualizáciu stavov ovládacích prvkov kvízu, // predovšetkým tlačidiel, ale aj iných, ako vstupného riadka. private void obnovTlačidlá() { String navigácia = kvíz.odporúčanáNavigácia(); // Testovací výpis: System.out.println(); System.out.println("Navigácia: " + navigácia); if (-1 != navigácia.indexOf("<")) späť.zobraz(); else späť.skry(); if (-1 != navigácia.indexOf(">")) ďalej.zobraz(); else ďalej.skry(); if (-1 != navigácia.indexOf("O")) opakuj.zobraz(); else opakuj.skry(); if (-1 != navigácia.indexOf("0")) koniec.zobraz(); else koniec.skry(); if (-1 != navigácia.indexOf("E")) vykonaťNeskôr(() -> začniVstup()); else zrušVstup(); } /** * Vstupný bod programu: * * – prevezme názov súboru z prvého argumentu príkazového riadka, * – ak nebol zadaný žiadny názov súboru, načíta sa predvolený súbor * (definovaný v triede Kvíz). * * Voliteľné argumenty -podrobnosti a -vypíšKvíz spôsobia výpis * podrobností o spracovaní súboru resp. výpis spracovaného kvízu. */ public static void main(String[] args) { /*# * Predvolené hodnoty argumentov príkazového riadka: */ boolean podrobnosti = false; boolean vypíšKvíz = false; String názovSúboru = null; for (String arg : args) { // Testovací výpis: System.out.println("arg: " + arg); if (arg.equalsIgnoreCase("-podrobnosti") || arg.equalsIgnoreCase("-podrobnosti=1") || arg.equalsIgnoreCase("-podrobnosti=true")) podrobnosti = true; else if ( arg.equalsIgnoreCase("-vypíš") || arg.equalsIgnoreCase("-vypíš=1") || arg.equalsIgnoreCase("-vypíš=true") || arg.equalsIgnoreCase("-vypíšKvíz") || arg.equalsIgnoreCase("-vypíšKvíz=1") || arg.equalsIgnoreCase("-vypíšKvíz=true") || arg.equalsIgnoreCase("-vypíš_kvíz") || arg.equalsIgnoreCase("-vypíš_kvíz=1") || arg.equalsIgnoreCase("-vypíš_kvíz=true")) vypíšKvíz = true; else { String argLo = arg.toLowerCase(); if (argLo.startsWith("-súbor=")) názovSúboru = arg.substring(7); else if (argLo.startsWith("-názovsúboru=")) názovSúboru = arg.substring(13); else if (argLo.startsWith("-názov_súboru=")) názovSúboru = arg.substring(14); } } // Testovací výpis: System.out.println("podrobnosti: " + podrobnosti); System.out.println("názovSúboru: " + názovSúboru); použiKonfiguráciu("kvíz.cfg"); nekresli(); try { kvíz = new Kvíz(názovSúboru, podrobnosti); if (vypíšKvíz) { farbaTextu(oranžová); vypíš("Názov súboru: "); farbaTextu(tyrkysová); if (null == názovSúboru) { vypíš(Kvíz.PREDVOLENÝ_NÁZOV_SÚBORU); farbaTextu(šedá); vypíšRiadok(" (predvolený)"); } else { vypíšRiadok(názovSúboru); } kvíz.výpisKvízu(); } else new HlavnáTrieda(); } catch (Throwable t) { vypíšRiadok(t); t.printStackTrace(); } kresli(); } }
~
package kvíz; /** * Túto výnimku generuje trieda Kvíz v rôznych situáciách ako napríklad: * neúspech pri pokuse o vygenerovanie výberu náhodných otázok, neúspech pri * pokuse o posunutie na ďalšiu otázku a podobne. * * Výnimka by mala byť vždy zachytená programátorom kvízu a podľa typu chyby * by sa malo upraviť správanie aplikácie alebo prinajmenšom zobraziť * prislúchajúci oznam o zlyhaní riešiteľovi. */ @SuppressWarnings("serial") public class ChybaKvízu extends Exception { /** * Číselná konštanta chybového stavu, ktorý signalizuje, že buď nie * je definovaná žiadna otázka kvízu, alebo nebola spustená metóda * generujVýberOtázok. */ public final static int NEDOSTATOK_OTÁZOK = 1; /** * Číselná konštanta chybového stavu, ktorý signalizuje, že nejestvuje * ďalšia otázka kvízu. */ public final static int NIE_JE_ĎALŠIA_OTÁZKA = 2; /** * Číselná konštanta chybového stavu, ktorý signalizuje, že nejestvuje * predchádzajúca otázka kvízu. */ public final static int NIE_JE_PREDCHÁDZAJÚCA_OTÁZKA = 3; /** * Číselná konštanta chybového stavu, ktorý signalizuje, že voľbu * odpovede práve nie je možné vykonať. */ public final static int VOĽBA_NIE_JE_MOŽNÁ = 4; /** * Číselná konštanta chybového stavu, ktorý signalizuje, že zadaná voľba * nie je platná. */ public final static int NEPLATNÁ_VOĽBA = 5; /** * Číselná konštanta chybového stavu, ktorý signalizuje, že miešanie * otázok alebo odpovedí bolo zamietnuté, pretože práve prebieha riešenie * kvízu. */ public final static int MIEŠANIE_ZAMIETNUTÉ = 6; /** * Číselná konštanta chybového stavu, ktorý signalizuje, že riešenie * kvízu bolo ukončené a voľba odpovede už nie je možná. */ public final static int JE_KONIEC = 7; /** Texty chybových stavov podľa kódov: */ private final static String[] textyChýb = { "Neznáma chyba.", // 0 – rezervované! "Do kvízu neboli vybrané žiadne otázky.", // 1 "Ďalšia otázka kvízu nejestvuje.", // 2 "Predchádzajúca otázka kvízu nejestvuje.", // 3 "Voľbu nie je možné vykonať.", // 4 "Zadaná voľba nie je platná.", // 5 "Nie je možné miešať otázky alebo odpovede počas riešenia kvízu.", // 6 "Voľba odpovede nie je možná, pretože neprebieha riešenie kvízu.", // 7 }; /** * Toto je kód chyby kvízu. Kód je nastavený konštruktorom. Môže nadobúdať * ľubovoľnú celočíselnú hodnotu, ale odporúčané je, aby mal hodnotu * rovnú jednej z vyššie definovaných konštánt (kódov chýb: * NEDOSTATOK_OTÁZOK až JE_KONIEC). */ public final int kód; /** * Predvolený konštruktor, ktorý vytvorí inštanciu chyby kvízu podľa * zadaného kódu chyby. Podľa zadaného kódu chyby je automaticky * nastavený text chybovej správy, ktorý sa dá čítať metódou getMessage. * Ak je kód chyby mimo povoleného rozsahu (v súčasnosti sú povolené * hodnoty 1 až 7, čo sú kódy chýb definované ako konštatny tejto * triedy), tak bude text chyby kvízu nastavený na „Neznáma chyba.“ * * Programátor kvízu môže kódy chýb rozširovať podľa potreby, ale musí * dať pozor na to, že kód 0 je rezervovaný na neznámu chybu. * * @param kód kód chyby v rámci povoleného rozsahu */ public ChybaKvízu(int kód) { super(textyChýb[kód < 0 || kód >= textyChýb.length ? 0 : kód]); this.kód = kód; } /** * Táto metóda vráti kód tejto chyby kvízu. * * @return kód tejto chyby */ public int kód() { return kód; } }
~
package kvíz; /** * Táto trieda uchováva údaje o jednej možnosti odpovede na otázku kvízu. */ public class Odpoveď { /** Atribút uchovávajúci text odpovede. */ private String textOdpovede; /** * Atribút uchovávajúci hodnotu (počet bodov) získateľný za túto odpoveď. */ private int početBodov = 0; /** * Konštruktor odpovede. * * @param textOdpovede znenie tejto možnosti odpovede (platí to isté, * čo pre znenie textu otázky) */ public Odpoveď(String textOdpovede) { this.textOdpovede = textOdpovede; } /** * Metóda slúžiaca na zistenie počtu bodov za túto odpoveď. * * @return bodová hodnota tejto odpovede */ public int početBodov() { return početBodov; } /** * Metóda slúžiaca na určenie počtu bodov za túto odpoveď. * * @param novýPočetBodov nový počet bodov za túto odpoveď */ public void početBodov(int novýPočetBodov) { početBodov = novýPočetBodov; } /** * Táto prekrytá metóda vracia text odpovede. * * Podrobnosti o tomto prekrytí sú zhodné s informáciami uvedenými * v opise prekrytej metódy Otázka.toString. * * @return text odpovede */ @Override public String toString() { return textOdpovede; } }
~
package kvíz; import knižnica.Zoznam; /** * Táto trieda uchováva všetky informácie o jednej otázke kvízu. * (A sprostredkúva prácu s nimi.) */ public class Otázka { /** Atribút uchovávajúci text otázky. */ private String textOtázky; /** * Zoznam všetkých (správnych aj nesprávnych) kvízových odpovedí na * túto otázku (čiže zoznam ponúkaných možností). */ private final Zoznam<Odpoveď> odpovede = new Zoznam<>(); /** * Tento atribút určuje bodovú sankciu, ktorá bude udelená študentovi, * ak nezvolí ani jednu odpoveď (to jest, ak otázku preskočí). Predvolene * nie je stanovená žiadna sankcia. * * V prípade, že je sankcia kladná, nemalo by jej udelenie za preskočenie * otázky zmysel. V takom prípade je táto hodnota započítaná ako odmena * za nepreskočenie tejto otázky. To sa dá využiť na udelenie paušálneho * počtu bodov, ktoré riešiteľ získa vždy, keď sa aspoň pokúsi na otázku * odpovedať (čiže aj v prípade nesprávnej voľby, čo by v inom prípade * bolo realizovateľné udelením kladného počtu bodov v rôzneh výške za * každú možnosť). */ private int sankcia = 0; /** * Konštruktor otázky. * * @param textOtázky znenie tejto otázky (predvolene sa predpokladá * forma čistého textu, ale programátor kvízu si môže * definovať vlastné kódy v texte, ktoré budú mať * špecifický význam) */ public Otázka(String textOtázky) { this.textOtázky = textOtázky; } /** * Táto metóda aktualizuje sankciu za preskočenie otázky, resp. odmenu * za jej nepreskočenie. Ak je zadaná hodnota záporná, tak plní úlohu * sankcie. Ak je kladná, tak ide o odmenu. * * @param nováSankcia bodová výška sankcie, resp. odmey */ public void aktualizujSankciu(int nováSankcia) { sankcia = nováSankcia; } /** * Táto metóda poskytne údaj o bodovej sankcii udelenej za preskočenie * otázky, resp. o odmene za jej nepreskočenie. Záporná hodnota znamená * sankciu, kladná odmenu. * * @return aktuálna výška sankcie, resp. odmey */ public int sankcia() { return sankcia; } /** * Pridá novú odpoveď k tejto otázke. * * @param textOdpovede text novej možnosti odpovede */ public void pridajOdpoveď(String textOdpovede) { odpovede.pridaj(new Odpoveď(textOdpovede)); } /** * Táto metóda poskytne počet definovaných odpovedí pre túto otázku. * * @return aktuálny počet možností odpovedí */ public int početOdpovedí() { return odpovede.dĺžka(); } /** * Táto metóda poskytne inštanciu naposledy definovanej možnosti odpovede * na túto otázku. Ak nie je žiadna definovaná, tak metóda vráti hodnotu * null. * * @return inštancia poslednej odpovede na túto otázku alebo null */ public Odpoveď poslednáOdpoveď() { if (odpovede.prázdny()) return null; return odpovede.posledný(); } /** * Táto metóda poskytne inštanciu možnosti odpovede na túto otázku podľa * určeného poradového čísla. Ak taká odpoveď nejestvuje, tak metóda * vráti hodnotu null. * * @param číslo index odpovede, s ktorou chceme pracovať * @return inštancia odpovede s indexom číslo alebo null */ public Odpoveď odpoveďČíslo(int číslo) { if (číslo >= odpovede.dĺžka()) return null; return odpovede.daj(číslo); } /** * Táto metóda vykoná aktualizáciu bodového ohodnotenia poslednej * možnosti odpovede (pridanej napríklad v priebehu čítania * konfiguračného súboru). Ak nemá táto otázka definované žiadne možnosti * odpovedí, tak táto metóda nevykoná žiadnu akciu. * * @param novýPočetBodov nový počet bodov poslednej pridanej možnosti * odpovede */ public void aktualizujBodyPoslednejOdpovede(int novýPočetBodov) { if (!odpovede.prázdny()) odpovede.posledný().početBodov(novýPočetBodov); } /** * Táto metóda vypočíta a vráti najnižší možný počet bodov, ktorý je * možné získať odpovedaním na túto otázku, pričom metóda počíta aj * s prípadnou sankciou za preskočenie otázky. * * @return najnižší možný počet bodov pridelený za odpovedanie, resp. * preskočenie tejto otázky */ public int vypočítajMinimum() { int min; if (odpovede.jePrázdny()) min = 0; else { min = odpovede.daj(0).početBodov(); for (int i = 1; i < odpovede.dĺžka(); ++i) { int body = odpovede.daj(i).početBodov(); if (body < min) min = body; } } if (sankcia < min) min = sankcia; if (0 < min) return 0; return min; } /** * Táto metóda vypočíta a vráti najvyšší možný počet bodov, ktorý je * možné získať odpovedaním na túto otázku. Metóda počíta aj s prípadnou * odmenou za odpovedanie na otázku (za jej nepreskočenie). * * @return najvyšší možný počet bodov získaný za odpovedanie na túto * otázku (ak ju riešiteľ nepreskočí) */ public int vypočítajMaximum() { int max; if (odpovede.jePrázdny()) max = 0; else { max = odpovede.daj(0).početBodov(); for (int i = 1; i < odpovede.dĺžka(); ++i) { int body = odpovede.daj(i).početBodov(); if (body > max) max = body; } } if (sankcia > 0) max += sankcia; else if (max < 0) return 0; return max; } /** * Táto prekrytá metóda vracia text otázky. * * Ide o prekrytie metódy, ktorú má definovaný každý objekt Javy. * Predvolene ju Java používa na vyjadrenie objektu v textovej podobe * (alebo na prevod do textovej podoby, ak chceme). * * To je využiteľné napríklad pri výpise informácie o objekte metódami * System.out.println alebo Svet.vypíšRiadok. * * Technický detail: Ak metóda toString nie je prekrytá, tak predvolene * vracia reťazec, ktorý obsahuje názov triedy, znak „at“ (@) a hešovací * kód v šestnástkovej číselnej sústave. V mnohých prípadoch je táto * metóda volaná úplne automaticky (vnútornými mechanizmami virtuálneho * stroja Javy). * * @return text otázky */ @Override public String toString() { return textOtázky; // Pôvodne by sa zobrazilo: // getClass().getName() + "@" + Integer.toHexString(hashCode()); } }
~
package kvíz; import static knižnica.Svet.náhodnéCeléČíslo; /** * Z definovanej množiny otázok sa má do kvízu vybrať podmnožina, ktorá bude * predložená riešiteľovi. Toto je trieda, ktorá slúži na uchovanie jednej * zo zvolených otázok. Séria inštancií tejto triedy bude tvoriť konkrétny * kvíz predložený riešiteľovi. */ public class VýberOtázky { /** * Toto je inštancia zvolenej otázky, čiže otázky, ktorú reprezentuje * tento výber (táto inštancia výberu otázky). Inštancia je nastavená * počas konštrukcie tohto objektu a je nemenná (finálna = konštantná). */ public final Otázka otázka; /** * Tento príznak určuje, či bola táto otázka v rámci vypĺňania kvízu * aspoň raz navštívená. (Ak boli navštívené všetky otázky, tak bude * dovolené riešiteľovi kvíz odoslať.) */ private boolean navštívená = false; /** * Tento atribút si bude pamätať riešiteľovu voľbu. Predvolená hodnota * (-1) znamená, že riešiteľ neoznačil žiadnu možnosť (napríklad otázku * preskočil, odložil na neskoršie riešenie, prípadne ešte nestihol * navštíviť). */ private int voľba = -1; /** * Toto pole slúži na vnútornú úpravu poradia odpovedí. */ private final int poradieOdpovedí[]; /** * Konštruktor, ktorý prijíma inštanciu otázky. Otázka je nemennou * inštanciou tejto triedy, to znamená, že každá inštancia typu * VýberOtázky musí mať vopred stanovenú inštanciu typu Otázka, ktorá * sa počas celého bytia objektu typu VýberOtázky nebude meniť. Jeden * VýberOtázky = jednoznačné priradenie k typu Otázka. (Pozor! To, že * sa otázky nebudú v rámci kvízu opakovať nie je zabezpečené tu. To * je zabezpečené o niečo komplikovanejším mechanizmom.) * * @param otázka inštancia otázky, ktorú reprezentuje tento výber */ public VýberOtázky(Otázka otázka) { this.otázka = otázka; int počet = otázka.početOdpovedí(); poradieOdpovedí = new int[počet]; for (int i = 0; i < počet; ++i) poradieOdpovedí[i] = i; } /** * Táto metóda premieša poradie odpovedí tohto výberu otázky. */ public void premiešajOdpovede() { int počet = otázka.početOdpovedí() - 1; for (int i = 0; i <= počet; ++i) { int j = (int)náhodnéCeléČíslo(0, počet); int k = (int)náhodnéCeléČíslo(0, počet); int l = poradieOdpovedí[j]; poradieOdpovedí[j] = poradieOdpovedí[k]; poradieOdpovedí[k] = l; } } /** * Táto metóda poskytne počet odpovedí tohto výberu otázky. * * @return počet možností odpovede na otázku, ktorú reprezentuje tento * výberu */ public int početOdpovedí() { return poradieOdpovedí.length; } /** * Táto metóda vráti text otázky tohto výberu. * * @return znenie otázky, ktorú reprezentuje tento výber */ public String textOtázky() { return otázka.toString(); } /** * Táto metóda vráti text odpovede určenej poradovým číslom. Metóda berie * do úvahy prípadné vnútorné premiešanie odpovedí tohto výberu. * * @param číslo poradové číslo (index) možnosti odpovede tohto výberu * (to jest index platný pre výber, ktorý môže možnosti * pôvodnej otázky premiešať) * @return text zadanej možnosti odpovede tohto výberu otázky */ public String textOdpovede(int číslo) { return otázka.odpoveďČíslo(poradieOdpovedí[číslo]).toString(); } /** * Táto metóda vráti body odpovede určenej poradovým číslom. Metóda berie * do úvahy prípadné vnútorné premiešanie odpovedí tohto výberu. * * @param číslo poradové číslo (index) možnosti odpovede tohto výberu * (to jest index platný pre výber, ktorý môže možnosti * pôvodnej otázky premiešať) * @return bodové ohodnotenie zadanej možnosti odpovede tohto výberu * otázky */ public int bodyOdpovede(int číslo) { return otázka.odpoveďČíslo(poradieOdpovedí[číslo]).početBodov(); } /** * Táto metóda označí túto otázku (v zmysle výberu otázky) za navštívenú. * (Čo môže mať vplyv na priebeh kvízu.) */ public void navštívená() { navštívená = true; } /** * Táto metóda overí, či riešiteľ túto otázku navštívil, alebo nie. * * @return ak bol tento výber otázky navštívený, tak je návratovou * hodnotou true, inak false */ public boolean bolaNavštívená() { return navštívená; } /** * Táto metóda slúži na nastavenie voľby podľa poradového čísla odpovede * (možnosti). Ide o voľbu zadanú riešiteľom, čiže o zvolenie konkrétnej * možnosti odpovede. * * @param voľba poradové číslo možnosti, ktorú zadal (zvolil) riešiteľ */ public void voľba(int voľba) throws ChybaKvízu { if (voľba > poradieOdpovedí.length) throw new ChybaKvízu(ChybaKvízu.NEPLATNÁ_VOĽBA); this.voľba = voľba; } /** * Táto metóda slúži na overenie hodnoty riešiteľom zvolenej možnosti * odpovede. Záporná hodnota znamená, že riešiteľ ešte nezvolil žiadnu * možnosť. * * @return poradové číslo možnosti, ktorú zvolil riešiteľ alebo −1, ak * zatiaľ nebola vykonaná žiadna voľba */ public int voľba() { return voľba; } /** * Táto metóda slúži na vrátenie počtu bodov, ktoré riešiteľ získal * za tento výber otázky (podľa toho, či otázku (ne)preskočil a ktorú * odpoveď zvolil). * * @return počet bodov pridelený za túto otázku */ public int body() { int body = 0; if (voľba < 0) { if (otázka.sankcia() < 0) return otázka.sankcia(); return 0; } else if (otázka.sankcia() > 0) body = otázka.sankcia(); body += otázka.odpoveďČíslo(poradieOdpovedí[voľba]).početBodov(); return body; } /** * Táto metóda slúži na vrátenie minimálnej hodnoty počtu bodov, ktoré * je možné získať za otázku v tomto výbere. * * @return minimálny počet bodov za túto otázku */ public int minimum() { return otázka.vypočítajMinimum(); } /** * Táto metóda slúži na vrátenie maximálnej hodnoty počtu bodov, ktoré * je možné získať za otázku v tomto výbere. * * @return maximálny počet bodov za túto otázku */ public int maximum() { return otázka.vypočítajMaximum(); } /** * Prekrytie tejto metódy zabezpečí korekciu toho, aby sa pri porovnávaní * inštancie tohto výberu otázky s inštanciou otázky porovnávala priamo * zhoda so zadanou otázkou (t. j. s atribútom otázka, a nie s touto * inštanciou ako celkom). * * To je výhodné, keď budeme chcieť overiť, či je tento výber otázky * zhodný s niektorou otázkou. Je logické, že keď budeme také porovnanie * potrebovať vykonať, nebudeme chcieť overiť zhodu medzi inštanciou * výberu otázky a inštanciou otázky, pretože tieto dve inštancie by sa * (technicky) nikdy nemohli zhodovať. V skutočnosti budeme chcieť * zistiť, či sa tento výber otázky vzťahuje k zadanej otázke. Keďže * takéto porovnanie by sa neudialo automaticky, je definované toto * prekrytie. * * V osatných prípadoch (napríklad pri porovnaní s inou inštanciou * výberu) je zavolaná metóda nadradenej triedy (java.lang.Object). * * @param obj referenčný objekt na porovnanie * @return ak je referenčný objekt zhodný s týmto objektom (v súlade * s opisom), tak metóda vráti true, inak false * * @see https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object- */ @Override public boolean equals(Object obj) { if (obj instanceof Otázka) // Rýchlejšie by bolo toto porovnanie: // return otázka == obj; // Ale korektnejšie je toto porovnanie: return otázka.equals(obj); // V ostatných prípadoch sa postupuje štandardne: return super.equals(obj); } /** * Prekrytie tejto metódy je vyžadované novšími verziami prekladačov * Javy v prípade, že prekrývame metódu equals (pozri vyššie). Keďže * v skutočnosti nemáme k dispozícii lepšiu verziu vypočítania * hašovacieho kódu, len voláme nadradenú metódu (t. j. metódu nadradenej * triedy „super,“ ktorou je v tomto prípade java.lang.Object). * * @return hodnota hašovacieho kódu pre tento výber otázky * * @see https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode-- */ @Override public int hashCode() { return super.hashCode(); } }
~
package kvíz; import knižnica.GRobot; import knižnica.Zoznam; import static knižnica.Svet.*; import java.io.IOException; /** * Toto je ústredná trieda balíčka kvíz. Konštruktor tejto triedy prečíta * konfiguráciu zo zadaného textového súboru a v prípade, že nenastala chyba * kvíz „spustí.“ Osoba, ktorá bude absolvovať kvíz je v tejto dokumentácii * označovaná termínom „riešiteľ.“ * * (Rozhranie interagujúce s riešiteľom zabezpečuje programátor, ktorý tento * balíček používa. Tento balíček sa stará len o vnútornú konzistenciu * a koordináciu kvízu.) */ public class Kvíz extends GRobot { /** * Zoznam všetkých kvízových otázok. (Prečítaných z konfiguračného * súboru.) */ private final Zoznam<Otázka> otázky = new Zoznam<>(); /** * Zoznam s výberom otázok, ktoré boli vybrané do aktuálneho kvízu, * to jest kvízu, ktorý bol vygenerovaný pre konkrétneho riešiteľa. */ private final Zoznam<VýberOtázky> výber = new Zoznam<>(); /** * Tento atribút je ukazovateľ aktuálnej otázky kvízu, to jest otázky vo * výbere, na ktorej sa práve nachádza riešiteľ. */ private int otázkaVýberu = 0; /** * Tento atribút si pamätá, koľko ráz bolo riešenie aktuálneho kvízu * zopakované riešiteľom (prostredníctvom prislúchajúceho ovládacieho * prvku, napríklad tlačidla s popisom „Opakuj“). */ private int početOpakovaní = 0; /** * Pravdivá hodnota tohto atribútu (true) znamená, že kvíz bol ukončený * a má sa zobraziť jeho vyhodnotenie. (Riešiteľ sa už k riešeniu nemôže * vrátiť.) */ private boolean koniec = true; /** * Tento špeciálny druh vymenovacej triedy (enum) slúži na uchovanie * aktuálneho stavu spracovania konfiguračného súboru. Má výstižne * pomenované entity (prvky), takže program je s jej použitím dostatočne * dobre čitateľný. */ public enum StavSpracovania { /** Stav signalizujúci chybu. */ CHYBA, /** * Stav signalizujúci, že na najbližšom neprázdnom riadku * konfiguračného súboru je očakávaný počet otázok, ktorý má byť * vygenerovaný riešiteľovi z celej množiny definovaných otázok. * Nula znamená všetky otázky. * * (Celkový počet otázok kvízu nie je explicitne zadaný. Je zistený * automaticky podľa počtu definícií otázok zadaných v konfiguračnom * súbore. Ak súbor neobsahuje dostatočný počet definícií otázok * (čiže ak je celkový počet otázok menší ako hodnota na riadku * POČET_OTÁZOK), tak budú riešiteľovi vygenerované všetky dostupné * otázky v náhodnom poradí.) */ POČET_OTÁZOK, /** * Stav signalizujúci, že na najbližšom neprázdnom riadku * konfiguračného súboru je očakávané konfiguračné písmeno (P, * O alebo L – v súlade s dokumentáciou nižšie). */ KONFIGURAČNÉ_PÍSMENO, /** * Stav signalizujúci, že na najbližšom neprázdnom riadku * konfiguračného súboru je očakávané znenie otázky. */ ZNENIE_OTÁZKY, /** * Stav signalizujúci, že na najbližšom neprázdnom riadku * konfiguračného súboru je očakávaná hodnota sankcie za preskočenie * otázky, resp. odmena za jej nepreskočenie. */ SANKCIA_OTÁZKY, /** * Stav signalizujúci, že na najbližšom neprázdnom riadku * konfiguračného súboru je očakávané znenie možnosti odpovede * na otázku. */ ZNENIE_ODPOVEDE, /** * Stav signalizujúci, že na najbližšom neprázdnom riadku * konfiguračného súboru je očakávaná hodnota bodového zisku * (prípadne straty) za zvolenie možnosti odpovede (vždy sa to * vzťahuje na naposledy definovanú možnosť odpovede na otázku). */ BODY_ODPOVEDE, } /** * Toto je inštancia vyššie definovanej vymenovacej triedy. (Tento atribút * je používaný na koordináciu čítania konfiguračného súboru kvízu.) */ private StavSpracovania stavSpracovania = StavSpracovania.CHYBA; /** * Toto je konštanta s predvoleným názvom súboru definície kvízu. Tento * názov bude použitý, ak používateľ nezadá žiadny názov súboru. */ public final static String PREDVOLENÝ_NÁZOV_SÚBORU = "kvíz.txt"; /** * Nastavenie výpisu podrobností počas čítania. Nastavenie hodnoty tohto * atribútu na true (parametrom konštruktora, pretože čítanie vykonáva * priamo konštruktor) bude znamenať, že počas čítania a analýzy * konfiguračného súboru budú na vnútornú konzolu sveta programovacieho * rámca GRobot vypisované informácie o každom spracovanom riadku. Ide * v podstate o režim ladenia kvízu, ktorý pomáha odhaliť chyby * v definícii konfiguračného súboru. Prevolená je hodnota false. Vtedy * bude konfigurácia prečítaná v „tichom režime“ – priebežné informácie * o dianí sa nebudú zobrazovať. */ private boolean podrobnosti = false; /** * Tento súkromný atribút slúži na uloženie skutočného názvu * konfiguračného súboru, ktorý bol prečítaný. Je to vnútorný atribút * slúžiaci najmä na výpis rôznych hlásení o chybách spracovania. */ private String názovSúboru; /** * Počet otázok, ktorý má byť vygenerovaný riešiteľovi. (Predvolená * hodnota 0 znamená všetky otázky. Prípadný výskyt zápornej hodnoty * má rovnaký efekt ako 0.) */ private int početOtázok = 0; /** * Konfiguračné písmeno určujúce správanie sa kvízu (počas riešenia): * * P – priamočiaro. Otázky sa budú respondentovi zobrazovať postupne. * Respondent sa nebude môcť vrátiť k predchádzajúcej otázke. * O – s opakovaním. Otázky budú kladené priamočiaro, ale po prejdení * všetkých otázok dostane riešiteľ možnosť prejsť si kvíz ešte * raz (aby mohol prípadne upraviť svoje odpovede). * L alebo Ľ – ľubovoľne. Respondent sa môže počas vypĺňania ľubovoľne * pohybovať medzi otázkami vpred a vzad, no kvíz môže potvrdiť * až keď každú otázku navštívi aspoň raz. * * Predvolené je písmeno L. * * Podstatné je povedať to, že toto „správanie sa“ je len spôsob * vnútornej koordinácie kvízu. Veľká časť implementácie musí byť * vykonaná programátorom, ktorý tento balíček používa (programátorom * rozhrania kvízu). Kvíz komunikuje prostredníctvom rôznych metód: * aktuálnaOtázka, odporúčanáNavigácia, početBodov, jeKoniec atď., ale * ich výsledky musí spracovať programátor rozhrania. */ private char konfiguračnéPísmeno = 'L'; /** * Tento atribút bude obsahovať inštanciu aktuálnej (teda práve * definovanej) otázky. */ private Otázka aktuálnaOtázka; /** * Tento atribút uchováva číslo riadka, ktorý bol práve prečítaný * z konfiguračného súboru kvízu. Poznať číslo riadka je užitočné pri * hlásení chybových stavov (pri ladení definície/konfigurácie nového * kvízu). */ private int čísloRiadka; /** * Tento atribút uchováva aktuálny riadok textu prečítaný * z konfiguračného súboru kvízu. Atribút zabezpečuje komunikáciu medzi * konštruktorom a súkromnými metódami inicializujúcimi inštanciu kvízu * (čítajÚdaje, čísloNaRiadku, spracujÚdaje, prípadne ďalších). */ private String riadok; /** * Základný konštruktor kvízu. Pracuje rovnako ako nasledujúca verzia * konštruktora (Kvíz(názovSúboru, podrobnosti)), ibaže výpis podrobností * (ladiacich informácií) je predvolene vypnutý. * * @param názovSúboru názov konfiguračného súboru s kvízom */ public Kvíz(String názovSúboru) { this(názovSúboru, false); } /** * Konštruktor kvízu umožňujúci zapnúť ladenie. Vytvorí nový kvíz podľa * definície uloženej v súbore so zadaným názvom. Parameter podrobnosti * umožňuje zapnúť režim ladenia, v ktorom sú na vnútornú konzolu sveta * programovacieho rámca GRobot vypísané všetky podrobnosti o analýze * súboru. * * @param názovSúboru názov konfiguračného súboru s kvízom * @param podrobnosti atribút určujúci, či majú byť počas čítania * vypisované podrobnosti o spracovaní na vnútornú * konzolu sveta programovacieho rámca GRobot */ public Kvíz(String názovSúboru, boolean podrobnosti) { // Robot inštancie kvízu je predvolene skrytý. skry(); /*# * Hodnota atribútu podrobnosti nastavená na true zapne podrobný výpis * priebehu spracovania konfiguračného súboru. */ this.podrobnosti = podrobnosti; /*# * Overenie názvu súboru. Ak nie je zadaný žiadny názov, tak táto * inštancia prečíta (resp. sa o to pokúsi) súbor s názvom zadaným * v konštante PREDVOLENÝ_NÁZOV_SÚBORU (v súčasnosti je to kvíz.txt). */ if (null == názovSúboru || názovSúboru.isEmpty()) názovSúboru = PREDVOLENÝ_NÁZOV_SÚBORU; /*# * Najprv uložíme meno súboru do vnútorného atribútu a inicializujeme * ďalšie atribúty. */ this.názovSúboru = názovSúboru; aktuálnaOtázka = null; čísloRiadka = 0; riadok = null; /*# * Hneď potom prejdeme na spracovanie textového súboru: */ spracujSúbor(); } /** * Táto súkromná metóda slúži na otvorenie a zavretie konfiguračného * súboru kvízu, pričom ona sama volá ďalšiu súkromnú metódu slúžiacu * na sekvenčné čítanie údajov zo súboru. */ private void spracujSúbor() { /*# * Toto je prvá úroveň zachytávania výnimiek. Na tejto úrovni sú * zachytené výnimky, ktoré vznikli pri otváraní alebo zatváraní * súboru. Operácia zavretia súboru je uložená v samostatnej * (vnorenej) vrstve, aby mohla byť prípadná ňou spôsobená chyba * (vrhnutá výnimka) jednoznačne odlíšená. */ try { súbor.otvorNaČítanie(názovSúboru); if (podrobnosti) { farbaTextu(purpurová); vypíšRiadok("Čítam konfiguráciu:"); } /*# Prvým stavom bude „počet otázok.“ */ stavSpracovania = StavSpracovania.POČET_OTÁZOK; /*# * Nasledujúca metóda prečíta všetky údaje z konfiguračného * súboru kvízu, počas čoho bude volať ďalšie súkromné metódy * slúžiace na ich spracovanie. */ čítajÚdaje(); try { /*# Zavretie konfiguračného súboru. */ súbor.zavri(); } catch (IOException e) { // Vstupno-výstupná chyba vzniknutá počas zatvárania súboru // bude vypísaná (červenou farbou) bez ohľadu na hodnotu // atribútu „podrobnosti“ (pozri konštruktory). farbaTextu(červená); vypíšRiadok("Chyba počas zavretia súboru v stave čítania " + "na riadku ", čísloRiadka, "."); } } catch (java.io.FileNotFoundException e) { // Ak súbor nejestvuje, tak bude vzniknutá chyba vypísaná // (červenou farbou) bez ohľadu na hodnotu atribútu „podrobnosti“ // (pozri konštruktory). farbaTextu(červená); vypíšRiadok("Chyba počas otvorenia súboru. Súbor „", názovSúboru, "“ nejestvuje."); } catch (IOException e) { // Iná vstupno-výstupná chyba vzniknutá počas otvárania súboru // bude vypísaná rovnakým spôsobom ako predchádzajúca (červenou // farbou a bez ohľadu na hodnotu atribútu „podrobnosti“), ale // vo výpise budú uvedené: trieda chyby a text chyby // (v angličtine). farbaTextu(červená); vypíšRiadok("Neznáma chyba počas otvorenia súboru „", názovSúboru, "“: ", e.getClass().getName(), ": ", e.getMessage(), "."); } catch (Exception e) { // Všetky „iné chyby“ budú vypísané vo všeobecnom tvare: // „Úroveň #: «trieda chyby»: «text chyby» na riadku #.“ // (Pričom toto je úroveň 1. Tiež bez ohľadu na hodnotu // atribútu „podrobnosti.“) farbaTextu(červená); vypíšRiadok("Úroveň 1: ", e.getClass().getName(), ": ", e.getMessage(), " na riadku ", čísloRiadka, "."); } } /** * Táto súkromná metóda slúži na sekvenčné čítanie údajov * z konfiguračného súboru kvízu, pričom ona sama volá ďalšie súkromné * metódy, ktoré slúžia na zmenu stavu spracovania a/alebo spracovanie * prečítaných údajov. */ private void čítajÚdaje() { /*# * Toto je druhá úroveň zachytávania výnimiek. Na tejto úrovni sú * zachytené výnimky, ktoré vznikli pri čítaní súboru. */ try { while (null != (riadok = súbor.čítajRiadok())) { ++čísloRiadka; if (podrobnosti) { vypíšRiadok(); farbaTextu(zelená); vypíšRiadok("Stav: ", stavSpracovania); } if (riadok.isEmpty()) /*# * Volanie tejto metódy vykoná spracovanie prázdneho * riadka, čo v podstate zahŕňa len správnu zmenu stavu * spracovania. */ spracujPrázdnyRiadok(); else /*# * Volanie tejto metódy vykoná spracovanie údajov * z neprázdnych riadkov. */ spracujÚdaje(); } } catch (IOException e) { // Chyba vzniknutá počas čítania bude vypísaná (červenou farbou; // priebežne počas spracovania) a to bez ohľadu na hodnotu // atribútu „podrobnosti“ (pozri konštruktory). farbaTextu(červená); vypíšRiadok("Chyba počas čítania údajov na riadku ", čísloRiadka, "."); } catch (Exception e) { // Všetky „iné chyby“ budú vypísané vo všeobecnom tvare: // „Úroveň #: «trieda chyby»: «text chyby» na riadku #.“ // (Pričom toto je úroveň 2. Tiež bez ohľadu na hodnotu // atribútu „podrobnosti.“) farbaTextu(červená); vypíšRiadok("Úroveň 2: ", e.getClass().getName(), ": ", e.getMessage(), " na riadku ", čísloRiadka, "."); } if (podrobnosti) { vypíšRiadok(); farbaTextu(oranžová); vypíšRiadok("Koniec súboru."); } } /** * Táto súkromná metóda slúži na spracovanie prázdneho riadka prečítaného * z konfiguračného súboru kvízu. Jej jedinou úlohou je zabezpečenie * správnej zmeny stavu spracovania v závislosti od aktuálneho stavu * spracovania. * * To v krátkosti znamená, že prázdne riadky slúžia na zmenu stavu * spracovania. * * Prázdny riadok znamená „nezadaný údaj“ alebo „oddeľovač.“ Vo väčšine * prípadov ide o nezadaný údaj, ktorému bude ponechaná predvolená * hodnota. Bez ohľadu na to je nevyhnutné vykonanť správnu zmenu stavu * spracovania súboru, inak by spracovanie nemohlo korektne pokračovať. * * (Ak by sme nezmenili stav spracovania, algoritmus spracúvajúci súbor * by naďalej očakával ten istý druh údajov a najbližší neprázdny riadok * by bol spracovaný nesprávne.) * * Poznámka: Metóda prednostne spracúva tie stavy, ktorých * pravdepodobnosť výskytu je vyššia. */ private void spracujPrázdnyRiadok() { if (podrobnosti) { farbaTextu(tyrkysová); vypíšRiadok("Prázdny riadok ", čísloRiadka, "."); } /*# * Prázdne riadky budú ovplyvňovať stav podľa nasledujúcich pravidiel: */ switch (stavSpracovania) { /*# * Ak je aktuálny stav spracovania „sankcia otázky,“ tak prázdny * riadok znamená, že sankcia nie je (nechce byť) definovaná (rovnaký * efekt má zadanie nuly do tohto riadka). Ďalším stavom spracovania * je „znenie odpovede.“ */ case SANKCIA_OTÁZKY: stavSpracovania = StavSpracovania.ZNENIE_ODPOVEDE; break; /*# * Ak je aktuálny stav spracovania „znenie odpovede,“ tak prázdny * riadok ukončí definíciu otázky, ale len v prípade, ak má otázka * definované aspoň dve odpovede. (Na toto obmedzenie nesmie zabúdať * zadávateľ kvízu, inak bude ním vytvorený konfiguračný súbor * nesprávne spracovaný.) */ case ZNENIE_ODPOVEDE: if (aktuálnaOtázka.početOdpovedí() >= 2) { stavSpracovania = StavSpracovania.ZNENIE_OTÁZKY; prekresli(); } break; /*# * Ak je aktuálny stav spracovania „body odpovede,“ tak prázdny * riadok bude znamenať počet bodov rovný nule a spracovanie prejde * na čítanie ďalšej odpovede – to znamená, že na ukončenie definície * otázky budú potrebné dva prázdne riadky. */ case BODY_ODPOVEDE: stavSpracovania = StavSpracovania.ZNENIE_ODPOVEDE; break; /*# * Zo stavu spracovania „počet otázok“ sa prejde na stav * „konfiguračné písmeno.“ (Toto je úplne prvý stav a vyskytuje * sa len raz.) */ case POČET_OTÁZOK: stavSpracovania = StavSpracovania.KONFIGURAČNÉ_PÍSMENO; break; /*# * Zo stavu spracovania „konfiguračné písmeno“ sa prejde na stav * „znenie otázky.“ (Toto je druhý stav v poradí spracovania. * Vyskytuje sa len raz.) */ case KONFIGURAČNÉ_PÍSMENO: stavSpracovania = StavSpracovania.ZNENIE_OTÁZKY; prekresli(); break; } } /** * Toto je pomocná súkromná metóda, ktorá slúži na prevedenie prvého * nájdeného číselného reťazca na riadku na celočíselnú hodnotu (typu * int). * * @return celočíselná hodnota (ak sa obsah riadka nedá previesť, tak je * vrhnutá výnimka NumberFormatException) * * @throws NumberFormatException ak riadok neobsahuje údaj v takom * tvare, ktorý sa dá previesť na celé číslo */ private int čísloNaRiadku() { // Nájdem na riadku medzeru: int indexOf = riadok.indexOf(' '); // Ak sa medzera našla, prevedie sa na číslo len časť reťazca po // prvú medzeru: if (-1 != indexOf) return Integer.parseInt(riadok.substring(0, indexOf)); // Inak sa prevedie celý reťazec: return Integer.parseInt(riadok); } /** * Táto súkromná metóda slúži na spracovanie údajov v neprázdnych * riadkoch prečítaných z konfiguračného súboru kvízu. Údaje sú * spracúvané podľa aktuálneho stavu spracovania. */ private void spracujÚdaje() { if (podrobnosti) { farbaTextu(modrá); vypíšRiadok("Riadok ", čísloRiadka, ": ", riadok); } /*# * Toto je tretia úroveň zachytávania výnimiek. Na tejto úrovni sú * zachytené výnimky, ktoré vznikli pri spracovaní údajov. */ try { /*# * Podľa stavu sa vykoná prislúchajúca činnosť, ktorá zahŕňa * zmenu stavu: */ switch (stavSpracovania) { /*# * V stave spracovania „počet otázok“ je riadok prevedený * na číselnú hodnotu určujúcu počet otázok kvízu, ktoré budú * predložené riešiteľovi. * * (Zmysluplné sú nezáporné hodnoty menšie alebo rovné celkovému * počtu otázok, ktorý je zistený automaticky. Väčšie hodnoty sú * interpretované tak, že riešiteľovi majú byť predložené všetky * otázky kvízu, pretože počet otázok na vyradenie bude záporný. * V opise atribútu početOtázok je uvedené, že nulová alebo * záporné hodnoty tohto atribútu sú iterpretované rovnako. * Vďaka tomu chybná hodnota tohto atribútu nespôsobí chybu * spracovania.) * * Stav spracovania sa zmení na „konfiguračné písmeno.“ */ case POČET_OTÁZOK: stavSpracovania = StavSpracovania.KONFIGURAČNÉ_PÍSMENO; početOtázok = čísloNaRiadku(); if (podrobnosti) { farbaTextu(hnedá); vypíšRiadok("Počet otázok: ", početOtázok); } break; /*# * V stave spracovania „konfiguračné písmeno“ je brané do úvahy * len prvé písmeno (znak) na riadku. Zvyšok riadka je * ignorovaný. Prvé písmeno (znak) je porovnané so znakmi * z povolenej množiny konfiguračných znakov (POL). Ak nepatrí * do množiny, tak bude konfigurácia automaticky nastavená na * predvolenú hodnotu L. (Preto by bolo zbytočné zaradiť znak Ľ * do povolenej množiny znakov.) * * Stav spracovania sa zmení na „znenie otázky.“ */ case KONFIGURAČNÉ_PÍSMENO: stavSpracovania = StavSpracovania.ZNENIE_OTÁZKY; konfiguračnéPísmeno = riadok.charAt(0); switch (konfiguračnéPísmeno) { case 'P': case 'O': case 'L': break; case 'p': konfiguračnéPísmeno = 'P'; break; case 'o': konfiguračnéPísmeno = 'O'; break; default: konfiguračnéPísmeno = 'L'; } if (podrobnosti) { farbaTextu(hnedá); vypíšRiadok("Konfiguračné písmeno: ", konfiguračnéPísmeno); } prekresli(); break; /*# * V stave spracovania „znenie otázky“ je riadok priamo poslaný * do novej inštancie otázky ako jej znenie. * * Stav spracovania sa zmení na „sankcia otázky.“ */ case ZNENIE_OTÁZKY: stavSpracovania = StavSpracovania.SANKCIA_OTÁZKY; aktuálnaOtázka = new Otázka(riadok); otázky.pridaj(aktuálnaOtázka); if (podrobnosti) { farbaTextu(hnedá); vypíšRiadok("Nová otázka: ", aktuálnaOtázka); } break; /*# * V stave spracovania „sankcia otázky“ je riadok prevedený na * číselnú hodnotu, ktorá je poslaná do otázky metódou * aktualizujSankciu. * * (Poznámka: Záporná sankcie určuje počet trestných bodov * udelených za preskočenie otázky a kladná hodnota odmenu * udelenú za nepreskočenie otázky.) * * Stav spracovania sa zmení na „znenie odpovede.“ (Poznámka: Do * tohto stavu sa bude spracovanie cyklicky vracať, kým nebude * nájdený potrebný počet prázdnych riadkov.) */ case SANKCIA_OTÁZKY: stavSpracovania = StavSpracovania.ZNENIE_ODPOVEDE; aktuálnaOtázka.aktualizujSankciu(čísloNaRiadku()); if (podrobnosti) { farbaTextu(hnedá); vypíšRiadok("Sancia otázky: ", aktuálnaOtázka.sankcia()); } break; /*# * V stave spracovania „znenie odpovede“ je riadok poslaný priamo * do textu novej odpovede patriacej aktuálne spracúvanej otázke. * * Stav spracovania sa zmení na „body odpovede.“ */ case ZNENIE_ODPOVEDE: stavSpracovania = StavSpracovania.BODY_ODPOVEDE; aktuálnaOtázka.pridajOdpoveď(riadok); if (podrobnosti) { farbaTextu(hnedá); vypíšRiadok("Nová odpoveď: ", aktuálnaOtázka.poslednáOdpoveď()); } break; /*# * V stave spracovania „body odpovede“ je riadok prevedený * na číselnú hodnotu, ktorá je poslaná do otázky metódou * aktualizujBodyPoslednejOdpovede. (Tá aktualizuje bodové * ohodnotenie poslednej definovanej odpovede.) * * Stav spracovania sa zmení na „znenie odpovede,“ čím sa * uzatvára cyklus zadávania znenia odpovedí a ich bodového * ohodnotenia. Tieto stavy sa opakujú, kým nie je * v konfiguračnom súbore nájdený potrebný počet prázdnych * riadkov. (Pozri aj spracovanie stavu SANKCIA_OTÁZKY vyššie.) */ case BODY_ODPOVEDE: stavSpracovania = StavSpracovania.ZNENIE_ODPOVEDE; aktuálnaOtázka.aktualizujBodyPoslednejOdpovede(čísloNaRiadku()); if (podrobnosti) { farbaTextu(hnedá); vypíšRiadok("Počet bodov odpovede: ", aktuálnaOtázka.poslednáOdpoveď().početBodov()); } break; } } catch (NumberFormatException e) { // Ak vznikne chyba pri spracovaní číselného údaju, bude to // oznámené používateľovi (červenou farbou, priebežne počas // spracovania a bez ohľadu na hodnotu atribútu „podrobnosti“). farbaTextu(červená); vypíšRiadok("Nesprávny číselný formát na riadku ", čísloRiadka, "."); } catch (Exception e) { // Všetky „iné chyby“ budú vypísané vo všeobecnom tvare: // „Úroveň #: «trieda chyby»: «text chyby» na riadku #.“ // (Pričom toto je úroveň 3. Tiež bez ohľadu na hodnotu // atribútu „podrobnosti.“) farbaTextu(červená); vypíšRiadok("Úroveň 3: ", e.getClass().getName(), ": ", e.getMessage(), " na riadku ", čísloRiadka, "."); } } /*# Nasledujú definície súkromných pomocných metód ladiacich výpisov. #*/ /** * Toto je pomocná súkromná metóda pre metódu výpisKvízu. Má za úlohu * vypísať nenulovú bodovú hodnotu s prípadným prefixom podľa znamienka * a s možnosťou uzavretia výpisu do zátvoriek. * * @param hodnota bodová hodnota na vypísanie * @param kladnýPrefix prefix, ktorý má byť vypísaný pred kladnou * hodnotou; hodnota null znamená „žiadny prefix“ * @param zápornýPrefix prefix, ktorý má byť vypísaný pred zápornou * hodnotou (tým nie je myslený výpis záporného * znamienka, ktoré je vypísané v každom prípade); * hodnota null znamená „žiadny prefix“ * @param zátvorky ak je true, tak bude výpis uzavretý do zátvoriek */ private void vypíšBodovúHodnotu(int hodnota, String kladnýPrefix, String zápornýPrefix, boolean zátvorky) { if (hodnota < 0) { if (zátvorky) { farbaTextu(šedá); vypíš(" ("); } if (null != zápornýPrefix) { farbaTextu(červená); vypíš(zápornýPrefix, ": "); } farbaTextu(svetločervená); vypíš(hodnota); if (zátvorky) { farbaTextu(šedá); vypíš(")"); } } else if (hodnota > 0) { if (zátvorky) { farbaTextu(šedá); vypíš(" ("); } if (null != kladnýPrefix) { farbaTextu(zelená); vypíš(kladnýPrefix, ": "); } farbaTextu(svetlozelená); vypíš(hodnota); if (zátvorky) { farbaTextu(šedá); vypíš(")"); } } } /** * Táto verzia súkromnej metódy nevyžaduje zadanie argumentu zátvorky – * vypíše ich automaticky. * * @param hodnota bodová hodnota na vypísanie * @param kladnýPrefix prefix, ktorý má byť vypísaný pred kladnou * hodnotou; hodnota null znamená „žiadny prefix“ * @param zápornýPrefix prefix, ktorý má byť vypísaný pred zápornou * hodnotou (tým nie je myslený výpis záporného * znamienka, ktoré je vypísané v každom prípade); * hodnota null znamená „žiadny prefix“ */ private void vypíšBodovúHodnotu(int hodnota, String kladnýPrefix, String zápornýPrefix) { vypíšBodovúHodnotu(hodnota, kladnýPrefix, zápornýPrefix, true); } /** * Táto verzia súkromnej metódy nevyžaduje zadanie argumentov kladného * a záporného prefixu – nevypíše žiadne. * * @param hodnota bodová hodnota na vypísanie * @param zátvorky ak je true, tak bude výpis uzavretý do zátvoriek */ private void vypíšBodovúHodnotu(int hodnota, boolean zátvorky) { vypíšBodovúHodnotu(hodnota, null, null, zátvorky); } /** * Táto verzia súkromnej metódy nevyžaduje zadanie argumentov kladného * a záporného prefixu, ani zátvoriek – prefixy nevypíše, zátvorky áno. * * @param hodnota bodová hodnota na vypísanie */ private void vypíšBodovúHodnotu(int hodnota) { vypíšBodovúHodnotu(hodnota, null, null, true); } /** * Táto metóda vypíše úplné znenie všetkých otázok a odpovedí kvízu tak * ako boli definované. Výpis slúži na kontrolu pre zadávateľa, či je * jeho definícia kvízu správna. */ public void výpisKvízu() { int najhlbšíPrepadVKvíze = 0; int bodyZaKvíz = 0; for (int ot = 0; ot < otázky.dĺžka(); ++ot) { Otázka otázka = otázky.daj(ot); farbaTextu(purpurová); vypíšRiadok(); if (ot < 9) vypíš(" "); vypíš(1 + ot, ". "); strop.zmrazOdsadenie(true); farbaTextu(modrá); vypíš(otázka); int sankcia = otázka.sankcia(); if (0 != sankcia) vypíšBodovúHodnotu(sankcia, "odmena", "sankcia"); vypíšRiadok(); for (int od = 0; od < otázka.početOdpovedí(); ++od) { Odpoveď odpoveď = otázka.odpoveďČíslo(od); farbaTextu(hnedá); vypíš(" ", (char)('a' + od), ". "); strop.zmrazOdsadenie(true); int body = odpoveď.početBodov(); if (body >= -1 && body <= 1) { if (body < 0) farbaTextu(červená); else if (body > 0) farbaTextu(zelená); else farbaTextu(šedá); vypíšRiadok(odpoveď); } else { if (body < 0) farbaTextu(červená); else farbaTextu(zelená); vypíš(odpoveď); vypíšBodovúHodnotu(body); vypíšRiadok(); } } int minimum = otázka.vypočítajMinimum(); int maximum = otázka.vypočítajMaximum(); if (0 != minimum && 0 != maximum) { if (-1 != minimum || 1 != maximum) { farbaTextu(šedá); vypíš("Minimum: "); vypíšBodovúHodnotu(minimum, false); farbaTextu(šedá); vypíš("; maximum: "); vypíšBodovúHodnotu(maximum, false); vypíšRiadok(); } if (0 > maximum) { farbaTextu(červená); vypíš("Za túto otázku nie je možné získať body!"); vypíšRiadok(); } } else if (0 != maximum) { if (1 != maximum) { farbaTextu(šedá); vypíš("Maximum: "); vypíšBodovúHodnotu(maximum, false); vypíšRiadok(); } } else if (0 != minimum) { farbaTextu(šedá); vypíš("Minimum: "); vypíšBodovúHodnotu(minimum, false); vypíšRiadok(); if (0 > minimum) { farbaTextu(červená); vypíš("Za túto otázku nie je možné získať body!"); vypíšRiadok(); } } else { farbaTextu(červená); vypíš("Za túto otázku nie je možné získať ani stratiť body!"); vypíšRiadok(); } najhlbšíPrepadVKvíze += minimum; bodyZaKvíz += maximum; } vypíšRiadok(); if (0 == bodyZaKvíz) { farbaTextu(červená); vypíš("V kvíze nie je možné dosiahnuť kladný počet bodov!"); vypíšBodovúHodnotu(bodyZaKvíz, false); } else { farbaTextu(modrá); vypíš("Maximálny počet bodov za kvíz: "); vypíšBodovúHodnotu(bodyZaKvíz, false); } vypíšRiadok(); if (najhlbšíPrepadVKvíze < 0) { farbaTextu(šedá); vypíš("("); farbaTextu(modrá); vypíš("Najhlbší prepad v kvíze: "); vypíšBodovúHodnotu(najhlbšíPrepadVKvíze, false); farbaTextu(šedá); vypíšRiadok(")"); } else if (najhlbšíPrepadVKvíze > 0) { // Toto asi nikdy nenastane, ale pre istotu je tu takéto // upozornenie implementované… farbaTextu(červená); vypíš("Pozor!"); farbaTextu(modrá); vypíš(" V kvíze nie je možné získať menej než: "); vypíšBodovúHodnotu(najhlbšíPrepadVKvíze, false); farbaTextu(modrá); switch (najhlbšíPrepadVKvíze) { case 1: vypíš(" bod"); break; case 2: case 3: case 4: vypíš(" body"); break; default: vypíš(" bodov"); } vypíšRiadok(); } } /** * Táto metóda zvolí všetky otázky kvízu v tom poradí, v ktorom boli * definované a spustí nový kvíz. V tejto fáze smú byť otázky (a ich * možnosti) ešte premiešané. * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public void vyberVšetkyOtázky() throws ChybaKvízu { if (0 == otázky.dĺžka()) throw new ChybaKvízu( ChybaKvízu.NEDOSTATOK_OTÁZOK); výber.vymaž(); for (Otázka otázka : otázky) výber.pridaj(new VýberOtázky(otázka)); koniec = false; otázkaVýberu = 0; početOpakovaní = 0; } /** * Táto metóda vygeneruje nový výber otázok kvízu v tom poradí ako boli * definované, s počtom zadaným v konfiguračnom súbore kvízu a spustí * nový kvíz. V tejto fáze smú byť otázky (a ich možnosti) ešte * premiešané. * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public void generujVýberOtázok() throws ChybaKvízu { // Metóda generovania výberu otázok je jednoduchá: // – najskôr sa vyberú všetky definované otázky, // – potom sa zistí, koľko otázok je potrebné vyradiť // a zabezpečí sa ich vyradenie. vyberVšetkyOtázky(); int trebaVyradiť = početOtázok <= 0 ? 0 : (otázky.dĺžka() - početOtázok); for (int i = 0; i < trebaVyradiť; ++i) výber.vymaž((int)náhodnéCeléČíslo(0, výber.dĺžka() - 1)); } /** * Táto metóda dodatočne premieša poradie zvolených otázok. Nesmie byť * spustená počas riešenia kvízu. Ide o doplnok metód vyberVšetkyOtázky * a generujVýberOtázok. * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public void premiešajOtázky() throws ChybaKvízu { if (0 == výber.dĺžka()) throw new ChybaKvízu( ChybaKvízu.NEDOSTATOK_OTÁZOK); for (VýberOtázky otázka : výber) if (otázka.bolaNavštívená()) throw new ChybaKvízu(ChybaKvízu.MIEŠANIE_ZAMIETNUTÉ); int počet = výber.dĺžka() - 1; for (int i = 0; i <= počet; ++i) výber.vymeň((int)náhodnéCeléČíslo(0, počet), (int)náhodnéCeléČíslo(0, počet)); } /** * Táto metóda je nepovinným doplnkom k metódam vyberVšetkyOtázky * a generujVýberOtázok a slúži na premiešanie poradia odpovedí všetkých * otázok. Nesmie byť spustená počas riešenia kvízu. * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public void premiešajOdpovedeOtázok() throws ChybaKvízu { if (0 == výber.dĺžka()) throw new ChybaKvízu( ChybaKvízu.NEDOSTATOK_OTÁZOK); for (VýberOtázky otázka : výber) if (otázka.bolaNavštívená()) throw new ChybaKvízu(ChybaKvízu.MIEŠANIE_ZAMIETNUTÉ); int počet = výber.dĺžka(); for (int i = 0; i < počet; ++i) výber.daj(i).premiešajOdpovede(); } /** * Táto metóda vráti objekt typu VýberOtázky, s ktorým je možné ďalej * pracovať. (Pozri opis tried VýberOtázky a Otázka a opisy ich metód.) * Základom je zobrazenie textu otázky a jej možností v určenom poradí. * * Ak táto metóda vráti hodnotu null, znamená to, že kvíz sa skončil * a môže byť vykonané hodnotenie riešiteľa, prípadne mu môže byť * ponúknutá možnosť opätovného prejdenia otázok kvízu (na kontrolu), * ak to dovoľuje odporúčaná navigácia. * * Množinu odporúčaných ovládacích prvkov vracia metóda * odporúčanáNavigácia. * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @return inštancia triedy VýberOtázky alebo null; vrátená inštancia * je aktuálnou otázkou kvízu * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public VýberOtázky aktuálnaOtázka() { System.out.println("A"); if (koniec) return null; System.out.println("B: " + otázkaVýberu); if (otázkaVýberu < 0 || otázkaVýberu >= výber.dĺžka()) return null; System.out.println("C"); VýberOtázky aktuálna = výber.get(otázkaVýberu); if (null != aktuálna) aktuálna.navštívená(); return aktuálna; } /** * Táto metóda posunie kvíz na nasledujúcu otázku. Metóda aktuálnaOtázka * slúži na získanie objektu VýberOtázky na ďalšie spracovanie. (Pozri aj * metódu ďalšiaOtázka.) * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public void choďVpred() throws ChybaKvízu { if (otázkaVýberu >= výber.dĺžka()) throw new ChybaKvízu(ChybaKvízu.NIE_JE_ĎALŠIA_OTÁZKA); ++otázkaVýberu; } /** * Táto metóda posunie kvíz na predchádzajúcu otázku. Metóda * aktuálnaOtázka slúži na získanie objektu VýberOtázky na ďalšie * spracovanie. (Pozri aj metódu predchádzajúcaOtázka.) * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public void choďVzad() throws ChybaKvízu { if (otázkaVýberu < 0) throw new ChybaKvízu(ChybaKvízu.NIE_JE_PREDCHÁDZAJÚCA_OTÁZKA); --otázkaVýberu; } /** * Táto metóda posunie kvíz na nasledujúcu otázku a vráti jej inštanciu, * ak je to možné. Volá pri tom metódu aktuálnaOtázka. * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @return inštancia triedy VýberOtázky alebo null; vrátená inštancia * je aktuálnou otázkou kvízu; pozri aj metódu aktuálnaOtázka * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public VýberOtázky ďalšiaOtázka() throws ChybaKvízu { choďVpred(); return aktuálnaOtázka(); } /** * Táto metóda posunie kvíz na predchádzajúcu otázku a vráti jej * inštanciu, ak je to možné. Volá pri tom metódu aktuálnaOtázka. * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @return inštancia triedy VýberOtázky alebo null; vrátená inštancia * je aktuálnou otázkou kvízu; pozri aj metódu aktuálnaOtázka * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public VýberOtázky predchádzajúcaOtázka() throws ChybaKvízu { choďVzad(); return aktuálnaOtázka(); } /** * Táto metóda posunie kvíz na začiatok – zopakuje cyklus riešenia. * Spustenie tejto metódy by malo byť reakciou na voľbu ovládacieho * prvku (napr. tlačidla) určeného na opakovanie riešenia kvízu. * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @return inštancia triedy VýberOtázky alebo null; vrátená inštancia * je aktuálnou otázkou kvízu; pozri aj metódu aktuálnaOtázka * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public VýberOtázky opakujKvíz() { otázkaVýberu = 0; ++početOpakovaní; return aktuálnaOtázka(); } /** * Táto metóda slúži na získanie množiny odporúčaných navigačných prvkov, * ktoré by mala aplikácia zobraziť riešiteľovi v súvislosti s aktuálnym * stavom kvízu. * * Napríklad v prípade, že definícia kvízu poskytuje možnosť ľubovoľnej * navigácie medzi otázkami počas kvízu to budú nasledujúce ovládacie * prvky (predpokladajme, že používame tlačidlá) v nasledujúcich * situáciách: * * — tlačidlo „ďalej“ pri prvej otázke kvízu, * — tlačidlo „späť“ pri poslednej otázke kvízu, * — obidve spomenuté tlačidlá pre všetky ostatné otázky kvízu * — a tlačidlo „koniec kvízu“ po prejdení všetkých otázok kvízu. * * Množina prvkov je vrátená vo forme reťazca, v ktorom každý má znak * význam jedného navigačného prvku (napr. tlačidla). * * <!-- Poznámka: Znakové entity < a > označujú znaky znamienok * menší než a väčší než. Sú tu použité preto, lebo v rámci dokumentácie * by tieto znaky nemali byť používané, pretože slúžia na vloženie HTML * značiek. --> * * Môže ísť o nasledujúce znaky: * * < – ovládací prvok (napr. tlačidlo) na posun späť, * > – ovládací prvok (napr. tlačidlo) na posun ďalej, * O – ovládací prvok (napr. tlačidlo) na spustenie opätovného * prejdenia otázok (rekapitulácie kvízu pred ukončením), * 0 – ovládací prvok (napr. tlačidlo) na ukončenie kvízu * a zobrazenie vyhodnotenia, * E – ovládací prvok alebo prvky (napr. vstupný riadok, sústava * tlačidiel a pod.) slúžiace na voľbu odpovede, * * * – ovládací prvok na spustenie nového kvízu. * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @return reťazec pozostávajúci z povolených znakov, z ktorých každý * označuje jeden odporúčaný ovládací prvok, ktorý by mal byť * zobrazený riešiteľovi (pozri opis vyššie) * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public String odporúčanáNavigácia() { if (koniec) return "*"; String zoznam = ""; if ('L' == konfiguračnéPísmeno && otázkaVýberu > 0) zoznam += "<"; if (otázkaVýberu < výber.dĺžka()) zoznam += ">"; else if ('L' == konfiguračnéPísmeno || ('O' == konfiguračnéPísmeno && 0 == početOpakovaní)) zoznam += "O"; boolean môžeSkončiť = true; for (VýberOtázky otázka : výber) if (!otázka.bolaNavštívená()) { môžeSkončiť = false; break; } if (môžeSkončiť && (0 != početOpakovaní || otázkaVýberu >= výber.dĺžka())) zoznam += "0"; if (otázkaVýberu >= 0 && otázkaVýberu < výber.dĺžka()) zoznam += "E"; return zoznam; } /** * Táto metóda pošle do jadra kvízu číslo odpovede (možnosti), ktorú * zvolil riešiteľ. * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @param možnosť číslo odpovede (možnosti), ktorú zvolil riešiteľ * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public void voľbaOdpovede(int možnosť) throws ChybaKvízu { if (koniec) throw new ChybaKvízu(ChybaKvízu.JE_KONIEC); VýberOtázky aktuálna = aktuálnaOtázka(); if (null == aktuálna) throw new ChybaKvízu(ChybaKvízu.VOĽBA_NIE_JE_MOŽNÁ); aktuálna.voľba(možnosť); } /** * Táto metóda ukončí riešenie kvízu. Spustenie tejto metódy by malo byť * reakciou na voľbu ovládacieho prvku (napr. tlačidla) určeného na * ukončenie riešenia kvízu. * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @return null, pretože po ukončení kvízu vracia metóda aktuálnaOtázka * hodnotu null (pozri aj metódu aktuálnaOtázka) * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public VýberOtázky koniec() { koniec = true; return aktuálnaOtázka(); } /** * Táto metóda overí, či sa riešenie kvízu už skončilo. * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @return true ak sa kvíz už skončil * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public boolean jeKoniec() { return koniec; } /** * Táto metóda zráta a vráti počet bodov, ktoré získal riešiteľ * na základe svoji odpovedí. * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @return počet bodov získaných riešiteľom * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public int početBodov() { int body = 0; for (VýberOtázky otázka : výber) body += otázka.body(); return body; } /** * Táto metóda vypočíta a vráti najnižší počet bodov, ktoré bolo možné * získať v tomto kvíze (aby bolo možné posúdiť úspešnosť riešiteľa). * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @return najnižší počet bodov tohto kvízu * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public int minimumBodov() { int body = 0; for (VýberOtázky otázka : výber) body += otázka.minimum(); return body; } /** * Táto metóda vypočíta a vráti najvyšší počet bodov, ktoré bolo možné * získať v tomto kvíze, aby bolo možné posúdiť úspešnosť riešiteľa. * * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním * v iných projektoch.) * * @return najvyšší počet bodov tohto kvízu * * @see #vyberVšetkyOtázky() * @see #generujVýberOtázok() * @see #premiešajOtázky() * @see #premiešajOdpovedeOtázok() * * @see #aktuálnaOtázka() * @see #choďVpred() * @see #choďVzad() * @see #ďalšiaOtázka() * @see #predchádzajúcaOtázka() * @see #opakujKvíz() * @see #odporúčanáNavigácia() * * @see #voľbaOdpovede(int) * @see #koniec() * @see #jeKoniec() * * @see #početBodov() * @see #minimumBodov() * @see #maximumBodov() */ public int maximumBodov() { int body = 0; for (VýberOtázky otázka : výber) body += otázka.maximum(); return body; } }
Prosím, zvoľte triedu balíčka kvíz
.
Poznámka: Ak by ste sa rozhodli ukladať triedy balíčka ručne, nezabudnite, že musia byť uložené v (pod)priečinku s názvom kvíz
.
2 otázky Ľpriamočiaro – platí iba prvý znak, čiže v skutočnosti je nastavenie „ľubovoľne“ (zvyšok riadka je ignorovaný) Otázka s dvomi možnosťami a odmenou 1. 2 áno 1 nie -2 Otázka s tromi možnosťami a sankciou 1. -2 jeden 1 dva 2 nič 0 Otázka s dvomi možnosťami a odmenou 2. 2 áno 1 nie -2 Otázka s tromi možnosťami a sankciou 2. -2 jeden 1 dva 2 nič 0 Otázka s dvomi možnosťami a odmenou 3. 2 áno 1 nie -2 Otázka s tromi možnosťami a sankciou 3. -2 jeden 1 dva 2 nič 0 Otázka s dvomi možnosťami a odmenou 4. 2 áno 1 nie -2 Otázka s tromi možnosťami a sankciou 4. -2 jeden 1 dva 2 nič 0 Otázka s dvomi možnosťami a odmenou 5. 2 áno 1 nie -2 Otázka s tromi možnosťami a sankciou 5. -2 jeden 1 dva 2 nič 0 Otázka s dvomi možnosťami a odmenou 6. 2 áno 1 nie -2 Otázka s tromi možnosťami a sankciou 6. -2 jeden 1 dva 2 nič 0