Włodzimierz WYSOCKI, Walery SUSŁOW Katedra Inżynierii Komputerowej, Politechnika Koszalińska E mail: wlodek.wysocki@gmail.com, walery.suslow@tu.koszalin.pl Moduł generatora dokumentów tekstowych oparty o otwartą bibliotekę ODFDOM Streszczenie: W artykule przedstawiono koncepcję typowego modułu generatora dokumentów tekstowych do zastosowań biznesowych. Autorzy wykorzystują otwarty format dokumentów biurowych OpenDocument oraz opierają konstrukcję modułu na wykorzystaniu biblioteki języka Java ODFDOM. Poza ogólnym opisem idei działania modułu generatora, w artykule przedstawiono architekturę zbudowanej realizacji oraz wskazano możliwe kierunki rozwoju zbudowanego modułu. Artykuł zawiera również link do repozytorium z kodem modułu. 1. Wstęp Nowoczesne systemy informatyczne najczęściej tworzą raporty i inne dokumenty tekstowe jako pliki PDF, potem wysyłają je do przeglądarki użytkownika, gdzie można je obejrzeć, wydrukować lub zapisać na dysk. Żeby zmienić wygląd lub zawartość tak powstałych dokumentów trzeba mieć dostęp do komercyjnych narzędzi do edycji plików PDF. Podobne ograniczenia występują także, gdy dokumenty i raporty są tworzone w formacie DOC. Jest to również format zamknięty, komercyjny tak jak aplikacje służące do edycji dokumentów w tym formacie. Open Document Format for Office Applications jest to format otwarty, którego specyfikacja jest publicznie dostępna. Został utworzony jako alternatywa dla komercyjnych, zamkniętych formatów dokumentów używanych przez Microsoft Office. Swoim zakresem obejmuje dokumenty tekstowe, arkusze kalkulacyjne, wykresy i prezentacje. Rozwojem standardu zajmuje się niezależna organizacja OASIS, do której należą największe firmy informatyczne jak: IBM, Microsoft, Oracle oraz organizacje rządowe USA. Rozwiązaniem, które umożliwi użytkownikowi systemu informatycznego zmianę wyglądu i treści tworzonych raportów bez dostępu do komercyjnych edytorów, jest wygenerowanie raportu w postaci dokumentu ODT, który można edytować, na przykład popularnym, darmowym edytorem tekstu Writer z pakietu OpenOffice. Takie rozwiązanie wymuszałoby jednak na użytkowniku, żeby za każdym razem po wygenerowaniu raportu od nowa korygował jego wygląd. Większą elastyczność zapewni umożliwienie użytkownikowi edycji szablonu, według którego tworzony jest raport czy inny doku- 86
ment. Szablon taki powinien zawierać nazwy pól, które będą następnie wypełniane treścią wygenerowaną lub odczytaną przez aplikację z bazy danych. Użytkownik za pomocą znanych mu narzędzi edytora Writer do formatowania tekstu będzie mógł dostosować wygląd i treść raportu do swoich indywidualnych potrzeb. Co ważne, zmiany te wprowadzone raz do szablonu będą odzwierciedlone w każdym dokumencie generowanym według tego szablonu przez system. W ramach niniejszego artykułu prezentowana jest autorska koncepcja budowy typowego modułu generatora dokumentów tekstowych, wykorzystująca opisany pomysł edytowalnego szablonu i oparta o funkcjonalność biblioteki ODFDOM [1]. Istnieje szereg innych bibliotek przydatnych do zbudowania podobnego modułu, chociażby JODReports [2]. Wybór na korzyść ODFDOM został dokonany z powodu solidności wsparcia dla projektu tej biblioteki. Artykuł przybliża konstrukcję modułu generatora dokumentów opracowaną w wersji prototypowej w trakcie przygotowania magisterskiej pracy dyplomowej [3], gdzie moduł ten został użyty po raz pierwszy do generowania raportów z przebiegu prac testowych na podstawie danych zapisanych w bazie systemu bug traker'a. Wcześniejsze próby utworzenia modułu generatora, przeprowadzone przy pomocy OpenOffice SDK (dostarcza on technologii Unified Network Objects [4] pozwalającej na dostęp do komponentów OpenOffice'a niezależnie od platformy sprzętowej, systemu operacyjnego czy języka programowania) okazały się nieudane. Dopiero użycie biblioteki ODFDOM przyspieszyło prace i umożliwiło realizację założonych celów. 2. Krótki opis biblioteki ODFDOM Biblioteka ODFDOM jest częścią większego projektu o nazwie ODF Toolkit [5]. Jest to projekt utrzymywany przez społeczność Open Source, która powstała w celu rozwoju i utrzymania narzędzi ułatwiających pracę z dokumentami w formacie ODF. ODFDOM jest najaktywniejszą częścią tego projektu. Interfejs programistyczny biblioteki jest tak zaprojektowany, żeby można było łatwo tworzyć, zmieniać, odczytywać i zapisywać dokumenty ODF przy pomocy kilku linijek kodu. Użycie biblioteki nie wymaga od programisty szczegółowej znajomości specyfikacji formatu ODF. Biblioteka powstała by zrealizować dwa główne cele. Pierwszym z nich jest zapewnienie łatwiejszego i wygodniejszego dostępu do dokumentów ODF niż przez aplikacje takie jak OpenOffice czy IBM Lotus Symphony. Drugim jest umożliwienie programistom realizacji typowych w przemyśle informatycznym scenariuszy przypadków użycia, do których odnoszą się: automatyczne generowanie wielkiej liczby dokumentów tekstowych w środowisku korporacyjnym zgodnie ze specyficznymi wymaganiami biznesowymi na podstawie szablonów i danych z baz danych; np. wygenerowanie odcinków płac dla wszystkich pracowników organizacji w postaci dokumentów ODF. walidacja dokumentów sprawdzenie, czy i z którą wersją specyfikacji ODF dokument jest zgodny; 87
przeszukiwanie treści dokumentów w celu znalezienia fragmentów wg zadanych kryteriów. Biblioteka ODFDOM (patrz rysunek 1) ma strukturę złożoną z dwóch warstw, są to: warstwa paczki ODF i warstwa XML. Warstwa paczki ODF zapewnia dostęp do zasobów (plików), strumieni XML, grafiki, wbudowanych obiektów umieszczonych w paczce ZIP, którą jest plik ODF. Warstwa ta zapewnia wszystkie opisane w trzeciej części specyfikacji ODF 1.2 [6] funkcje, opierające się na obsłudze plików ZIP, obsłudze szyfrowania W3C, obsłudze podpisu cyfrowego W3C i obsłudze metadanych W3C. Warstwa XML gwarantuje dostęp do wszystkich elementów, które mogą być umieszczone w pliku ODF (tabele, grafika, numeracja). Dostępne elementy zdefiniowane są w pierwszej części specyfikacji ODF opisującej schemat XML ODF. Warstwa XML składa się z dwóch interfejsów programistycznych reprezentujących dwa różne podejścia do zapewnienia funkcjonalności. Pierwsze z nich dostarcza niskopoziomowy API drzewa DOM, co umożliwia dostęp do elementów XML. Interfejs programistyczny umożliwia manipulowanie węzłami drzewa DOM w sposób zgodny ze standardem DOM API W3C [7] znanym z przeglądarek internetowych. Klasy Javy składające się na API są wygenerowane automatycznie na podstawie gramatyki ODF zdefiniowanej w języku RelaxNG, co zapewnia zgodność ze specyfikacją i łatwość dostosowania do przyszłych wersji standardu. Drugie podejście realizuje wysokopoziomowy API dokumentu ten interfejs programistyczny jest zorientowany na użyteczność, ukrycie przed użytkownikiem szczegółów implementacji i realizację najczęściej wykorzystywanych przypadków użycia, na przykład zmianę zawartości pewnej komórki arkusza kalkulacyjnego. Rys. 1. Model warstwowy interfejsów programistycznych biblioteki ODFDOM W niskopoziomowym ODF DOM API każda z klas reprezentuje węzeł XML ODF, w wysokopoziomowym API jedna klasa odpowiada wielu elementom XML ODF i ich atrybutom. Szczegóły dotyczące ODFDOM można poznać z publikacji [8-13]. 88
3. Idea typowego modułu generatora dokumentów tekstowych Ideę generatora przedstawia schemat kontekstowy podany na rys. 2. Generator dokumentów czyta szablon dokumentu i następnie za pomocą mechanizmów refleksji języka Java wyszukuje nazwy pól zdefiniowanych w szablonie wśród obiektowych reprezentacji danych odczytanych z bazy danych. Iterując po kolekcjach rekordów bazy Db i pobierając właściwe dane, generator buduje tabele umieszczone w dokumencie szablonu, a następnie zapisuje utworzony na podstawie szablonu dokument w formacie ODF i przesyła go do przeglądarki użytkownika. Użytkownik może sporadycznie redagować dokument wynikowy lub wprowadzić zmiany do szablonu dokumentu, co spowoduje stałą zmianę budowy generowanych dokumentów wynikowych. W obu przypadkach może on korzystać z oprogramowania biurowego, czyli nie musi mieć nawyków programisty. W opisywanej realizacji projektu komunikacja generatora z bazą danych została wykonana w oparciu o komponenty (ziarna) EJB 3.0 [14] i standard mapowania obiektowo-relacyjnego firmy Sun Microsystems Java Persistence API (skrót JPA [15]) dla języka Java. Podstawowym wymaganiem dla generatora dokumentów jest wypełnienie zawartości pól użytkownika umieszczonych w szablonie dokumentu. Równie niezbędnym wymaganiem jest możliwość odwzorowania danych pochodzących z bazy na treść tabelaryczną dokumentu wynikowego. By moduł był użyteczny, algorytm generowania tabeli powinien obsługiwać następujące przypadki tego odwzorowania: Tworzenie tabeli prostych wiersz tabeli odpowiada rekordowi z bazy danych. Tworzenie tabeli zagnieżdżonych w komórce tabeli dane są umieszczane w postaci tabeli. Tworzenie tabeli pionowych kilka wierszy tabeli tworzonych jest z jednego rekordu. Autonumeracja wierszy tabeli. Rys. 2. Schemat kontekstowy działania generatora dokumentów 89
4. Proponowana architektura modułu generatora dokumentów Budowa modułu jest zoptymalizowana względem procesu generowania dokumentu, który składa się z dwóch faz. W pierwszej fazie wyszukiwane są deklaracje pól użytkownika w dokumencie-szablonie. W drugiej fazie wyszukiwane są odwołania do pól użytkownika. Nazwy umieszczone w odwołaniach są następnie wyszukiwane w komponentach trwałych dzięki mechanizmom refleksji języka Java i przekształcane na reprezentacje obiektowe. Finalizacja drugiej fazy to iterowanie po kolekcjach i budowanie elementów dokumentu z zawartością rekordów wczytanych z bazy danych. Rys. 3. Diagram klas generatora dokumentów 90
W przypadku, kiedy generator dokumentów nie odnajdzie w drugiej fazie komponentu trwałego, do którego odnoszą się odwołania, w logach serwera aplikacji zostaje zarejestrowany błąd, a w zawartości odpowiedniego pola użytkownika pojawia się stosowny komunikat błędu. Jeżeli typ danych odnalezionego komponentu trwałego jest inny niż znakowy (np. walutowy lub numeryczny), to dane są automatycznie skonwertowane na tekst. Klasa główna generatora dokumentów DocumentGenerator (patrz diagram na rysunku 3) zawiera najważniejszą metodę generate(), której wywołanie z parametrami: odwołaniem do komponentu trwałego, nazwą szablonu i nazwą pliku wynikowego spowoduje wypełnienie szablonu danymi z obiektu trwałego i zapisanie go jako dokumentu wynikowego w katalogu tymczasowym oraz wysłanie dokumentu wynikowego do przeglądarki użytkownika. Klasy bazowe, które odpowiadają za obiektową reprezentację szablonu dokumentu, są zdefiniowane w sposób następujący: FieldDecl opisuje deklarację pola użytkownika umieszczoną w szablonie dokumentu, FieldRef opisuje odwołanie do pola użytkownika występujące w szablonie dokumentu. Obiekty klasy FieldDecl są tworzone przez generator dokumentów tylko dla pól bezpośrednio występujących w przekazanym do generatora komponencie trwałym. Obiekty te są zapamiętywane w mapie obiektów i następnie są używane do zapisania wartości obiektów, do których się odwołują w deklaracjach pól w dokumencie wynikowym. Obiekty klasy FieldRef są tworzone w czasie wyszukiwania w przekazanych do generatora komponentach trwałych nazwy odwołania do pola użytkownika. Klasa ta służy jako klasa bazowa dla klas RelationRef i CollRef, realizujących montowanie tabeli w dokumencie wynikowym. Do opisu tabeli umieszczonej w szablonie dokumentu służy klasa TableDefinition. Klasa ta zawiera kolekcję obiektów klasy FieldRef opisujących odwołania do pól komponentów trwałych, których wartości należy umieścić w tabeli. Warto tutaj podkreślić, że postać tabelaryczna jest bardzo powszechna w wydrukach i generator powinien być w stanie sprytnie obsługiwać nie tylko tabele proste, ale i tabele tabel, czyli konstrukcje zagnieżdżone. W przypadku tabeli prostych obsługę danych załatwia klasa RelationRef, która opisuje relację jeden do jednego lub wiele do jednego. Mianowicie w tej relacji znajdują się pola wskazywane przez nazwę wpisaną w odwołanie do pola użytkownika umieszczone w szablonie dokumentu. Bardziej skomplikowana obsługa danych niezbędna jest w przypadku tabeli tabel. Służy do tego klasa CollRef, która ma opisywać kolekcję, czyli relację jeden do wielu umieszczoną w komponencie trwałym przekazanym do generatora. Obiekt klasy CollRef zawiera informację o tabelach w szablonie dokumentu, wewnątrz których znajdują się pola tej kolekcji i dla których tabeli trzeba tworzyć nowe wiersze podczas iteracji po kolekcji. Informacja o tabelach przechowywana jest jako kolekcja obiektów klasy TableDefinition. Mapowanie na obiekty klasy CollRef dokumentów zawierających tabele zagnieżdżone powoduje zagłębienie kolekcji w kolekcji. Do obsługi iteracji kolekcji umieszczonych w kolekcjach służy klasa CollRefStack implementująca metody push() i pop(), które pozwalają na utworzenie stosu obiektów klasy CollRefComponent odwzorowującego aktual- 91
ne zagłębienie iterowanej kolekcji. Klasa CollRefComponent zawiera odwołanie do obiektu klasy CollRef i komponentu trwałego, wewnątrz którego kolekcja jest umieszczona. Tab. 1. Porównanie modułu generatora dokumentów i biblioteki JODReports Aspekt porównania Pola szablonu Edycja szablonu Budowa szablonu Iteracja kolekcji i wypełnianie zawartości tabel Złożoność generowanych dokumentów Łatwość użytkowania Moduł generatora dokumentów Standardowe pola użytkownika edytora Writer Wsparcie edytora dla zarządzania polami wstawianie z listy, itd. Brak instrukcji przetwarzania, odpowiedzialność przeniesiona na klasy Java, zmiana wymaga ponownej kompilacji kodu Automatyczne i uproszczone Proste dokumenty bezpośrednio odzwierciedlające zawartość obiektów Java Przygotowanie szablonu przez programistę, jest możliwość tworzenia i edycji szablonów przez zwykłych użytkowników Biblioteka JODReports Pola oznaczone przez znak dolara i nawiasy klamrowe ${nazwa_pola} składnia FreeMarker, jest opcjonalna możliwość użycia pól użytkownika edytora Writer Brak wsparcia, edytor nie odróżnia pól od zwykłego tekstu, złożona składnia szablonów, potrzeba znajomości wewnętrznej budowy dokumentów ODF Złożone instrukcje przetwarzania, możliwość modyfikacji szablonów bez kompilacji kodu Java Za pomocą instrukcji przetwarzania umieszczanych w specjalnych miejscach dokumentu Złożone dokumenty ze względu na możliwość umieszczenia wyrażeń arytmetycznych, wyrażeń logicznych i instrukcji warunkowych Przygotowanie i zmiana szablonów przez programistów, teoretycznie jest możliwość edycji szablonu przez użytkownika zaawansowanego Powyższa tabela przedstawia możliwość porównania zaproponowanej konstrukcji modułu z rozwiązaniem dostarczanym przez wspomnianą wyżej bibliotekę JODReport w kontekście warunków pracy użytkownika systemu informatycznego. 92
5. Podsumowanie Zaprezentowana konstrukcja modułu generatora przeszła testy akceptacyjne. Próbna eksploatacja potwierdziła poprawność podjętych decyzji projektowych. Kod modułu jest dostępny do pobrania ze strony http://code.google.com/p/gen-dok-odf. Przewidywalny rozwój opisanego modułu może być związany z refaktoryzacją istniejącego kodu poprzez zamianę ścisłych powiązań między klasami powstałymi przez użycie dziedziczenia na luźne powiązania i wprowadzenie mechanizmu rozszerzania interfejsów. Rozluźnienie powiązań między klasami i wypracowanie bardziej uniwersalnych kontraktów dla klas umożliwiłoby zniesienie zależności od klas biblioteki JBoss Seam oraz od technologii EJB3, w której muszą być obecnie wykonane komponenty trwałe dostarczane do generatora dokumentów. W oparciu o technologię EJB3 jest zbudowany mechanizm iterowania po kolekcjach i budowania tabel w dokumencie wynikowym. Drugim pożytecznym zabiegiem rozwojowym może być wprowadzenie dynamicznej podmiany nazwy pola oraz samego komponentu trwałego. W tej chwili trzeba używać dokładnie takich samych nazw, jakie są użyte w komponencie EJB, przez to wartości wprowadzane w pola użytkownika w szablonie stają się bardzo długie i nieczytelne. Rozwiązaniem tego problemu może być zastosowanie pliku konfiguracyjnego do definiowania aliasów nowych, krótszych nazw komponentów i ich pól. Literatura 1. ODFDOM - the OpenDocument API, http://odftoolkit.org/projects/odfdom/pages/home 2. Java OpenDocument Reports, http://jodreports.sourceforge.net/ 3. Wysocki W., Projekt aplikacji wspomagającej testowanie oprogramowania, zgodny z metodologią Unified Software Development Process, praca dyplomowa magisterska, Politechnika Koszalińska, Koszalin 2011. 4. UNO - interfejs komponentów pakietu OpenOffice, http://wiki.services.openoffice.org/wiki/uno 5. ODF Toolkit Union, http://odftoolkit.org/ 6. Open Document Format for Office Applications, Version 1.2, http://docs.oasisopen.org/office/v1.2/part1/cd04/opendocument-v1.2-part1-cd04.html 7. Document Object Model, http://www.w3.org/dom/ 8. ODFDOM Tutorial, http://opendocument.xml.org/news/odfdom-tutorial 9. Ming Fei Jia, ODFDOM for Java: Simplifying programmatic control of documents and their data, Part 1, http://www.ibm.com/developerworks/lotus/library/symphonyodfdom-pt1/index.html 10. Wei Hua Wang, ODFDOM for Java: Simplifying programmatic control of documents and their data, Part 2, http://www.ibm.com/developerworks/lotus/library/symphonyodfdom-pt2/index.html 93
11. Li Wei, ODFDOM for Java: Simplifying programmatic control of documents and their data, Part 3, http://www.ibm.com/developerworks/lotus/library/symphony- odfdom-pt3/?ca=drs- 12. Florian Hopf, Template based document generation using ODFDOM, http://blog.synyx.de/2010/06/template-based-document-generation-using-odfdom/ 13. OpenDocument Format and the ODFDOM API, http://langintro.com/odfdom_tutorials/odf_internals.html 14. EJB 3.0 Tutorial, http://docs.jboss.org/ejb3/app-server/tutorial/index.html 15. Introduction to the Java Persistence API, http://download.oracle.com/javaee/5/tutorial/doc/bnbpz.html 94