**Cvičení je zaměřené na práci s výjimkami a vnořenými kolekcemi.**
1. Vytvořte výjimky v balíčku `cz.muni.fi.pb112.project.exception`:
*`TransparentColorException` je _kontrolovaná_ (checked) výjimka při kreslení stejnou barvou na stejném pozadí, např. bílou tužkou na bílý papír.
*`EmptyDrawableException` je _kontrolovaná_ výjimka, pokud na kreslicím objektu není nic namalováno.
*`MissingVerticesException` představuje _**nekontrolovanou**_ (unchecked) výjimku v případě, že v kolekci není dostatek vrcholů.
Všechny výjimky budou mít alespoň dva konstruktory, které:
* umožňují nastavit **řetězec** s chybovou zprávou,
* umožňují nastavit **řetězec** a **příčinu** (cause) výjimky: výjimku, která byla bezprostřední příčinou této výjimky.
2. Upravte konstruktor `SimplePolygon` tak, aby vyhazoval `MissingVerticesException`, pokud pole obsahuje méně než tři vrcholy.
> Hlavičky metod, které vyhazují kontrolované výjimky, musí obsahovat `throws`.
3. Upravte třídu `Paper` následovně:
* Metoda `eraseAll()` volaná na čistém (prázdném) papíře vyhodí `EmptyDrawableException`.
* Metoda `drawPolygon` vyhodí `TransparentColorException` při kreslení bílou barvou.
Výjimka bude obsahovat textový popis s názvem barvy.
*`Paper` bude implementovat rozhraní `PolygonFactory`.
* Metoda `Polygon tryToCreatePolygon(List<Vertex2D>)` se pokusí vytvořit `CollectionPolygon` ze seznamu vrcholů.
* Pokud je vstupní argument `null`, metoda vyhodí `NullPointerException`.
* Metoda zkopíruje vstupní kolekci (nebude měnit původní kolekci).
* Pokud během vytváření polygonu dojde k chybě `IllegalArgumentException`, metoda výjimku pohltí, odstraní všechny `null` vrcholy z kolekce a zkusí to znovu. Výjimku `MissingVerticesException` metoda nechá projít dál.
> Poznámka: Požadované chování porušuje princip používání výjimek. Logičtější a jednodušší by bylo nejprve zkontrolovat a odstranit null vrcholy. To by také zabránilo zbytečnému vyhazování výjimky konstruktorem třídy CollectionPolygon. Požadované chování slouží pouze k procvičení práce s výjimkami.
* Metoda `void tryToDrawPolygons(List<List<Vertex2D>>)` přijímá seznam seznamů vrcholů
(tj. seznam polygonů uložených jako kolekce vrcholů).
* Metoda se pokusí z každé kolekce vytvořit polygon (`tryToCreatePolygon`) a následně jej vykreslit (`drawPolygon`).
* Pokud při kreslení polygonu dojde k výjimce `TransparentColorException`, ostatní polygony budou vykresleny černou barvou.
* Pokud během vytváření dojde k výjimce `MissingVerticesException` nebo `NullPointerException`, metoda výjimku zachytí a pokusí se vytvořit a vykreslit další polygon. Pokud **se nepodaří vykreslit žádný polygon**, vyhodí se `EmptyDrawableException` s příčinou **poslední** chyby.
* Přidejte metodu `Collection<Polygon> getPolygonsWithColor(Color color)`, která vrátí všechny polygony s barvou `color`. Můžete použít lambda streamy, konkrétně `filter`, `map`, `collect`.
3. Spuštěním třídy `Draw` se vykreslí černobílý domeček.

## Zadání iterace 10
**Cvičení je zaměřené na práci se vstupem a výstupem.**
Upravte třídu `LabeledPolygon.Builder` tak, aby implementovala rozhraní `PolygonReadable`.
Upravte třídu `LabeledPolygon` tak, aby implementovala rozhraní `PolygonWritable`.
1. Metoda `read(InputStream)` přijímá otevřený vstupní proud obsahující pojmenované vrcholy, načte vrcholy a přidá je ke stávajícím vrcholům polygonu.
Při jakékoli chybě vstupu/výstupu nebo chybě formátu vstupních dat musí metoda atomicky selhat a vyhodit `IOException`. (atomicky = buď se načte vše, nebo nic)
Formát vstupních dat je následující:
* Vstup je textový.
* Každý vrchol je na jednom řádku.
* Každý řádek je ve formátu _„x y název vrcholu“_, tj. nejprve souřadnice vrcholu oddělené mezerou
a poté název vrcholu (název může obsahovat mezery).
Viz například soubor `polygon-ok.txt`.
2. Metoda `write(OutputStream)` zapisuje vrcholy do zadaného výstupního proudu.
Výstupní formát je stejný jako u předchozí metody.
3. Metody `write(File)` a `read(File)` budou fungovat stejně jako dříve,
nicméně budou pracovat se souborem místo I/O proudu.
Vyhněte se duplicitě kódu!
4. Vytvořte metodu `writeJson(OutputStream os)`, která zapíše mapu ve formátu JSON do výstupního proudu.
* Použijte externí knihovnu [gson](https://github.com/google/gson).
Pro Maven je potřeba přidat závislost do souboru `pom.xml` v sekci `<dependencies>`.
* Přečtěte si dokumentaci třídy _Gson_.
* Podle [dokumentace použijte tzv. _pretty print_](https://github.com/google/gson/blob/master/UserGuide.md#compact-vs-pretty-printing-for-json-output-format).
* Objekt typu _Gson_ lze znovu použít, takže jej stačí vytvořit pouze jednou.
6. Upravte třídu `Demo` následovně:
* Metoda `main` bude vyhazovat `IOException`.
* Třída vytvoří `LabeledPolygon` ze souboru `polygon-ok.txt`.
* Polygon bude také obsahovat vrchol s názvem `vertex x` se souřadnicemi `[123, 456]`.
* Zapište výstup do výstupního proudu `System.out` ve formátu JSON.
* Pro ověření, že výstupní proud je stále otevřený, poté vytiskněte `Hello World!`.
5. Spuštěním třídy `Draw` se načte `polygon-ok.txt` a vykreslí se na obrazovku toto.

### Nápověda
- Zavřete pouze ty proudy/soubory, které jste sami otevřeli.
- Použijte _try with resources_.
- Prostudujte metody `Writer#flush()`, `Reader#ready()`.
- Vytvoření souboru: `new File("soubor.txt")`.
-`Demo.main` může vyhazovat `IOException`.
- Místo `\n` použijte univerzální oddělovač řádků `System.lineSeparator()`.