Podręcznik referencyjny: Aplikacje typu Lista

Spis treści

Niniejszy podręcznik referencyjny opisuje podstawy techniczne i programowanie konfigurowalnych aplikacji typu Lista. Po krótkim wprowadzeniu wymieniono obiekty deweloperskie i interfejsy programowania, a w niektórych przypadkach zilustrowano je krótkimi przykładami.
Działanie aplikacji typu Lista opisano w artykule Listy.

Grupa docelowa

  • Programiści aplikacji

Opis

Aplikacje typu Lista są aplikacjami zapytań, które można łatwo i wygodnie dostosowywać i rozszerzać. Technologia listy może być również wykorzystywana w innych konfigurowalnych aplikacjach, np. poprzez osadzenie komponentu interfejsu użytkownika SearchView jako edytora. Infrastruktura aplikacji typu Lista jest również wykorzystywana przez pozostałe usługi.
Ogólne informacje na temat list można znaleźć w niniejszych rozdziałach:

Zestaw kolumn

Kluczową cechą aplikacji typu Lista jest zestaw kolumn, z których użytkownicy mogą dowolnie tworzyć swoje widoki. Obejmuje to wykorzystanie kolumn na liście wyników, jako pól wyszukiwania w nagłówku oraz jako funkcji sortowania.
Zestaw kolumn jest dostarczany w kilku etapach. Punktem wyjścia jest wyrażenie OQL (klauzula FROM), w którym wybrane obiekty biznesowe są ze sobą powiązane. Atrybuty, które mają być zawarte w zestawie kolumn, są następnie wybierane z tych obiektów biznesowych. Oba są zapisywane razem w obiektach deweloperskich Search i Search extension, przy czym dostarczany jest status dostawy dla zestawu kolumn.
W systemie docelowym użytkownik może indywidualnie rozszerzyć lub zmniejszyć zestaw kolumn dla każdej bazy danych, jeśli posiada niezbędne uprawnienia. Obiekty biznesowe zdefiniowane w obiekcie deweloperskim Search i w powiązanych obiektach deweloperskich typu Search extension są do tego podstawą. Nie tylko wszystkie atrybuty tych obiektów biznesowych są dostępne do wyboru, ale także atrybuty innych obiektów biznesowych, jeśli odpowiednie relacje 1:1 zostaną wprowadzone w aplikacji Obiekty deweloperskie. Relacje te można śledzić do dowolnej głębokości.
Oprócz zwykłych atrybutów, Pola zdefiniowane przez użytkownika (rozszerzenia encji) i atrybuty wirtualne, a także Managed Supplements mogą być również zawarte na liście kolumn. Atrybuty wirtualne to atrybuty, które nie należą bezpośrednio do obiektu biznesowego, ale mogą być obliczane na podstawie atrybutów obiektów biznesowych.

Możliwość konfigurowania

Interfejs użytkownika aplikacji typu Lista można dostosować na wiele sposobów (patrz artykuł Listy). Konfiguracje można zapisywać na trzech poziomach: dla użytkownika, dla bazy danych OLTP lub w obiekcie deweloperskim Application extension. Nagłówki i obszary robocze są edytowane razem. Dla obszaru roboczego można zapisać dowolną liczbę widoków na poziom; dla nagłówka można zapisać tylko jeden widok na układ.
 
Uwaga
Przed dostarczeniem obiektu deweloperskiego typu Application extension należy zawsze upewnić się, że odpowiednie układy również zostały w nim zapisane. Wyjątkiem są wyszukiwania dla SearchViews, jeśli zostały one skonfigurowane w taki sposób, że ich układy są zapisane w aplikacji.

Wyszukiwanie szczegółowe

Wyszukiwanie szczegółowe można zintegrować z aplikacjami typu Lista, umożliwiając użytkownikom wyświetlanie określonych szczegółowych informacji o wyświetlanych wierszach. Podobnie jak wyszukiwanie główne, wyszukiwanie szczegółowe jest obiektem deweloperskim typu Search. Rola wyszukiwania w aplikacji typu Lista zależy od tego, w jaki sposób wyszukiwania są ze sobą powiązane. Kilka wyszukiwań szczegółowych można przypisać do wyszukiwania głównego, a inne wyszukiwania szczegółowe można przypisać do wyszukiwania szczegółowego (struktura drzewa).
 
Uwaga
Wyszukiwania szczegółowe mogą być obecnie używane tylko w aplikacjach typu Lista, ale nie w SearchViews lub usługach REST.

Model programowania

Rozwój aplikacji typu Lista opiera się głównie na dostarczaniu metadanych, takich jak zestaw kolumn za pośrednictwem obiektu deweloperskiego Search i powiązanych rozszerzeń wyszukiwania.
Podobnie jak w przypadku każdej aplikacji, obiekt deweloperski typu Application i typu Java class są również wymagane dla aplikacji typu Lista. W najprostszym przypadku, implementacja klasy Java zasadniczo polega na wyprowadzeniu specjalnej abstrakcyjnej klasy bazowej (patrz te rozdziały: CisCustomisableCockpit i AbstractOrderCockpit).
Wizualizacja
Wizualizacja danych na liście wyników jest delegowana przez infrastrukturę do specjalnych klas. Klasy używane do wizualizacji są określane przez logiczny typ danych odpowiedniej kolumny. Odpowiednie klasy są już dostarczane przez infrastrukturę dla wszystkich standardowych typów danych. W zależności od typu danych, infrastruktura wykorzystuje Formatter (java.text.Format) lub Renderer (com.cisag.pgm.gui.Renderer). Odpowiednie renderery i formatery są tworzone za pomocą specjalnych Factory (patrz te rozdziały: RendererFactory i FormatFactory). Wizualizację można dostosować, implementując i rejestrując własne Factory.
Filtr
Aby zoptymalizować wyniki wyszukiwania, dla każdej kolumny zestawu kolumn można użyć odpowiednich wyrażeń filtrujących. Programowo takie filtry są reprezentowane przez podklasy klasy Filter Expression (patrz rozdział FilterExpression). Do wyświetlania i edycji wyrażeń filtrujących w nagłówku listy wymagane są specjalne edytory (patrz rozdział FilterExpressionEditor). Edytory te są dostarczane za pośrednictwem odpowiednich klas (patrz rozdział FilterExpressionEditorFactory). Logiczny typ danych odpowiedniej kolumny określa, które klasy muszą zostać użyte. Odpowiednie klasy są już dostarczane przez infrastrukturę dla wszystkich standardowych typów danych. Wyświetlanie i edycję wyrażeń filtrujących można dostosować, implementując i rejestrując własne Factory.

Aplikacje działające w tle

Wynik wyszukiwania z aplikacji typu Lista może zostać przesłany do aplikacji działającej w tle. Użytkownik ma następujące opcje:
  • Obiekty wybrane w obszarze roboczym są przenoszone do aplikacji działającej w tle
  • Właściwości wyszukiwania wprowadzone w nagłówku aplikacji typu Lista w momencie wywołania akcji są przenoszone do aplikacji działającej w tle, z którą wykonywane jest zapytanie do bazy danych w momencie uruchomienia aplikacji działającej w tle. Wynik tego zapytania może różnić się od tego, które zostało już wykonane w aplikacji. W związku z tym, użytkownik niekoniecznie widział wcześniej wynik wyszukiwania, a liczba obiektów do przetworzenia może nieświadomie skutkować niezamierzonym zakresem.
 
Uwaga
Jeśli w nagłówku aplikacji typu Lista nie wprowadzono żadnych cech wyszukiwania, przed wykonaniem odpowiedniej akcji wyświetlany jest komunikat ostrzegawczy. Wyświetlanie tego komunikatu ostrzegawczego można kontrolować za pomocą następującej właściwości: com.cisag.pgm.services.batch.BatchJobSearch.suppressConfirmationDialog.
 
  • Zarówno obiekty wybrane w obszarze roboczym, jak i charakterystyki wyszukiwania z nagłówka listy są przesyłane do aplikacji działającej w tle. W czasie wykonywania aplikacji w tle wykonywane jest zapytanie do bazy danych z funkcjami wyszukiwania, ale w tym przypadku wszystkie obiekty wybrane w obszarze roboczym są pomijane (lista negatywna).
Ta parametryzacja jest transparentna dla aplikacji działającej w tle: otrzymuje ona ResultSet za pośrednictwem interfejsu, który dostarcza odpowiednio przefiltrowane obiekty.
Eksport i usługi sieciowe RESTful
Wyświetlane wiersze wyników można eksportować ze wszystkich aplikacji typu Lista (w tym SearchView), np. do pliku MS Excel lub PDF. Możliwe jest również eksportowanie za pośrednictwem aplikacji działającej w tle. Aby móc korzystać z tej opcji w aplikacji typu Lista, musi być ona połączona z usługą sieciową (RESTful) (patrz artykuł Interfejs do eksportu z aplikacji typu Lista za pomocą usług sieciowych (web service) zgodnych z REST).

Obiekty deweloperskie

Ten rozdział opisuje definicje wymagane dla aplikacji typu Lista w odpowiednich typach obiektów deweloperskich.

Typy obiektów deweloperskich Search i Search extension

Obiekt deweloperski Search służy do definiowania zapytania do bazy danych i zestawu kolumn listy lub wyszukiwania szczegółowego. Wyszukiwania główne i szczegółowe nie są specjalnie oznaczone; zamiast tego relacja między dwoma wyszukiwaniami jest określona w powiązanym kodzie aplikacji Java. Wyszukiwanie może być zatem używane jako wyszukiwanie główne w jednej aplikacji i jako wyszukiwanie szczegółowe w innej.
Uwaga
Zapytanie do bazy danych oraz zestaw kolumn wyszukiwania i wyszukiwania szczegółowego można rozszerzyć za pomocą rozszerzeń wyszukiwania.
Zakładka From/Where: Zapytanie do bazy danych
Zakładka From/Where służy do definiowania obiektów biznesowych zaangażowanych w wyszukiwanie i zdefiniowanie ich relacji dla zapytania do bazy danych.
Przykład
com.cisag.app.edu.obj.Book BOOK
  LEFT OUTER JOIN com.cisag.app.general.obj.Partner EMPLOYEE
    ON BOOK:employee = EMPLOYEE:guid

Uwaga
W miarę możliwości należy przestrzegać znormalizowanej konwencji nazewnictwa dla używanych aliasów. Zaleca się stosowanie nazwy odpowiedniej relacji (jeśli jest dostępna).
Klucze obce
Podczas tworzenia zapytania do bazy danych należy zauważyć, że relacje klucza obcego są wyraźnie opisane przez JOIN, dzięki czemu atrybuty obiektu docelowego mogą zostać przeniesione do zestawu kolumn i, jeśli to konieczne, również użyte jako łącze (patrz rozdział Pole wyboru: Obiekt biznesowy).
REMOVABLE JOIN
W czasie wykonywania, konkretne zapytanie do bazy danych wynika z faktycznie używanych kolumn (wyświetlanie, filtrowanie i sortowanie). Nie tylko klauzule SELECT, WHERE i ORDER BY są odpowiednio konfigurowane, ale także niepotrzebne JOIN są usuwane z klauzuli FROM, jeśli są odpowiednio oznaczone. Aby oznaczyć JOIN jako usuwalny, przed LEFT OUTER JOIN należy wstawić dodatkowe słowo kluczowe REMOVABLE.
Przykład
 
SELECT BOOK:code
  FROM com.cisag.app.edu.obj.Book BOOK
  REMOVABLE LEFT OUTER JOIN
     com.cisag.app.general.obj.Partner EMPLOYEE
    ON BOOK:employee = EMPLOYEE:guid
staje się:
SELECT BOOK:code
  FROM com.cisag.app.edu.obj.Book BOOK
 
Uwaga
W przypadku zestawu wartości, którego zakres wartości został ograniczony za pomocą Data description, warunek ten jest automatycznie uwzględniany w klauzuli WHERE, nawet jeśli odpowiednia kolumna w aplikacji typu Lista nie jest używana jako filtr. Z tego powodu JOIN zawierające takie kolumny nie mogą zostać usunięte. Przy ograniczonym zakresie zestawu wartości należy zatem zawsze sprawdzić, czy w bazie danych mogą być faktycznie przechowywane inne wartości niż wartości ograniczone. Jeśli tak nie jest, powiązany atrybut obiektu biznesowego powinien być oznaczony jako kompletny. Oznacza to, że klauzula WHERE nie musi być rozszerzana (patrz rozdział BusinessObjectRegistryHook).
Automatyczna optymalizacja
Inną opcją optymalizacji jest zastąpienie niektórych JOIN przez dostęp do usługi trwałości. W takim przypadku JOIN i wszystkie atrybuty danego obiektu biznesowego są usuwane podczas zapytania do bazy danych. Zamiast tego klucz podstawowy obiektu biznesowego jest zawarty w klauzuli SELECT. Persistent service jest następnie używane do ładowania obiektów biznesowych (blok po bloku) i określania wartości żądanych atrybutów.
Przykład
SELECT BOOK:code, EMPLOYEE:number
  FROM com.cisag.app.edu.obj.Book BOOK
    LEFT OUTER JOIN
    com.cisag.app.general.obj.Partner EMPLOYEE
    ON BOOK:employee = EMPLOYEE:guid
staje się:
SELECT BOOK:code, BOOK:employee
  FROM com.cisag.app.edu.obj.Book BOOK
plus dostęp przez Persistent service
Partner partner = om.getObject(
      Partner.getPrimaryKey(rs.getGuid(2));
  String number = partner.getNumber();
 
Zalety tej procedury polegają na włączeniu współdzielonej pamięci podręcznej (szczególnie w przypadku danych podstawowych) i zmniejszeniu złożoności zapytania do bazy danych. Istnieje jednak ryzyko, że wynik określony w ten sposób będzie różnił się od wyniku niezoptymalizowanego zapytania. W związku z tym, optymalizacja ta nie jest aktywowana automatycznie, ale musi być aktywowana jawnie dla każdego JOIN poprzez dodatkowe określenie słowa kluczowego REMOVEABLE. W praktyce powyższe ryzyko jest bardzo rzadko istotne, ponieważ nawet w niezoptymalizowanym przypadku dane mogą ulec zmianie po ich odczytaniu z bazy danych. Ze względu na wyraźne korzyści, zaleca się, aby słowo kluczowe REMOVEABLE było również określone dla (LEFT OUTER) JOIN.
Optymalizacja jest możliwa tylko w przypadku niektórych typów JOIN: Możliwe tylko w przypadku LEFT OUTER JOIN, a połączenie musi być wykonane za pomocą klucza głównego. Te warunki wstępne są automatycznie sprawdzane, gdy wyszukiwanie jest zapisywane (sprawdzane) i w razie potrzeby wyświetlany jest komunikat o błędzie. W czasie wykonywania optymalizacja jest stosowana tylko wtedy, gdy atrybuty obiektu biznesowego są używane wyłącznie do wyświetlania (tj. w klauzuli SELECT).
Zakładka Atrybuty: Zestaw kolumn
W aplikacji Obiekty deweloperskie, zarówno dla typu Search, jak i typu Search extension, zestaw kolumn jest definiowany na liście w obszarze roboczym w zakładce Edytor i znajdującej się pod nią zakładce Atrybuty . Kolumny wymagane w aplikacji typu Lista muszą zostać dodane do tej listy jako atrybuty wyszukiwania. Definicja atrybutu (kolumny) składa się z nazwy atrybutu, wyrażenia i innych parametrów. Dostępne są różne typy kolumn: atrybuty regularne, kolumny obliczane i kolumny wirtualne.
 
Uwaga
Zestaw kolumn powinien już zawierać wszystkie atrybuty, które są zwykle wymagane przez użytkowników. Należy również wziąć pod uwagę, że zestaw kolumn w systemie docelowym nie może zostać rozszerzony zgodnie z wymaganiami. Dostępne są tylko te obiekty biznesowe, które zostały jawnie zadeklarowane w klauzuli FROM lub do których można uzyskać dostęp za pośrednictwem relacji (1:1) zdefiniowanych w aplikacji Obiekty deweloperskie.
Kolumna Nazwa
Podczas przypisywania nazwy atrybutu należy przestrzegać następujących punktów:
  • Nazwa musi być unikalna w ramach wyszukiwania
  • Nazwa służy jako identyfikacja kolumny i jest również używana poza własnym obiektem deweloperskim, np. w interfejsach programowania oraz zapisanych układach i wzorcach wyszukiwania
  • Aby móc przenieść wyrażenie filtru do aplikacji typu Lista jako parametr aplikacji, nazwa kolumny musi być używana jako nazwa parametru
Wskazówka
Nazwy parametrów aplikacji muszą zawsze zaczynać się wielką literą, nazwy atrybutów – małą literą. Różnica ta jest automatycznie uwzględniana podczas porównywania.
 
Uwaga
Po zakończeniu wyszukiwania nie należy już usuwać ani zmieniać nazw zdefiniowanych w nim atrybutów. Usunięcie lub zmiana nazwy atrybutów jest niezgodną zmianą wyszukiwania i spowoduje, że wszelkie konfiguracje wyszukiwania i zadania przetwarzania oparte na starej wersji wyszukiwania będą musiały zostać odrzucone lub anulowane w systemie rzeczywistym.
 
Jeśli to możliwe, nazwy powinny być przypisywane zgodnie ze znormalizowanym schematem. W przypadku zwykłych atrybutów zaleca się pobranie nazwy atrybutu z obiektu biznesowego i poprzedzenie jej aliasem obiektu biznesowego (małe litery). W ten sposób można łatwo zapewnić unikalność nazw, nawet jeśli obiekt biznesowy jest używany wielokrotnie (przykłady: bookCode, employeeNumber).
 
Uwaga
Przycisk [Wybór atrybutu] na pasku przycisków listy może być użyty do przeniesienia kilku atrybutów z obiektu biznesowego (aliasu) do wyszukiwania w tym samym czasie. Użytkownik może również określić żądany prefiks.
Kolumna Wyrażenie
W kolumnie Wyrażenie na liście atrybutów należy wprowadzić jedną z poniższych informacji:
  • Odniesienie do atrybutu obiektu biznesowego (składnia: ALIAS:Nazwa atrybutu)
  • Wyrażenie obliczane przez bazę danych (patrz rozdział Kolumny obliczane)
  • Odniesienie do obiektu biznesowego (patrz rozdział Złożone typy danych)
  • Puste, jeśli dotyczy atrybutu wirtualnego (patrz rozdział Pole wyboru: Wirtualnie)

Kolumny obliczane

Niektóre funkcje (UPPER, AVG, MAX, MIN, SUM, COUNT) i operacje (*, /, +, -) mogą być również używane jako wyrażenie dla atrybutu.
Przykład
BO:a - BO:b
BO:a + BO:b + BO:c
SUM(BO:a) - SUM(BO:b)
SUM(BO:a) - BO:b // niedostępne
Zalety:
  • Funkcje agregujące często mogą być lepiej obliczane w bazie danych
  • Proste obliczenia mogą być wykonywane bez wirtualnych kolumn i nie wymagają specjalnych rendererów
  • Kolumny mogą być filtrowane i sortowane w bazie danych (nie dotyczy funkcji agregujących)
Ograniczenia:
  • Atrybuty wirtualne i renderery są nadal wymagane dla Special Parts
  • Baza danych może dawać inne wyniki dla niektórych operacji niż wyspecjalizowane logiki w Comarch ERP Enterprise (np. reguły uruchamiania)
  • Długość wyrażenia jest ograniczona do 100 znaków
Złożone typy danych

Parts lub całe obiekty biznesowe mogą być również zawarte w zestawie kolumn pod następującymi warunkami:

  • Do wizualizacji (obszar wyświetlania) wymagany jest odpowiedni FormatFactory lub RendererFactory
  • Filtrowanie i sortowanie jest możliwe tylko z dodatkowymi (wirtualnymi) atrybutami (wyjątek: Special parts są oferowane przez system)
  • Parts są wybierane w edytorze wyszukiwania jak zwykłe atrybuty
  • W przypadku obiektów biznesowych w polu Wyrażenie określa się tylko alias (bez nazwy atrybutu). Pole wyboru Obiekt biznesowy musi być również aktywowane.
Uwaga
Zamiast wczytywać całe obiekty biznesowe, zazwyczaj korzystniej jest użyć tylko wymaganych atrybutów (wirtualne kolumny).
Pole wyboru: Wirtualnie
Kilka atrybutów można podsumować w ramach atrybutu wirtualnego i w ten sposób wyświetlić jako pojedynczą kolumnę. Ta opcja jest przydatna, jeśli w interfejsie użytkownika ma być wyświetlana tylko jedna kolumna, ale do wyświetlenia wymagane są dane z kilku kolumn.
Atrybut wirtualny musi być oznaczony jako Wirtualnie, a pole Wyrażenie musi pozostać puste. Atrybuty należące do atrybutu wirtualnego muszą zaczynać się od nazwy atrybutu wirtualnego, po której następuje kropka.
Przykład (patrz Status w artykule Lista: Zamówienia sprzedaży):
 
Nazwa Wyrażenie Wirtualnie Logiczny typ danych
vstatus   X ..SalesOrderStatus
vstatus.attr1 SO:attr2    
vstatus.attr2 SO:attr2    
   
 
Aby atrybut wirtualny mógł być wyświetlany w aplikacji typu Lista, należy zapewnić odpowiedni RendererFactory i odpowiedni FilterExpressionEditorFactory, aby móc użyć atrybutu jako filtra (patrz rozdziały RendererFactory i FilterExpressionEditorFactory). W tym celu należy wprowadzić logiczny typ danych w kolumnie Logiczny typ danych atrybutu, który jest powiązany z opisem danych, w którym wprowadzane są implementacje tych klas (patrz rozdział: Typ obiektu deweloperskiego Data description).
Dla każdego z atrybutów podrzędnych można określić, czy mają one być wyświetlane i możliwe do filtrowania. Ustawienia te określają, które z atrybutów podrzędnych są przekazywane do renderera lub FilterExpressionEditor. Jeśli atrybut wirtualny jest oznaczony jako możliwy do filtrowania, wówczas co najmniej jeden z atrybutów podrzędnych musi być również oznaczony jako możliwy do filtrowania. To samo dotyczy atrybutów oznaczonych jako wyświetlane.
W przypadku sortowania atrybutów wirtualnych obowiązuje następująca zasada: jeśli atrybut wirtualny jest oznaczony jako sortowalny, wówczas dokładnie jeden z podatrybutów musi być oznaczony jako sortowalny.
Pole wyboru: Obiekt biznesowy
Odznaczenie jako obiekt biznesowy określa, że kolumna ma podobną funkcjonalność do EntityField. Atrybuty oznaczone w ten sposób mają menu kontekstowe i mogą być używane jako link. Menu kontekstowe i link zawsze odnoszą się do obiektu biznesowego, do którego należy sam atrybut (zdefiniowany poprzez alias). Każdy atrybut powinien otrzymać takie oznaczenie, jeśli reprezentuje klucz biznesowy własnego obiektu biznesowego.
Aby zapewnić menu kontekstowe i linki również dla atrybutów klucza obcego, wymagane jest inne podejście. Pierwszą (i preferowaną) opcją jest dodanie obiektu docelowego i atrybutu docelowego do zapytania bazy danych i zestawu kolumn (jawny JOIN). Jeśli JOIN nie jest możliwy, dla atrybutu można zapisać specjalny URIRenderer, który może obliczyć URI dla obiektu docelowego z klucza obcego (patrz rozdział URIRenderer).
Odznaczanie jako obiekt biznesowy jest również konieczne, jeśli w kolumnie Wyrażenie nie zostanie wprowadzony żaden atrybut obiektu biznesowego, a jedynie alias obiektu biznesowego. To ustawienie określa, że cały obiekt biznesowy jest odczytywany z bazy danych zamiast pojedynczego atrybutu.
 
Uwaga
W miarę możliwości należy unikać używania całych obiektów biznesowych ze względu na następujące wady: zwiększa się zapotrzebowanie na pamięć i obciążenie bazy danych. Takie kolumny wymagają dodatkowego działania deweloperskiego (renderer) i ani filtrowanie, ani sortowanie nie jest dostępne dla użytkownika.
Pole wyboru: Zaprogramowany filtr
Filtrowanie może być wykonywane nie tylko przez użytkownika, ale także przez programowanie. Za pomocą pola wyboru Zaprogramowany filtr można skonfigurować kolumnę tak, aby filtrowanie było możliwe tylko przez programowanie. Użytkownicy nie mogą już wstawiać kolumn oznaczonych w ten sposób jako pole wyszukiwania w nagłówku. Głównymi przypadkami użycia definicji jako zaprogramowanego filtra są implementacja uprawnień, konfiguracja ustawień i łączenie szczegółowych wyszukiwań.
 
Uwaga
W wyszukiwaniu atrybutu obiektu biznesowego można również wprowadzić kilka atrybutów wyszukiwania. W ten sposób, w razie potrzeby, można również połączyć ze sobą kilka filtrów (AND).
Pole wyboru: Możliwość filtrowania
Tylko te kolumny, które są odznaczone jako możliwe do filtrowania, mogą być zawarte w nagłówku aplikacji typu Lista. Wszystkie kolumny, które mogą być wyświetlane, powinny być również włączone do filtrowania, ponieważ odpowiada to ogólnym oczekiwaniom użytkowników.
 
Uwaga
Jeśli atrybut wirtualny jest oznaczony jako możliwy do filtrowania, wówczas co najmniej jeden atrybut grupy musi być oznaczony jako możliwy do filtrowania. Ponadto, w atrybucie wirtualnym musi być określony logiczny typ danych z odpowiednim FilterExpressionEditorFactory.
Pole wyboru: Możliwe sortowanie
Aby kolumna w aplikacji typu Lista mogła być używana jako funkcja sortowania, musi być oznaczona jako sortowalna. Wszystkie kolumny, które mogą być wyświetlane, powinny być również włączone do sortowania. Ze względu na to, że jednak sortowanie zawsze odbywa się w bazie danych, w każdym indywidualnym przypadku należy sprawdzić, czy wynik będzie naprawdę przydatny. Decyzja zależy przede wszystkim od typu danych kolumny.
 
Uwaga
Jeśli atrybut wirtualny jest odznaczony jako możliwy do sortowania, wówczas dokładnie jeden atrybut grupy musi być oznaczony jako możliwy do sortowania. Atrybut wirtualny jest sortowany zgodnie z tym atrybutem.
Pole wyboru: Możliwy do wyświetlenia
Aby kolumna mogła być używana na liście wyników aplikacji typu Lista, musi być oznaczona jako możliwa do wyświetlenia.
Kolumna odznaczona jako możliwa do wyświetlenia powinna być również zawsze oznaczona jako filtrowana i sortowana, jeśli to możliwe. Dzięki tym ustawieniom nagłówki kolumn mogą być przeciągane i upuszczane bezpośrednio do obszaru zapytania w celu filtrowania lub sortowania bezpośrednio poprzez kliknięcie nagłówka kolumny. Ze względu na to, że dotyczy to większości kolumn, użytkownicy oczekują tej funkcjonalności od wszystkich kolumn.
Kolumny, które zostały uwzględnione w zestawie kolumn tylko ze względów technicznych, takich jak klawisz powrotu lub zaprogramowane filtry, powinny być oznaczone jako niewyświetlane.
 
Uwaga
Jeśli atrybut wirtualny jest oznaczony jako możliwy do wyświetlenia, co najmniej jeden atrybut grupy musi być oznaczony jako wyświetlany. Ponadto, dla atrybutu wirtualnego musi być zapisany logiczny typ danych z odpowiednim FormatFactory lub RendererFactory.
Pole wyboru: Klucz zwrotu
Odznaczenie jako klucz zwrotu służy do jednoznacznej identyfikacji zestawu danych (wiersza wyników). Z reguły jest to jeden lub więcej identyfikatorów GUID, które tworzą klucz podstawowy obiektu biznesowego. Podczas, gdy wszystkie inne atrybuty są ładowane z bazy danych tylko wtedy, gdy są faktycznie używane w widoku, atrybuty oznaczone jako klucze zwrotu są zawsze ładowane. Odznaczenie jako klucz zwrotny jest zatem konieczne również wtedy, gdy poszczególne wartości atrybutów muszą być dostępne w interfejsie użytkownika niezależnie od ich użycia. Przykładami tego są
  • transfer i dostęp z przetwarzania w tle
  • atrybuty do łączenia szczegółowych wyszukiwań
  • atrybuty, które są sprawdzane podczas analizy wybranych lub zaznaczonych wierszy
W większości przypadków jednak klucz podstawowy jednostki biznesowej jest wystarczający dla wszystkich tych zastosowań, więc tylko bardzo rzadko inne atrybuty muszą być oznaczone jako klucze zwrotu.
 
Uwaga
Atrybuty oznaczone jako klucze zwrotu są zazwyczaj atrybutami technicznymi (np. GUID), które powinny być niewidoczne dla użytkowników. Takie atrybuty techniczne nie powinny być zatem oznaczane jako wyświetlane, filtrowane lub sortowane.
Logiczny typ danych
Infrastruktura wymaga pewnych metadanych dla każdego atrybutu, takich jak typ danych, etykieta i ewentualnie specjalne Factory. Wymagane metadane są dostarczane przez logiczny typ danych i powiązany opis danych. W przypadku atrybutów wyszukiwania, które bezpośrednio odwołują się do atrybutu z obiektu biznesowego, logiczny typ danych i opis danych są pobierane z atrybutu obiektu biznesowego. Odpowiedni logiczny typ danych musi zostać wprowadzony dla wszystkich innych typów atrybutów, tj. atrybutów wirtualnych, atrybutów wyliczanych i obiektów biznesowych. Logiczny typ danych musi być powiązany z opisem danych, który definiuje etykietę i niezbędne czynniki dla atrybutu (patrz ten rozdział: Typ obiektu deweloperskiego Data description).
 
Uwaga
Logiczny typ danych pobrany z atrybutu obiektu biznesowego jest pomijany poprzez wyraźne określenie innego logicznego typu danych. Może to być konieczne, jeśli logiczny typ danych określony dla atrybutu obiektu biznesowego nie ma pożądanej unikalnej etykiety lub pożądanych fabryk. Jeśli jednak wyraźnie określono inny logiczny typ danych, należy zadbać o to, aby prymitywny typ danych pozostał niezmieniony.
Zalecenia dotyczące zestawu kolumn
Etykieta
Aby użytkownicy mogli identyfikować i używać kolumn z list kolumn bez pomyłek, oznaczenia (etykiety) powinny się od siebie różnić. Ze względu na to, że etykieta jest definiowana za pomocą logicznego typu danych, może być konieczne określenie alternatywnych logicznych typów danych dla niektórych kolumn, tak aby móc nadać tym kolumnom unikalne nazwy.
Znacznik usuwania
Podobnie jak każda inna kolumna, znacznik usuwania musi zostać jawnie dodany do zestawu kolumn. Atrybut updateInfo.deleteTime musi być wybrany z odpowiedniego obiektu biznesowego. Dla nazwy kolumny należy użyć konwencji <alias>UpdateInfoDeleteState.
Logicznym typem danych powinien być com.cisag.pgm.datatype.DeleteState lub typ danych pochodny od niego. Odpowiednie Renderer i FilterExpressionEditorFactory są automatycznie dostarczane za pośrednictwem powiązanego Data description, tak aby zwykła wizualizacja i filtrowanie były używane dla znaczników usuwania.
Zakładka Układy
Układy aplikacji typu Lista są zapisywane w rozszerzeniach aplikacji. Układy w wyszukiwaniach są używane tylko wtedy, gdy nie ma odpowiedniego układu w rozszerzeniu aplikacji.
Układy SearchViews są nadal zapisywane w wyszukiwaniach, jeśli są odpowiednio ustawione.
Wszystkie układy zapisane bezpośrednio w tym obiekcie deweloperskim są wymienione w zakładce Układy. Wyświetlanie służy głównie do sprawdzania, edycja odbywa się bezpośrednio w SearchView. Aby móc edytować zapisane układy za pomocą SearchView, ten obiekt deweloperski musi najpierw zostać włączony do zadania deweloperskiego przez programistę.
Aby SearchView mógł być natychmiast używany przez użytkowników, nagłówek i co najmniej jeden widok dla obszaru roboczego powinny być zapisane w wyszukiwaniu. W razie potrzeby pola nagłówka powinny mieć wartości domyślne, które są użyteczne dla użytkownika i powodują najniższe możliwe wykorzystanie bazy danych.
Zakładka Ustawienia
Hook
W polu Hook można zarejestrować Java class implementującą interfejs com.cisag.pgm.search.SearchHook. Klasa ta może być używana do manipulowania zapytaniem do bazy danych przed jego wykonaniem (patrz rozdział SearchHook).
Nazwę klasy obiektu biznesowego można wprowadzić w polu Obiekt bazowy. Gdy wywoływana jest metoda init dla hook, odpowiedni obiekt klasy jest przekazywany jako parametr.
Implementacja SearchHook jest alternatywą dla zmiany metody preSearch (patrz rozdziały CisCustomisableCockpit i SearchView). Jest to szczególnie przydatne, jeśli wyszukiwanie jest używane nie tylko przez pojedynczą implementację CisCustomisableCockpit, ale powinno być również użyteczne np. jako usługa REST.
Baza danych
Aktywując pole wyboru Distinct, zapytanie do bazy danych można rozszerzyć o słowo kluczowe DISTINCT. Podczas korzystania z tego słowa kluczowego należy zwrócić uwagę na następujące kwestie:
  • Zestaw kolumn zawiera już jeden lub więcej atrybutów, które razem zapewniają unikalną identyfikację obiektu (np. klucz podstawowy obiektu biznesowego). Te atrybuty oznaczone jako klucz zwrotu w zestawie kolumn są zawsze zawarte w klauzuli SELECT. Ze względu na unikalność klucza zwrotu, żadne zduplikowane obiekty nie mogą wystąpić w wyniku, a dodatkowe DISTINCT tylko niepotrzebnie obciążyłoby bazę danych.
  • Jeśli to możliwe, relacje Do-N powinny być zlecane wyszukiwaniom szczegółowym
  • W klauzuli SELECT automatycznie uwzględniany jest nie tylko klucz zwrotu, ale także wszystkie właściwości sortowania i grupowania. Wynik zależy zatem również od tego, czy i które kolumny są używane do sortowania i grupowania.

Typ obiektu deweloperskiego Search extension

Rozszerzenia wyszukiwania rozszerzają zestaw kolumn i klauzulę FROM przywołanego wyszukiwania. Dla jednego wyszukiwania może istnieć kilka rozszerzeń wyszukiwania. W systemie tworzenia aplikacji można utworzyć rozszerzenie wyszukiwania dla wyszukiwania w każdej aplikacji. W standardowym systemie deweloperskim dla wyszukiwania można utworzyć tylko jedno rozszerzenie wyszukiwania na prefiks deweloperski. Rozszerzenia wyszukiwania są powiązane tylko z wyszukiwaniem. Nie można tworzyć między nimi żadnych relacji.
Rozszerzenia klauzul FROM wszystkich rozszerzeń wyszukiwania są dołączane do klauzuli FROM wyszukiwania za pomocą REMOVABLE LEFT OUTER JOIN. Atrybuty są dodawane do listy kolumn. Unikalność atrybutów jest osiągana poprzez poprzedzenie ich prefiksem deweloperskim i nazwą aplikacji.
Po scaleniu wyszukiwanie zachowuje się tak, jakby wszystko zostało zdefiniowane w samym wyszukiwaniu.

Typ obiektu deweloperskiego Application

W przypadku aplikacji typu Lista (CisCustomisableCockpit) w polu Szczególne zastosowanie należy wybrać pozycję Lista, a w polu Szukanie wpisać pełną nazwę obiektu deweloperskiego. Wprowadzenie wyszukiwania jest ważne, aby zestaw kolumn wyszukiwania mógł być automatycznie używany jako definicja parametru dla aplikacji. Eliminuje to również konieczność zmiany metody getDefaultCockpitSearchName() w klasie CisCustomisableCockpit (patrz rozdział CisCustomisableCockpit).
 
Uwaga
Aby włączyć rozszerzony eksport w aplikacji typu Lista (patrz rozdział Eksport i usługa webservice RESTful), należy również utworzyć aplikację typu Usługa sieciowa (specjalne zastosowanie: Usługa REST). W takim przypadku obiekt deweloperski Search musi zostać wprowadzony zarówno w aplikacji dla aplikacji typu Lista, jak i w aplikacji dla usługi sieciowej.

Typ obiektu deweloperskiego Application extension

Układy aplikacji typu Lista są zapisywane w rozszerzeniach aplikacji. Dla jednej aplikacji może istnieć kilka rozszerzeń aplikacji, przy czym dla każdego prefiksu deweloperskiego może istnieć tylko jedno rozszerzenie, a w systemach deweloperskich aplikacji – jedna aplikacja.
 
Przykład dla prefiksu deweloperskiego:
Aplikacja: com.cisag.app.general.ui.MyApplication
Rozszerzenia aplikacji:
com.cisag.app.general.ui.MyFirstApplicationExtension
com.bdv.app.general.ui.BranchApplicationExtension
____________________________________________
Przykład systemu tworzenia aplikacji:
Aplikacja: com.sem.ext.app.abc.ui.MyApplication
Rozszerzenia aplikacji:
com.sem.ext.app.abc.ui.MyFirstApplicationExtension
com.sem.ext.app.def.ui.MySecondApplicationExtension
com.cdv.app.general.ui.CustomerApplicationExtension
Układy są zarządzane w aplikacjach typu Lista.

Typ obiektu deweloperskiego Data description

Metadane kolumny pochodzą z logicznego typu danych i powiązanego Data description (w razie potrzeby przez dziedziczenie). W przypadku kolumn, które bezpośrednio odwołują się do atrybutu obiektu biznesowego, przyjmowany jest logiczny typ danych atrybutu. Alternatywnie, kolumnie można również przypisać specjalny logiczny typ danych. Specjalny logiczny typ danych musi być przypisany do kolumn wirtualnych, kolumn obliczanych i obiektów biznesowych.
Następujące właściwości Data description są oceniane przez aplikacje typu Lista:
  • Pełna etykieta i pomoc kontekstowa
  • FormatFactory
  • RendererFactory
  • FilterExpressionEditorFactory
  • DataDescriptionFilter
  • Typ zawartości (dla stringów)
Pełna etykieta i pomoc kontekstowa
W opisie danych rozróżnia się Etykietę i Pełną etykietę. W ten sposób można zapewnić dwa różne oznaczenia dla atrybutu z tylko jednym opisem danych. Przykładowo, etykieta dla atrybutu Opis obiektu biznesowego Artykuł to tylko Opis, pełna etykieta to Opis artykułu. Podczas, gdy inne aplikacje używają etykiety, aplikacje typu Lista używają pełnej etykiety. Jeśli w opisie danych nie ma pełnej etykiety, zamiast niej używana jest etykieta.
W opisie danych można również wprowadzić drugi tekst dla pomocy kontekstowej. Bezpośrednia pomoc dla pełnej etykiety jest używana w aplikacjach typu Lista.
Factory
Odpowiednie fabryki są dostarczane przez infrastrukturę dla wszystkich prymitywnych typów danych i Special Parts. Dla wszystkich innych typów danych, a w szczególności dla kolumn wirtualnych, kolumn wyliczanych i obiektów biznesowych, należy zaimplementować odpowiednie Factory i określić je w opisie danych.
Jeśli kolumna jest wyświetlana, należy zapewnić FormatFactory lub RendererFactory. Dla atrybutów, które mogą być filtrowane, wymagana jest FilterExpressionEditorFactory.

Obiekty biznesowe proxy

Wyszukiwania są używane w aplikacjach typu Lista. W niektórych przypadkach przydatne może być użycie informacji z repozytorium lub bazy danych konfiguracji w wyszukiwaniu. Przykładowo, informacje te mogą być wykorzystane do poinformowania użytkownika, o tym kto utworzył lub ostatnio zmienił obiekt. Wyświetlanie wersji używanego obiektu deweloperskiego jest bardziej istotne z technicznego punktu widzenia.
Ze względów technicznych nie jest możliwe JOIN poprzez obiekty biznesowe z różnych baz danych. W związku z tym, dostępne są obiekty biznesowe proxy. Obiekty biznesowe proxy są substytutami obiektów biznesowych z bazy danych repozytorium i bazy danych konfiguracji w bazie danych OLTP.
Obiekty te mogą być aktualizowane w bazie danych OLTP za pomocą aktualizacji danych. Żadna logika nie może być oparta na obiektach biznesowych proxy. Są one używane wyłącznie do wizualizacji w aplikacjach typu Lista.
Ze względu na to, że obiekty biznesowe proxy nie są używane z wyjątkiem bardziej technicznych analiz, dane niekoniecznie muszą być wypełnione. W związku z tym, każde użycie w wyszukiwaniu powinno być scharakteryzowane jako REMOVABLE LEFT OUTER JOIN (patrz rozdział REMOVABLE JOIN).
Obiekty biznesowe proxy mają ten sam klucz podstawowy, co ich odpowiedniki. Są one również zredukowane do niektórych atrybutów istotnych dla wyświetlania.
Obiekty konfiguracyjne

Namespace: com.cisag.sys.configuration.obj

Oryginalny Proxy
User ProxyUser
UserGroup ProxyUserGroup
UserGroupAssignment  ProxyUserGroupAssignment
 
Obiekty repozytorium
Namespace: com.cisag.sys.repository.internal.obj
 
Oryginalny Proxy
ObjectDirEntr ProxyObjectDirEntry
Version  ProxyVersion
 
Aktualizacja danych
Obiekty biznesowe proxy można aktualizować za pomocą aplikacji Aktualizacja obiektów biznesowych proxy (com.cisag.sys.update.log.UPDProxyBOs). Aplikacja ta jest powtarzalną aktualizacją danych.

Interfejsy programowania Java

W tym rozdziale opisano klasy Java istotne dla aplikacji typu Lista. Dalsze informacje na temat poszczególnych metod można również znaleźć w JavaDoc odpowiednich klas.

CisCustomisableCockpit

Wszystkie aplikacje typu Lista muszą wywodzić się z następującej abstrakcyjnej klasy bazowej:
com.cisag.pgm.base.CisCustomisableCockpit
 
Klasa ta rozszerza poniższą klasę i oferuje różne metody hook i szablonów, które mogą być zmieniane przez konkretne podklasy:
com.cisag.pgm.base.CisUiApplication
 
Uwaga
Klasa AbstractOrderCockpit zapewnia specjalną podklasę do implementacji aplikacji typu Lista związanych z dokumentami, co upraszcza i standaryzuje rozwój tych aplikacji (patrz rozdział AbstractOrderCockpit).
Metody hook
Poniższe metody są wywoływane przez infrastrukturę dla określonych zdarzeń lub w określonym czasie. Zmieniając te metody, aplikacja typu Lista może dostosować i uzupełnić standardową funkcjonalność.
 
Metoda  Objaśnienie
getSearchContext() Aplikacje typu Lista muszą zmienić tę metodę, aby odpowiedni kontekst wyszukiwania był dostępny dla pól wyszukiwania.
getDefaultCockpitSearchName()
@Odrzucony od V5R0M0-PD-03
Od V5R0M0-PD-03 aplikacja typu Lista jest powiązana z wyszukiwaniem bezpośrednio w obiekcie deweloperskim Application. W związku z tym, zmiana tej metody jest konieczna tylko wtedy, gdy w aplikacji nie wprowadzono żadnego wyszukiwania. W takim przypadku należy zwrócić w pełni kwalifikowaną nazwę wyszukiwania.
preInit() Jest wywoływana raz przed rozpoczęciem wewnętrznego inicjalizacji.
postInit() Jest wywoływana raz po zakończeniu wewnętrznej inicjalizacji. Aplikacje typu Lista powinny zmienić tę metodę, aby wykonać własne inicjalizacje (np. zarejestrować akcje do przetwarzania w tle, patrz rozdział Rejestrowanie akcji).
preSearch()

Jest wywoływana przed wykonaniem wyszukiwania w bazie danych. Aplikacje typu Lista mogą zmienić tę metodę, aby zmienić kryteria wyszukiwania (np. ustawić wartości dla zaprogramowanych filtrów).

Uwaga
Zamiast zmiany tej metody można również zarejestrować implementację com.cisag.pgm.search.SearchHook w obiekcie deweloperskim Search (patrz rozdział Hook). Ta procedura jest szczególnie zalecana, jeśli wyszukiwanie może być również używane w innym kontekście.
postSearch() Jest wywoływana po przeprowadzeniu wyszukiwania w bazie danych. Aplikacje typu Lista mogą zmienić tę metodę, aby reagować na informacje z wyniku wyszukiwania.
validateFilter()
Aplikacje typu Lista mogą zmienić tę metodę, aby zaimplementować dodatkowe sprawdzanie filtrów. Filtry są automatycznie sprawdzane przed wykonaniem wyszukiwania.
 
Użytkownicy mogą to sprawdzić, wybierając przycisk [Weryfikuj].
Programiści mogą wywołać metodę validateFilter() w klasie GridSearchSupport, aby zainicjować sprawdzanie (patrz rozdział GridSearchSupport).
Metoda musi zwracać wartość true, jeśli podczas sprawdzania nie wykryto żadnych błędów. Jeśli błędy zostały wykryte, metoda musi zwrócić false i wcześniej wysłać odpowiednie komunikaty za pomocą GridSearchSupport#sendMessage().
clearResult()
Jest wywoływana po usunięciu wyniku wyszukiwania. Aplikacje typu Lista mogą zmienić tę metodę, aby reagować na zmiany wyniku wyszukiwania. Wynik wyszukiwania jest automatycznie usuwany przed każdym nowym wyszukiwaniem.
 
Użytkownicy mogą wyczyścić wynik wyszukiwania, klikając przycisk [Nowy].
Programiści mogą wywołać metodę clearResult() w klasie GridSearchSupport, aby wyczyścić wynik wyszukiwania (patrz rozdział GridSearchSupport).
resetFilter()

Jest wywoływana po zresetowaniu wszystkich filtrów do ich wartości domyślnych. Aplikacje typu Lista mogą zmienić tę metodę, aby ustawić własne wartości domyślne, ale oryginalne wartości domyślne nie powinny być zmieniane. Filtry są inicjowane automatycznie tylko za pomocą akcji [Otwórz puste].

Użytkownicy mogą wyczyścić wynik wyszukiwania, klikając przycisk [Nowy].
Programiści mogą wywołać metodę clearResult() w klasie GridSearchSupport, aby wyczyścić wynik wyszukiwania (patrz rozdział GridSearchSupport).
 
 
Dalsze metody
Metoda  Objaśnienie
setExecuteActions(List<Action>) Wszystkie akcje zarejestrowane za pomocą tej metody są podsumowywane na standardowym pasku przycisku w przycisku menu [Wybierz akcję] powszechnie używanym w aplikacji typu Lista. Metoda ta powinna zostać wywołana raz podczas inicjalizacji aplikacji (patrz metoda postInit()).
load(boolean record) Metoda ta może być używana do wyzwalania otwierania (aktualizacji) obiektów bezpośrednio poprzez programowanie.
refreshUi() Za pomocą tej metody można odświeżyć wyświetlane wyniki bez ponownego otwierania obiektów.
getGridSearchSupport() Metoda ta zwraca instancję klasy GridSearchSupport, która umożliwia rozszerzony dostęp do konfiguracji i kontroli. (Patrz rozdział: GridSearchSupport).
getBatchApplicationParameters() Wyszukuje obiekty, które muszą zostać przekazane do przetwarzania w tle.
 
Akcje i przetwarzanie w tle
Operacje masowe powinny być zawsze implementowane jako aplikacja działająca w tle i nie powinny być wykonywane w oknie dialogowym. Jest to jedyny sposób, aby zapewnić, że nie wystąpią problemy z zapotrzebowaniem na pamięć i że użytkownik nie będzie musiał czekać zbyt długo, aż system będzie ponownie gotowy do wprowadzania danych. Ponadto, tylko aplikacje działające w tle pozwalają na przetwarzanie wszystkich obiektów pasujących do kryterium wyszukiwania, podczas gdy wybór jest ograniczony do maksymalnej liczby obiektów (np. 1000).
Poniższe fragmenty kodu pokazują, w jaki sposób rejestrowane są akcje niestandardowe, tak aby
  • były dołączane do przycisku akcji na standardowym pasku przycisków
  • bieżące kryteria wyszukiwania i wybór użytkownika były przesyłane jako parametry do aplikacji działającej w tle, a parametry te były używane w aplikacji działającej w tle do iteracji po wybranych obiektach
Więcej informacji można znaleźć w rozdziale Aplikacje działające w tle.
Rejestrowanie akcji
@Override
protected void postInit() {
  Action action1 = new Action(...);
  Action action2 = new Action(...);
  List<Action> actions = new ArrayList<Action>();
  actions.add(testBatch1Action);
  actions.add(testBatch2Action);
  setExecuteActions(actions);
}
Przekazanie parametrów do aplikacji działającej w tle
protected void performAction(Action action) {
  switch (action.getId());
  case ...
    CisBatchJob batch = ...
    CisParameterList params = getBatchApplicationParameters();
    batch.setParameters(params);
    :
:
}
Zapytanie ResultSet w aplikacji działającej w tle
public CisParameterList run(int action, CisParameterList parms) {
  CisBatchJobSearchStatement stmt =
      om.getBatchJobSearchStatement(parms);
  CisResultSet rs = stmt.execute();
  int position = stmt.getResultPosition("bookGuid");
  try {
    while (rs.next()) {
      byte[] guid = rs.getGuid(position);
        :
    }
  } catch (SQLException ex) {
    throw new RuntimeException(ex);
  } finally {
    rs.close();
  }
:
}

AbstractOrderCockpit

Aplikacje typu Lista dla dokumentów (np. zamówień, zapytań, faktur itp.) powinny używać tej klasy jako podstawy:
com.cisag.app.general.cockpit.ui.AbstractOrderCockpit
Klasa ta zapewnia specjalne wsparcie dla następujących funkcji:
  • Baza i pozycje
  • Łańcuch dokumentów powiązanych
  • Uprawnienia (uprawnienia do treści)
  • Znacznik usuwania
  • SearchContext
  • Wyszukiwanie szczegółowe
Przykładami użycia tej klasy są implementacje dla następujących aplikacji typu Lista:
  • com.cisag.app.sales.cockpit.ui.CustomerProposalCockpitBase
  • com.cisag.app.sales.cockpit.ui.CustomerProposalCockpitDetail
  • com.cisag.app.purchasing.cockpit.ui.RequestForProposalCockpitBase

SearchView

Klasa com.cisag.pgm.search.gui.SearchView może być używana do integracji funkcjonalności aplikacji typu Lista jako komponentu w innych aplikacjach. Interfejs i użycie tej klasy są porównywalne do klasy CisCustomisableCockpit z następującymi różnicami i rozszerzeniami:
  • Klasa reprezentuje konkretną klasę, która może być również używana bezpośrednio, tj. bez wyprowadzania oddzielnej klasy. Derywacja jest konieczna tylko wtedy, gdy metody takie jak preSearch() mają zostać zmienione.
  • Link do powiązanego obiektu deweloperskiego Search jest tworzony przez wywołanie tej metody: init(String searchName)
  • Do przypisania tytułu do komponentu można użyć następujących metod: setTitle(String) i setTitleFrom(…)
  • Należy użyć następującej metody, aby określić, czy komponent zapewnia zakres zapytań: setAllowFilter(boolean)
  • Szczegółowe wyszukiwania nie są oferowane
  • Następująca metoda może być użyta do kontrolowania, gdzie przechowywane są widoki (układy) zdefiniowane dla tego komponentu: should.setStorageLocation(StorageLocation)
  • Te wartości są dostępne do wyboru:
    • StorageLocation.SEARCH — jest wartością domyślną i oznacza, że widoki są zapisywane w odniesieniu do obiektu deweloperskiego Search. W tym przypadku zapisane widoki są współdzielone przez wszystkie aplikacje typu Lista i SearchViews, które odnoszą się do tego samego obiektu deweloperskiego Search.
    • StorageLocation.APPLICATION. — SearchViews, które są używane jako edytor w aplikacji z konfigurowalnym interfejsem użytkownika, mogą alternatywnie używać wartości StorageLocation.APPLICATION. W takim przypadku widoki są przechowywane jako część odpowiednich widoków aplikacji i są niezależne od innych zastosowań obiektu deweloperskiego Search.

GridSearchSupport

Interfejs com.cisag.pgm.search.gui.GridSearchSupport umożliwia konfigurację wyszukiwania i odpytywanie wyników wyszukiwania i wyboru.
Konfiguracja
 
Metoda Objaśnienie
getSearch() Wyszukuje w pełni kwalifikowaną nazwę bieżącego wyszukiwania głównego.
setSearch(String) Umożliwia ponowną inicjalizację z nowym wyszukiwaniem.
addDetailSearch(…) Dodaje kolejne wyszukiwanie szczegółowe (patrz rozdział Wyszukiwanie szczegółowe).
addHeaderElement(VisualElement) Dodaje VisualElement do paska narzędzi nad listą wyników.
getShowFilterPanel()
setShowFilterPanel()
Określa, czy wyświetlany jest nagłówek
isMultipleRowSelectionEnabled()
setMultipleRowSelectionEnabled()
Określa, czy dozwolony jest wielokrotny wybór. Ustawienie dotyczy tylko wyszukiwania głównego. Jeśli wybór wielokrotny jest włączony, na początku każdego wiersza wyświetlane jest pole wyboru, którego użytkownik może użyć do wybrania poszczególnych wierszy.
getAllowEditFilterExpression()
setAllowEditFilterExpression()
Określa, czy użytkownicy mogą zmieniać wyrażenie filtru dla określonej kolumny.
getDoubleClickAction()
setDoubleClickAction()
Umożliwia rejestrację akcji, która jest wykonywana po dwukrotnym kliknięciu wiersza.
getRendererFactory()
setRendererFactory()
Umożliwia zmianę RendererFactory określonego za pomocą logicznego typu danych dla określonej kolumny.
getFilterExpressionEditorFactory()
setFilterExpressionEditorFactory()
Umożliwia zmianę FilterExpressionEditorFactory określonego za pomocą logicznego typu danych dla określonej kolumny.
getSearchContextProvider()
setSearchContextProvider()

Umożliwia zmianę SearchContextProvider dla wyszukiwania.

Uwaga
SearchContextProvider jest zwykle dostarczany przez aplikację (poprzez implementację tego interfejsu).
 
 
Filtrowanie
Metoda Objaśnienie
getFilterExpression()
setFilterExpression()
Umożliwia odczyt lub ustawienie wyrażeń filtrujących dla określonej kolumny.
getFilterExtension()
setFilterExtension()
Umożliwia odczyt lub ustawienie specjalnych rozszerzeń filtrów dla wyszukiwania głównego lub określonego wyszukiwania szczegółowego (patrz rozdział FilterExtension).
resetFilter()
Resetuje wszystkie wyrażenia filtra do wartości domyślnych.
 
Uwaga
Wartości domyślne są definiowane poprzez widok dla obszaru filtra. Ponadto metoda hook o tej samej nazwie jest wywoływana w klasie CisCustomisableCockpit, która w razie potrzeby może wprowadzić dalsze zmiany w wyrażeniach filtra. Więcej informacji można znaleźć w rozdziale CisCustomisableCockpit.
 
validateFilter()

Sprawdza wszystkie wyrażenia filtru pod kątem poprawności składniowej.

Uwaga
Wartości domyślne są definiowane poprzez widok dla obszaru filtra. Ponadto metoda hook o tej samej nazwie jest wywoływana w klasie CisCustomisableCockpit, która w razie potrzeby może wprowadzić dalsze zmiany w wyrażeniach filtra. Więcej informacji można znaleźć w rozdziale CisCustomisableCockpit.

sendMessage(CisMessage, String…) Umożliwia wysyłanie wiadomości dla jednej lub więcej kolumn. Tej metody należy użyć, jeśli mają być wysyłane komunikaty z niestandardowej implementacji funkcji validateFilter().
 
 
Wynik wyszukiwania
Metoda Objaśnienie
clearResult() Usuwa listę wyników.
getRowCount() Zwraca liczbę znalezionych obiektów (tylko wyszukiwanie główne).
getBatchApplicationParameters()
Zwraca obiekty, które muszą zostać przekazane do przetwarzania w tle.
 
Uwaga
Klasa CisCustomisableCockpit wywołuje tę metodę wewnętrznie i zwraca ten sam wynik. Zobacz ten rozdział: CisCustomisableCockpit.
 
Wybór
Metoda Objaśnienie
clearSelection() Resetuje bieżący wybór.
selectAll()

Wybiera wszystkie wyświetlane obiekty głównego wyszukiwania.

Uwaga
Jeśli wielokrotny wybór nie jest dozwolony, metoda nie ma żadnego efektu.
 
getFocusedDetailSearch()  Zwraca nazwę (alias) wyszukiwania szczegółowego, które należy do aktualnie zogniskowanego wiersza. Metoda zwraca wartość null, jeśli fokus znajduje się w głównym wyszukiwaniu.
getFocusedRowData(String…)
Zwraca wartości określonych kolumn dla aktualnie zogniskowanego wiersza.
 
Uwaga
Określone kolumny muszą być zgodne z aktualnie skoncentrowanym wyszukiwaniem (patrz: getFocusedDetailSearch()) i być oznaczone jako Klucz zwrotu w obiekcie deweloperskim Search (patrz rozdział Pole wyboru: Klucz zwrotu).
 
 
getSelectedRowCount()

Zwraca liczbę aktualnie wybranych obiektów.

Uwaga
Wybór zawsze odnosi się do głównego wyszukiwania. Jeśli wielokrotny wybór nie jest dozwolony dla głównego wyszukiwania, wybór zawsze odpowiada aktualnie zogniskowanemu wierszowi.
getSelectedRowData(String…)

Zwraca wartości określonych kolumn dla aktualnie wybranych wierszy.

Uwaga
Wybór zawsze odnosi się do głównego wyszukiwania. Określone kolumny muszą być oznaczone jako Klucz zwrotu w obiekcie deweloperskim Search (patrz rozdział Pole wyboru: Klucz zwrotu).
 
 
Uwaga
Dla wszystkich metod klasy GridSearchSupport, które oczekują nazwy kolumny jako parametru, nazwa kolumny musi zostać rozszerzona o prefiks wyszukiwania szczegółowego, jeśli nazwa kolumny odnosi się do wyszukiwania szczegółowego. Przykład: detail:guid
Filtrowanie obiektów
Obiekty mogą być filtrowane zarówno przez użytkownika, jak i przez aplikację. Kolumny, które mają być używane wyłącznie przez aplikację do filtrowania, muszą być oznaczone jako Zaprogramowane filtry w wyszukiwaniu (patrz rozdział Pole wyboru: Zaprogramowany filtr).
Wartości domyślne
Jeśli to możliwe, wartości domyślne dla filtrów powinny być zawsze definiowane i zapisywane za pośrednictwem nagłówka, a nie programowane w aplikacji, ponieważ zaprogramowane wartości domyślne mogą kolidować z nagłówkami i wzorcami zdefiniowanymi przez użytkownika. Aplikacja nie powinna zmieniać istniejących wartości domyślnych.
Przykład
@Overwrite
protected void resetFilter() {
  // Inicjalizacja z zapisanymi wartościami
  super.resetFilter();
  GridSearchSupport gss = getGridSearchSupport();
  // Nie zmieniaj już zdefiniowanych wartości
  if (gss.getFilterExpression("partnerNumber") == null) {
    gss.setFilterExpression("partnerNumber",
        StringFilterExpressionList.create(
            "com.cisag.app.edu:CockpitEmployeeNumber.lt", "A*");
  }
}
Uwaga
Wartości domyślne powinny być zawsze ustawiane za pomocą metody resetFilter klasy CisCustomizableCockpit lub SearchView, aby użytkownicy mogli je rozpoznać jako takie i zmienić wartości domyślne (patrz metoda resetFilter()).
Zalecane jest również zapisanie nagłówka z obiektem deweloperskim Search, który zawiera odpowiednie wartości domyślne. Wartości domyślne powinny być użyteczne dla użytkownika i powodować jak najmniejsze wykorzystanie bazy danych.
Manipulowanie zapytaniem do bazy danych
Spełnienie pewnych wymagań wymaga ukierunkowanej manipulacji zapytaniem do bazy danych. Wymagania te obejmują np. egzekwowanie uprawnień lub wyszukiwanie wyrażenia w kilku kolumnach (Połączenie OR).
W prostych przypadkach takie wymagania można już rozwiązać za pomocą kolumn oznaczonych jako Zaprogramowane filtry (patrz rozdział Pole wyboru: Zaprogramowany filtr). Wyrażenie filtru można również zdefiniować w celu wdrożenia bardziej złożonych wymagań (patrz sekcja FilterExtension).
Podczas ustawiania zaprogramowanych filtrów lub FilterExtension ważne jest, kiedy dostarczany jest końcowy wynik zapytania do bazy danych. Aplikacje mają następujące możliwości manipulowania zapytaniem do bazy danych we właściwym czasie:
  • Zmiana metody preSearch() klasy CisCustomisableCockpit (lub SearchView)
  • Implementacja SearchHook
Implementacja SearchHook ma tę zaletę, że funkcjonalność w niej zlecona dotyczy wszystkich użytkowników obiektu deweloperskiego Search.
Wyszukiwanie szczegółowe
Dowolna liczba wyszukiwań szczegółowych może być powiązana z wyszukiwaniem głównym, a dowolna liczba innych wyszukiwań szczegółowych może być powiązana z każdym wyszukiwaniem szczegółowym. Ten typ połączenia może być kontynuowany na dowolnej liczbie poziomów.
W praktyce jednak całkowita liczba szczegółowych wyszukiwań nie powinna przekraczać pięciu, ponieważ każde szczegółowe wyszukiwanie wymaga dodatkowego miejsca w nagłówku i obszarze roboczym aplikacji typu Lista. Co więcej, każde wyszukiwanie szczegółowe dodatkowo obciąża bazę danych, ponieważ w przypadku wyszukiwania szczegółowego należy wykonać dodatkowe zapytanie do bazy danych dla każdego obiektu w wyszukiwaniu wyższego poziomu. Wyszukiwanie główne z tylko jednym wyszukiwaniem szczegółowym wymaga zatem już 1+N zapytań do bazy danych, gdzie N to liczba znalezionych obiektów. Wyszukiwanie główne z M równoległymi wyszukiwaniami szczegółowymi wymaga zatem 1+NM zapytań do bazy danych. Liczba zapytań do bazy danych wzrasta jeszcze bardziej w przypadku zagnieżdżonych wyszukiwań szczegółowych. Należy zatem unikać stosowania więcej niż jednego poziomu szczegółowości.
Zapytanie do bazy danych i zestaw kolumn wyszukiwania szczegółowego są definiowane za pomocą oddzielnych obiektów deweloperskich typu Search. Połączenie między wyszukiwaniem głównym a wyszukiwaniem szczegółowym jest tworzone za pomocą metody addDetailSearch() klasy GridSearchSupport:
 
GridSearchSupport.addDetailSearch(
 String parent, // tylko dla wyszukiwania szczegółowego
 String alias,
 String searchName,
 Action action,
 String[] parentColumnNames,
 String[] detailColumnNames)
 
Parametr Objaśnienie
parent Alias wyszukiwania parent musi być zawsze określony jako nadrzędny. Jeśli wyszukiwanie szczegółowe jest połączone bezpośrednio z wyszukiwaniem głównym, należy użyć wartości null dla parent.
alias Unikalny alias musi być przypisany do każdego dodanego wyszukiwania szczegółowego.
searchName W pełni kwalifikowana nazwa powiązanego obiektu deweloperskiego Search musi być wprowadzona jako searchName.
action Jako akcję należy przekazać instancję com.cisag.pgm.gui.Action, która zapewnia tekst dla przycisku do pokazywania i ukrywania wyszukiwania szczegółowego.
parentColumnNames Jako parentColumnNames należy przekazać tablicę z nazwami kolumn z wyszukiwania nadrzędnego, za pośrednictwem których ma zostać utworzone łącze do wyszukiwania szczegółowego. Określone kolumny muszą być oznaczone jako klucz zwrotu w wyszukiwaniu nadrzędnym (patrz rozdział Pole wyboru: Klucz zwrotu).
detailColumnNames
Tablica z nazwami kolumn z wyszukiwania szczegółowego ma zostać przekazana jako detailColumnNames, za pośrednictwem której ma nastąpić połączenie z wyszukiwaniem nadrzędnym. Określone kolumny muszą być oznaczone jako klucz zwrotu w wyszukiwaniu szczegółowym (patrz rozdział Pole wyboru: Klucz zwrotu). Ponadto, określone kolumny muszą być oznaczone jako możliwe do filtrowania w wyszukiwaniu szczegółowym (patrz ten rozdział: Pole wyboru: Możliwość filtrowania), a także powinny być oznaczone jako Zaprogramowane filtry (patrz rozdział Pole wyboru: Zaprogramowany filtr)
Liczba i typy danych określonych kolumn muszą być zgodne z liczbą i typami danych kolumn określonych w parentColumnNames.
 
 
Przykład
Poniższy przykład przedstawia wyszukiwanie główne, które jest powiązane z wyszukiwaniem szczegółowym. To szczegółowe wyszukiwanie jest następnie powiązane z innym szczegółowym wyszukiwaniem.
 
Wyszukiwanie główne (com.example.search.Main):
 
Nazwa Wyświetlanie Zaprogramowany filtr Możliwość filtrowania Klucz zwrotu
guid M:guid     X
code M:name      
description M:description      
 
Wyszukiwanie szczegółowe 1 (com.example.search.Detail):
 
Nazwa Wyświetlanie Zaprogramowany filtr Możliwość filtrowania Klucz zwrotu
guid D:guid     X
parent D:parent X X X
code D:name      
description D:description      
 
Wyszukiwanie szczegółowe 2 (com.example.search.Subdetail):
 
Nazwa Wyświetlanie Zaprogramowany filtr Możliwość filtrowania Klucz zwrotu
guid S:guid     X
parent S:parent X X X
code S:name      
description S:description      
 
Kod:
Action a1 = new Action(0, "com.example.search.DetailAction1");
Action a2 = new Action(0, "com.example.search.DetailAction2");
String[] parentColumnNames = {"guid"};
String[] detailColumnNames = {"parent"};
GridSearchSupport gss = getGridSearchSupport();
gss.addDetailSearch(null, "D1",
 "com.example.search.Detail", a1,
 parentColumnNames, detailColumnNames);
gss.addDetailSearch("D1", "D2",
 "com.example.search.Subdetail", a2,
 parentColumnNames, detailColumnNames); 

Format

Klasa abstrakcyjna java.text.Format jest używana do konwersji obiektów na (ewentualnie specyficzną dla języka i regionu) reprezentację tekstową (formatowanie) oraz do konwersji takich tekstów z powrotem na obiekty (parsowanie). Konkretne podklasy muszą implementować co najmniej dwie poniższe metody:
public StringBuffer format(Object obj, StringBuffer toAppendTo,
                           FieldPosition pos);
public Object parseObject(String source, ParsePosition pos);
Infrastruktura aplikacji typu Lista wykorzystuje podklasy klasy Format do wizualizacji danych w obszarze wyświetlania, do eksportu (PDF/MS Excel), a także dla niektórych FilterExpressionEditors. Klasa FormatFactory jest odpowiedzialna za tworzenie instancji formatu, których implementacja musi być zarejestrowana w opisie danych odpowiedniej kolumny (patrz sekcja FormatFactory).
Niemniej jednak, implementacje oparte na formatach nie są generalnie używane dla wszystkich typów danych, ale tylko wtedy, gdy reprezentacja tekstowa jest użyteczna i pożądana dla danego typu danych. W przeciwnym razie używane są renderery lub bardziej wyspecjalizowane edytory FilterExpressionEditors. Factory przechowywane dla logicznego typu danych decydują, która implementacja jest używana dla kolumny.
FormatFactory
Instancje klasy Format są tworzone przez infrastrukturę aplikację typu Lista przy użyciu specjalnych Klas Factory. Klasy Factory muszą implementować interfejs com.cisag.pgm.gui.FormatFactory i posiadać domyślny konstruktor. Interfejs definiuje następującą metodę:
public Format createFormat(DataDescription dataDescription);

Konkretne klasy implementujące ten interfejs powinny wykorzystywać informacje z przekazanego Data description do parametryzacji tworzonej instancji klas Format.

FormatFactory jest rejestrowany poprzez wprowadzenie jego w pełni kwalifikowanej nazwy klasy w opisie danych dla logicznego typu danych.
 
Uwaga
Nie tylko FormatFactory, ale także RendererFactory może być zarejestrowany dla logicznego typu danych (lub Data description) (patrz rozdział RendererFactory). Jeśli zarówno FormatFactory, jak i RendererFactory zostały zarejestrowane dla logicznego typu danych, FormatFactory jest ignorowany. Dotyczy to również sytuacji, gdy fabryki zostały zarejestrowane na różnych poziomach hierarchii.
FormatFactoryRegistry
Klasa com.cisag.pgm.gui.FormatFactoryRegistry może być używana do tworzenia odpowiednich instancji Format (patrz rozdział Format) lub FormatFactory (patrz rozdział FormatFactory) dla logicznego typu danych. Logiczny typ danych można określić za pomocą w pełni kwalifikowanej ścieżki lub identyfikatora GUID. Podczas rozwiązywania klasa przeszukuje łańcuch dziedziczenia logicznego typu danych aż do głównego typu danych. Pierwszy znaleziony FormatFactory jest zwracany i używany do utworzenia instancji formatu. Jeśli nie ma rejestru dla logicznego typu danych, zwracane jest zero.
FormatFactory dla prawie wszystkich prymitywnych typów danych oraz dla wszystkich wartości czasu i daty są zarejestrowane w FormatFactoryRegistry.
FormatFactoryRegistry jest używany w szczególności przez samą infrastrukturę aplikacji typu Lista. Można go również wykorzystać np. do ponownego użycia istniejącej implementacji w oddzielnej FormatFactory (delegacja).
Kolumny wirtualne
W przypadku kolumn wirtualnych (patrz rozdział Pole wyboru: Wirtualnie), wartości atrybutów są przekazywane do formatyzatora jako java.util.Map, przy czym względne nazwy podatrybutów są używane jako klucze.
 
Przykład
Wirtualna kolumna o nazwie vattr ma dwa podatrybuty vattr.attr1 i vattr.attr2, a wszystkie atrybuty są oznaczone jako możliwe do wyświetlenia. W formatterze wartości tych atrybutów podrzędnych powinny być dostępne w następujący sposób:
 
public public StringBuffer format(Object value,
    StringBuffer toAppendTo, FieldPosition pos) {
  if (value instanceof Map) {
    Map values = (Map) value;
    Object attr1 = values.get("attr1");
    Object attr2 = values.get("attr2");
    ...
  }
  return toAppendTo;
}
 
Uwaga
Map zawiera tylko podatrybuty oznaczone jako możliwe do wyświetlenia. Wartości atrybutów mogą również wynosić null.

Renderer

Jeśli wizualizacja jako tekst nie jest wystarczająca lub klasa Format nie może być użyta z innych powodów, można zaimplementować specjalny Renderer. W przeciwieństwie do klasy Format, renderery mogą być używane do tworzenia nie tylko reprezentacji tekstowych, ale także ikon, podpowiedzi, linków (URI) i instancji klasy VisualElement. Każdy z tych typów wizualizacji opiera się na własnym interfejsie. Konkretne implementacje renderera mogą również łączyć te interfejsy, jeśli jest to wymagane (np. IconRenderer w połączeniu z ToolTipRenderer).
Klasa RendererFactory (patrz rozdział: RendererFactory) jest odpowiedzialna za tworzenie instancji renderera, których implementacja musi być zarejestrowana w opisie danych odpowiedniej kolumny.
RendererFactory
Instancje rendererów są zawsze tworzone przez infrastrukturę aplikacji typu Lista za pośrednictwem specjalnych Klas Factory. Klasy Factory muszą implementować interfejs com.cisag.pgm.gui.RendererFactory i posiadać domyślny konstruktor. Interfejs definiuje następującą metodę:
public Renderer createRenderer(byte[] guid,
    DataDescription dataDescription)

Konkretne klasy implementujące ten interfejs powinny wykorzystywać informacje z przekazanego Data description do parametryzacji tworzonej instancji Renderera.

RendererFactory jest rejestrowany poprzez wprowadzenie jego w pełni kwalifikowanej nazwy klasy w opisie danych dla logicznego typu danych.
 
Uwaga
Dla logicznego typu danych (lub Data description) można zarejestrować nie tylko RenderFactory, ale także FormatFactory (patrz rozdział FormatFactory). Jeśli zarówno RendererFactory, jak i FormatFactory zostały zarejestrowane dla logicznego typu danych, FormatFactory jest ignorowany. Dotyczy to również sytuacji, gdy Factory zostały zarejestrowane na różnych poziomach hierarchii.
RendererFactoryRegistry
Klasa com.cisag.pgm.gui.RendererFactoryRegistry może być używana do tworzenia odpowiednich instancji RendererFactory dla logicznego typu danych. Podczas rozwiązywania, klasa przeszukuje łańcuch dziedziczenia logicznego typu danych aż do głównego typu danych. Zwracany jest pierwszy znaleziony RendererFactory. Jeśli nie ma rejestracji dla logicznego typu danych, zwracane jest zero.
Specjalne RendererFactory dla podstawowego typu danych Boolean, znacznika usuwania (com.cisag.pgm.datatype.DeleteState) i User-GUID są zarejestrowane w RendererFactoryRegistry.
RendererFactoryRegistry jest używany w szczególności przez samą infrastrukturę aplikacji typu Lista. Może być również używany, np. do ponownego wykorzystania istniejącej implementacji w oddzielnej RendererFactory (delegacja).
TextRenderer
Podobnie jak klasa Format, interfejs com.cisag.pgm.gui.Renderer.TextRenderer umożliwia konwersję obiektu na reprezentację tekstową. Jeśli wystarczająca jest czysta reprezentacja tekstowa, można również zaimplementować odpowiednią klasę formatu zamiast TextRenderer.
Konkretne klasy muszą implementować następującą metodę:
public String getText(Object value);
Przykład
public class SimpleTextRendererFactory
    implementuje RendererFactory {
  public Renderer createRenderer(byte[] guid,
      DataDescription dataDescription) {
    return new SimpleTextRenderer();
  }
  private class SimpleTextRenderer
      implementuje Renderer.TextRenderer {
    public String getText(Object value) {
      if (value != null) {
        return String.valueOf(value);
      }
      return null;
    }
  }
}

IconRenderer
Interfejs com.cisag.pgm.gui.Renderer.IconRenderer umożliwia konwersję obiektu na instancję com.cisag.pgm.gui.Icon.
Konkretne klasy muszą implementować następującą metodę:
public Icon getIcon(Object value);

Przykład

public class DocumentKindRendererFactory
    implementuje RendererFactory {
  private final Icon iconDocument = Icon.getIcon(...);
  private final Icon iconNote = Icon.getIcon(...);
  public Renderer createRenderer(byte[] guid,
      DataDescription dataDescription) {
    return new DocumentKindRenderer();
  }
  private class DocumentKindRenderer
      implementuje Renderer.IconRenderer {
    public Icon getIcon(Object value) {
      if (value != null) {
        switch ((Short) value) {
        case DocumentKinds.DOCUMENT:
          return iconDocument;
        case DocumentKinds.NOTE:
          return iconNote;
        }
      }
      return null;
    }
  }
}

 
Uwaga
Reprezentacja tekstowa jest również wymagana dla każdej kolumny w celu wyświetlenia nagłówków grup i eksportu. IconRenderer powinien być zatem połączony z ToolTipRenderer (patrz rozdział ToolTipRenderer).
LayeredIconRenderer
Interfejs com.cisag.pgm.gui.Renderer.LayeredIconRenderer umożliwia przekształcenie obiektu w tablicę instancji com.cisag.pgm.gui.Icon, które są nakładane jedna na drugą podczas wizualizacji. Podczas korzystania z częściowo przezroczystych ikon, np. różne częściowe ikony stanu mogą być łączone w celu utworzenia ogólnej ikony stanu.
Konkretne klasy muszą implementować następującą metodę:
public Icon[] getIcons(Object value);
Wskazówka
Reprezentacja tekstowa jest również wymagana dla każdej kolumny do wyświetlania nagłówków grup i do eksportu. W związku z tym, LayerdIconRenderer powinien być połączony z ToolTipRenderer (patrz rozdział ToolTipRenderer).
Uwaga
Ładowanie i renderowanie wielu ikon może pogorszyć wydajność przeglądarki.
ToolTipRenderer
Interfejs com.cisag.pgm.gui.Renderer.ToolTipRenderer umożliwia konwersję obiektu na tekst wyświetlany jako podpowiedź w aplikacji typu Lista.
Konkretne klasy muszą implementować następującą metodę:
public Icon[] getIcons(Object value);
URIRenderer
Interfejs com.cisag.pgm.gui.Renderer.URIRenderer umożliwia konwersję obiektu na URI. Te URI są następnie wyświetlane jako linki znajdujące się w aplikacji typu Lista. W przypadku identyfikatorów URI, które identyfikują jednostkę biznesową, aplikacja zapewnia również zwykłe menu kontekstowe dla tej jednostki.
Konkretne klasy muszą implementować następującą metodę:
public URI getUri(Object value);
Przykład
public URI getUri(Object value) {
  if (value != null) {
    CisObject obj = ... // przekonwertuj parametr "value" na CisObject
    BusinessEntityLink link = LinkUtili-ty.createBusinessEntityLink(
        LinkUtility.DEFAULT_FLAGS, env.getDatabaseGuid(), o);
    if (link != null) {
      return URI.create(LinkUtility.toUrlString(link));
    }
  }
  return null;
}

VisualElementRenderer
Interfejs com.cisag.pgm.gui.Renderer.VisualElementRenderer umożliwia konwersję obiektu na instancję om.cisag.pgm.gui.VisualElement.
Konkretne klasy muszą implementować następującą metodę:
public VisualElement getVisualElement(Object value);
Uwaga
Korzystanie z VisualElementRenderers wiąże się z wieloma wadami. Główną wadą jest większe zużycie zasobów, zarówno na serwerze, jak i w przeglądarce. Ponadto eksport danych (PDF, Excel, REST) nie jest oferowany. VisualElementRenderer nie może być łączony z innymi rendererami. VisualElementRenderers nie mogą być używane do interakcji, tj. linki, menu kontekstowe itp. nie działają.
Kolumny wirtualne
W przypadku kolumn wirtualnych (patrz rozdział Pole wyboru: Wirtualnie), wartości atrybutów są przesyłane do renderera jako java.util.Map, przy czym względne nazwy podatrybutów są używane jako klucze.
Przykład
Wirtualna kolumna o nazwie vattr ma dwa podatrybuty vattr.attr1 i vattr.attr2, a wszystkie atrybuty są oznaczone jako możliwe do wyświetlenia. W formatterze wartości tych atrybutów podrzędnych powinny być dostępne w następujący sposób:
 
public String getText(Object value) {
  if (value instanceof Map) {
    Map values = (Map) value;
    Object attr1 = values.get("attr1");
    Object attr2 = values.get("attr2");
    ...
    return ...
  }
  return null;
}
 
 
Uwaga
Map zawiera tylko podatrybuty oznaczone jako możliwe do wyświetlenia. Wartościami atrybutów mogą być również wartości null.

FilterExpression

Wszystkie wyrażenia filtrujące są oparte na abstrakcyjnej i ogólnej klasie bazowej com.cisag.pgm.search.filter.FilterExpression<T>. Pakiet com.cisag.pgm.search.filter zapewnia konkretne podklasy dla wszystkich odpowiednich logicznych typów danych. Każda z tych podklas ma odpowiednie statyczne metody Factory, które mogą być używane do tworzenia instancji.
 
Uwaga
Podczas tworzenia wyrażeń filtrujących należy zawsze określić logiczny typ danych, przy czym określony typ danych musi być zgodny z typem danych kolumny, do której odnosi się wyrażenie filtrujące.
FilterExpressionList
Klasa generyczna com.cisag.pgm.search.filter.FilterExpressionList<T> służy jako wspólna klasa bazowa dla prawie wszystkich typów danych, których wyrażenia filtrujące mogą składać się z pojedynczych wyrażeń oddzielonych przecinkami. Niemniej jednak, zamiast oddzielonej przecinkami listy ciągów znaków, klasa opiera się na java.util.List z elementami typu Expression<T>. Typ Expression<T> jest również ogólnym wewnętrznym interfejsem klasy FilterExpressionList. Klasy SingleExpression<T>, RangeExpression<T> i InvalidExpression<T>, które są generycznymi klasami wewnętrznymi FilterExpressionList, są dostępne jako konkretne implementacje tych interfejsów.
SingleExpression
Klasa SingleExpression służy do reprezentowania wyrażeń filtrujących, które składają się z operatora porównania i wartości. Możliwe operatory porównania są zdefiniowane przez typ ExpressionType:
  • EQUALS
  • UNEQUAL (<>)
  • LESS (<)
  • GREATER (>)
  •  LESS_EQUAL (<=)
  • GREATHER_EQUAL (>=)
 
Uwaga
Niektóre z wymienionych operatorów porównania nie są dozwolone dla wszystkich typów danych.
RangeExpression
Klasa RangeExpression służy do reprezentowania zakresów wartości. Zakres wartości jest definiowany przez dolną i górną wartość, przy czym jednej z tych wartości może brakować (przedział otwarty). Dolna wartość (jeśli występuje) jest zawsze łączna, górna wartość (jeśli występuje) może być łączna lub wyłączna (w zależności od odpowiedniego typu danych).
InvalidExpression
Nieprawidłowe wyrażenia filtra są reprezentowane przez klasę InvalidExpression. Zawiera ona nieprawidłową wartość jako ciąg znaków i (opcjonalnie) listę komunikatów (CisMessage). Takie instancje są zwykle tworzone, jeśli dane wejściowe użytkownika lub przesłane ciągi znaków nie mogą zostać przetworzone na prawidłowe wyrażenie filtrujące.
StringFilterExpressionList
Klasa com.cisag.pgm.search.filter.StringFilterExpressionList jest konkretną podklasą FilterExpressionList i opisuje wyrażenia filtrujące dla kolumn, których logiczne typy danych są oparte na łańcuchach lub tekstach. Klasa ma różne statyczne metody fabryczne, które mogą być używane do tworzenia instancji tej klasy.
ByteFilterExpressionList, ShortFilterExpressionList, IntegerFilterExpressionList, LongFilterExpressionList i DecimalFilterExpressionList
Następujące klasy z pakietu com.cisag.pgm.search.filter są konkretnymi podklasami FilterExpressionList i opisują wyrażenia filtrujące dla kolumn, których logiczne typy danych są oparte na bajtach, szortach, intach, longach lub CisDecimal:
  • ByteFilterExpressionList
  • ShortFilterExpressionList
  • IntegerFilterExpressionList
  • LongFilterExpressionList
  • DecimalFilterExpressionList
Każda z powyższych klas posiada różne statyczne metody fabryczne, które mogą być używane do tworzenia instancji tych klas.
Przykład

Wyszukiwanie dokładnej wartości:
FilterExpression expr =
      IntegerFilterExpressionList.create(path, 1);
Wyszukiwanie wielu dokładnych wartości:
FilterExpression expr =
      IntegerFilterExpressionList.create(path, 1, 3, 5);
Wyszukiwanie zakresu:
FilterExpression expr =
      IntegerFilterExpressionList.createRange(path, 1, 9);
Wyszukiwanie zakresu bez górnego limitu:
FilterExpression expr =
      IntegerFilterExpressionList.createRange(path, 1, null);
Wyszukiwanie wartości Nie równych 0:
FilterExpression expr = IntegerFilterExpressionList.create(path,
 IntegerFilterExpressionList.createSingleExpression(
 ExpressionType.UNEQUAL, 0

 
Uwaga
Klasa ValueSetFilterExpression jest dostępna dla zestawów wartości, które są również mapowane jako krótkie w bazie danych (patrz rozdział ValueSetFilterExpression).
Float i double nie są oferowane.
DateAndTimeFilterExpressionList
Klasa com.cisag.pgm.search.filter.DateAndTimeFilterExpressionList jest konkretną podklasą FilterExpressionList i opisuje wyrażenia filtrujące dla kolumn, których logiczne typy danych są oparte na wartościach daty lub punktach w czasie.
Klasa DateAndTimeFilterExpressionList ma różne statyczne metody fabryczne, które mogą być używane do tworzenia instancji tej klasy. Wartości czasu i daty mogą być określone jako znormalizowana reprezentacja łańcuchowa lub przy użyciu instancji com.cisag.pgm.search.filter.DateAndTimeValue. W obu przypadkach można używać zarówno konkretnych, jak i symbolicznych wartości (np. dzisiaj). Wszystkie specyfikacje daty i czasu są zawsze powiązane z CisCalendar, który może być przekazywany jawnie w metodach fabrycznych, jeśli jest to wymagane. Bez wyraźnej specyfikacji, CisCalendar jest określany wewnętrznie poprzez:
CisEnvironment.getInstance().getContext().getCisCalendar()
QuantityFilterExpressionList, DurationFilterExpressionList
Klasy com.cisag.pgm.search.filter.QuantityFilterExpressionList i com.cisag.pgm.search.filter.DurationFilterExpressionList są konkretnymi podklasami FilterExpressionList i opisują wyrażenia filtrujące dla kolumn, których logiczne typy danych są oparte na wartościach ilościowych lub przedziałach czasowych.
Każda z klas posiada statyczną metodę Factory, która może być użyta do tworzenia instancji tych klas. Metoda Factory oczekuje jako parametr listy Expression<CisDecimal> i identyfikatora GUID ilości lub jednostki czasu. Niezbędne listy Expression<CisDecimal> mogą być generowane przy użyciu różnych statycznych metod Factory.
DomesticAmountFilterExpressionList, ForeignAmountFilterExpressionList
Klasy com.cisag.pgm.search.filter.DomesticAmountFilterExpressionList i com.cisag.pgm.search.filter.ForeignAmountFilterExpressionList są konkretnymi podklasami FilterExpressionList i opisują wyrażenia filtrujące dla kolumn, których logiczne typy danych są oparte na kwotach w walucie krajowej lub obcej.
Każda z klas posiada statyczną metodę fabryczną, która może być użyta do tworzenia instancji tych klas. Metoda Factory oczekuje listy Expression<CisDecimal> i identyfikatora GUID jednostki waluty jako parametrów. Metoda Factory dla DomesticAmountFilterExpressionList również wymaga identyfikatora GUID jednostki organizacyjnej. Niezbędne listy Expression<CisDecimal> mogą być generowane za pomocą różnych statycznych metod Factory.
BinaryFilterExpressionList, GuidFilterExpressionList
Klasy com.cisag.pgm.search.filter.BinaryFilterExpressionList i com.cisag.pgm.search.filter.GuidFilterExpressionList są konkretnymi podklasami FilterExpressionList i opisują wyrażenia filtrujące dla kolumn, których logiczne typy danych są oparte na danych binarnych lub identyfikatorach GUID. Każda z klas ma różne statyczne metody Factory, które mogą być używane do tworzenia instancji tych klas.
BooleanFilterExpressionList
Klasa com.cisag.pgm.search.filter.BooleanFilterExpressionList jest konkretną podklasą FilterExpressionList i opisuje wyrażenia filtrujące dla kolumn, których logiczny typ danych jest oparty na liczbach logicznych. Klasa posiada statyczną metodę Factory, która może być używana do tworzenia instancji tej klasy.
CompoundFilterExpression
Klasa com.cisag.pgm.search.filter.CompoundFilterExpression jest wymagana do filtrowania kolumn wirtualnych (patrz rozdział Pole wyboru: Wirtualnie). W przypadku kolumn wirtualnych filtrowanie zawsze odbywa się na podstawie atrybutów podrzędnych oznaczonych jako możliwe do filtrowania. Dla każdego podatrybutu, który ma być filtrowany, należy utworzyć FilterExpression pasujące do typu danych podatrybutu. Za pomocą klasy CompoundFilterExpression poszczególne FilterExpression są łączone w jedno FilterExpression. Aby to zrobić, poszczególne FilterExpression muszą najpierw zostać podsumowane w java.util.Map, przy czym względne nazwy podatrybutów mają być używane jako klucze.
 
Przykład
 
FilterExpression attr1 = ....
FilterExpression attr2 = ...
Map<String, FilterExpression> map =
    new HashMap<String, FilterExpression>();
map.put("attr1", attr1);
map.put("
FilterExpression attr1 = …
FilterExpression attr2 = …
Map<String, FilterExpression> map =
 new HashMap<String, FilterExpression>();
map.put("attr1", attr1);
map.put("attr2", attr2);
String path = …
FilterExpression vattr =
 CompoundFilterExpression.create(path, map);
 
DeleteStateFilterExpression
Klasa com.cisag.pgm.search.filter.DeleteStateFilterExpression służy do filtrowania obiektów ze znacznikami usuwania (patrz rozdział Znaczniki usuwania). Obiekty ze znacznikiem usuwania można rozpoznać po wartości atrybutu updateInfo.deleteTime. Wszystkie obiekty, dla których ten atrybut zawiera prawidłowy czas, są rozpoznawane jako oznaczone do usunięcia.
Ponieważ atrybut updateInfo.deleteTime zawiera znacznik czasu, wyrażenie filtrujące dla tego atrybutu powinno być oparte na klasie com.cisag.pgm.search.filter.DateAndTimeFilterExpressionList. Aby uprościć filtrowanie według znacznika usuwania, wprowadzono również klasę DeleteStateFilterExpression, która może być używana w podobny sposób jak ValueSetFilterExpression dla zestawów wartości.
Przykład
String path = "com.cisag.pgm.objsearch:DeleteState.lt";
FilterExpression expression =
    DeleteStateFilterExpression.create(path,
        DeleteStateFilterExpression.MARKED_FOR_DELETION);
 
ValueSetFilterExpression
Klasa com.cisag.pgm.search.filter.ValueSetFilterExpression jest specjalną podklasą FilterExpression do filtrowania zestawów wartości. Klasa posiada metodę Factory, która może być użyta do utworzenia instancji klasy poprzez wyliczenie krótkich wartości.
Wyrażenie filtrujące ze wszystkimi możliwymi wartościami jest równoważne wyszukiwaniu bez warunków, tj. użyciu wartości null jako wyrażenia filtrującego. Należy jednak zauważyć, że zestawy wartości, których wartości zostały zredukowane za pomocą Data description, są traktowane specjalnie: aby zapewnić, że tylko wartości określone w opisie danych są odczytywane przez bazę danych, te określone wartości są automatycznie uwzględniane w klauzuli WHERE zapytania do bazy danych. Jeśli jednak zostanie zapewnione, że tylko wartości określone w opisie danych mogą faktycznie wystąpić w danej kolumnie, wówczas dana kolumna powinna zostać oznaczona jako kompletna za pomocą hook BusinessObjectRegistryHook (patrz rozdział BusinessObjectRegistryHook). W tym przypadku pozwala to uniknąć niepotrzebnego i kosztownego rozszerzenia klauzuli WHERE.
Przykład
Przykład 1: Filtrowanie kolumny bookFormat dla wartości Format.CD lub Format.DVD
import com.cisag.app.edu.Format;
...
FilterExpression expr = ValueSetFilterExpression.create(
    "com.cisag.app.edu:Format.lt", Format.CD, Format.DVD);
gridSearchSupport.setFilterExpression("bookFormat", expr);
 
Przykład
Przykład 2: Wyszukiwanie bez warunku (Wszystkie)
gridSearchSupport.setFilterExpression("bookFormat", null);
 
Uwaga
Jeśli wartości ValueSet zostały ograniczone za pomocą Data description, wówczas wszystkie wartości określone w opisie danych są automatycznie uwzględniane w klauzuli WHERE zapytania do bazy danych.

FilterExpressionEditor

FilterExpressionEditor jest wymagany do wyświetlania lub edycji wyrażenia filtrującego w aplikacji typu Lista. FilterExpressionEditors to klasy, które implementują interfejs com.cisag.pgm.search.gui.FilterExpressionEditor<T> i są tworzone za pośrednictwem FilterExpressionEditorFactory (patrz rozdział FilterExpressionEditorFactory).
 
Metoda Objaśnienie

VisualElement

getVisualElement()

Zwraca VisualElement dostarczony przez edytor. Metoda ta jest wywoływana przez infrastrukturę aplikacji typu Lista, a dostarczony element jest następnie dodawany do nagłówka tego typu aplikacji.
void initVisualElement() Ta metoda może być użyta przez edytor do zainicjowania dostarczonego elementu. Infrastruktura aplikacji typu Lista wywołuje tę metodę bezpośrednio po dodaniu elementu dostarczonego przez edytor.
T getValue(); Zadaniem getValue() jest konwersja aktualnie wyświetlanej wartości dostarczonego elementu VisualElement na instancję FilterExpression, która odpowiada typowi danych powiązanej kolumny (patrz ten rozdział: FilterExpression).
 
Uwaga
Metoda musi zwrócić wartość null, jeśli w dostarczonym VisualElement nie wprowadzono żadnego filtra lub wybrano opcję Wszystkie.
setValue (T value);
Zadaniem metody setValue() jest wyświetlenie przekazanego FilterExpression w dostarczonym VisualElement (patrz ten rozdział: FilterExpression).
 
Uwaga
Wartość zero jest równoważna wartości Brak lub Wszystkie.
 
 
Uwaga
Implementacje metod getValue() i setValue() muszą być skoordynowane w taki sposób, aby:
1. wywołanie editor.getValue() po wywołaniu editor.setValue(X) zwracało FilterExpression równe X, o ile użytkownik nie zmienił w międzyczasie wyrażenia filtrującego,
2. wywołanie editor.setValue(X) z wyrażeniem filtrującym X, które zostało określone w pewnym momencie wcześniej za pomocą X = editor.getValue(), prowadzi do tej samej wizualizacji, co w momencie wywołania editor.getValue().
 
Uwaga
Poniższy przykład pokazuje implementację dla FilterExpressionEditor dla kolumn, które są oparte na (arbitralnym) zestawie wartości, a także implementację powiązanego FilterExpressionEditorFactory. Oddzielna implementacja dla zestawów wartości nie jest jednak wymagana, ponieważ odpowiednia implementacja jest już zapewniona przez infrastrukturę.
 
Przykład
package com.example;
import com.cisag.pgm.datatype.Guid;
import com.cisag.pgm.dialogue.model.DataDescription;
import com.cisag.pgm.dialogue.model.DefaultDataDescription;
import com.cisag.pgm.gui.ValueSetField;
import com.cisag.pgm.gui.VisualElement;
import com.cisag.pgm.search.filter.ValueSetFilterExpression;
import com.cisag.pgm.search.gui.FilterExpressionEditor;
import com.cisag.pgm.search.gui.FilterExpressionEditorFactory;
public class ValueSetFilterExpressionEditorFactory implements
  FilterExpressionEditorFactory {
  public ValueSetFilterExpressionEditorFactory() {
  }
  public FilterExpressionEditor createFilterExpressionEditor(
      byte[] guid, DataDescription dataDescription) {
    return new ValueSetFilterExpressionEditor(guid,
        dataDescription);
  }
  private class ValueSetFilterExpressionEditor implements
      FilterExpressionEditor<ValueSetFilterExpression> {
    private final String path;
    private final ValueSetField field;
    private ValueSetFilterExpressionEditor(byte[] guid,
        DataDescription dataDescription) {
      this.path =
          (String) dataDescription.getValue(DataDescription.PATH);
      field = new ValueSetField(Guid.toHexString(guid),
          dataDescription);
      field.setSelectionMode(true);
    }
    public VisualElement getVisualElement() {
      return field;
    }
    public void initVisualElement() {
    }
    public ValueSetFilterExpression getValue() {
      short[] values = (short[]) field.getSelection();
      if ( values != null && values.length > 0 ) {
        boolean allEntriesSelected = false;
        short[] entries = field.getEntries();
        if (entries != null && entries.length == values.length) {
          allEntriesSelected = true;
          for (int i = 0; i < values.length; i++) {
            boolean found = false;
            for (int j = 0; j < entries.length; j++) {
              if (values[i] == entries[j]) {
                found = true;
                break;
              }
            }
            if (!found) {
              allEntriesSelected = false;
              break;
            }
          }
        }
        if (!allEntriesSelected) {
          return ValueSetFilterExpression.create(path, values);
        }
      }
      return null;
    }
    public void setValue(ValueSetFilterExpression value) {
      if (value != null) {
        short[] values = value.getSelection();
        field.setSelection(values);
      } else {
        field.setSelection(new short[0]);
      }
    }
  }
}

FilterExpressionEditorFactory
Instancje FilterExpressionEditor są zawsze generowane przez infrastrukturę aplikacji typu Lista za pośrednictwem specjalnych klas Factory. Klasy Factory muszą implementować interfejs com.cisag.pgm.search.gui.FilterExpressionEditorFactory i mieć domyślny konstruktor. Interfejs definiuje następującą metodę:
public FilterExpressionEditor createFilterExpressionEditor(
    byte[] guid, DataDescription dataDescription)
Konkretne klasy, które implementują ten interfejs, powinny wykorzystywać informacje z przekazanego opisu danych, aby odpowiednio sparametryzować utworzoną instancję FilterExpressionEditor.
FilterExpressionEditorFactory jest rejestrowany poprzez wprowadzenie jego w pełni kwalifikowanej nazwy klasy w Data description dla logicznego typu danych.
 
Uwaga
Ogólne implementacje są dostarczane przez infrastrukturę aplikacji typu Lista dla wszystkich prymitywnych typów danych i Special-Parts. Odpowiednie FilterExpressionEditorFactories muszą być zapewnione dla wszystkich innych typów danych, tj. wirtualnych kolumn, obiektów biznesowych i Parts, jeśli odpowiednia kolumna została oznaczona jako możliwa do filtrowania w obiekcie deweloperskim Search (patrz sekcja Pole wyboru: Możliwość filtrowania) i jednocześnie nie ma to wpływu na zaprogramowany filtr (patrz sekcja Pole wyboru: Zaprogramowany filtr).
FilterExpressionEditorFactoryRegistry
Klasa com.cisag.pgm.search.gui.FilterExpressionEditorFactoryRegistry może być używana do tworzenia odpowiednich instancji FilterExpressionEditorfactory dla logicznego typu danych. Podczas rozwiązywania klasa przeszukuje łańcuch dziedziczenia logicznego typu danych aż do głównego typu danych. Zwracany jest pierwszy znaleziony FilterExpressionEditorFactory. Jeśli nie ma rejestracji dla logicznego typu danych, wyszukiwane jest null.
FilterExpressionEditorFactoryRegistry jest używany w szczególności przez samą infrastrukturę aplikacji typu Lista. Może być również używany, np. do ponownego wykorzystania istniejącej implementacji w oddzielnym FilterExpressionEditorFactory (delegacja).
Kolumny wirtualne
W przypadku kolumn wirtualnych (patrz sekcja Pole wyboru: Wirtualnie), wyrażenia filtrujące są zawsze wymieniane z FilterExpressionEditor za pośrednictwem instancji CompoundFilterExpression, przy czym względne nazwy podatrybutów są używane jako klucze (patrz sekcja CompoundFilterExpression).
Przykład
Wirtualna kolumna o nazwie vattr ma dwa podatrybuty vattr.attr1 i vattr.attr2. Podatrybut attr1 jest oparty na zestawie wartości com.example.ValueSet1, a podatrybut attr2 jest oparty na zestawie wartości com.example.ValueSet2. Ponadto oba podatrybuty są oznaczone jako filtrowalne w obiekcie deweloperskim Search. FilterExpressionEditor dla tej wirtualnej kolumny mógłby wyglądać następująco:
package com.example;
import com.cisag.pgm.datatype.Guid;
import com.cisag.pgm.dialogue.model.DataDescription;
import com.cisag.pgm.gui.MultiValueSetField;
import com.cisag.pgm.gui.VisualElement;
import com.cisag.pgm.search.filter.CompoundFilterExpression;
import com.cisag.pgm.search.filter.FilterExpression;
import com.cisag.pgm.search.filter.ValueSetFilterExpression;
import com.cisag.pgm.search.gui.FilterExpressionEditor;
import java.util.HashMap;
import java.util.Map;
public class VAttrFilterExpressionEditor
    implementuje FilterExpressionEditor<
        CompoundFilterExpression<FilterExpression<?>> {
  private final String path;
  private final MultiValueSetField field;
  public VAttrFilterExpressionEditor(byte[] guid,
      DataDescription dataDescription) {
    this.path = (String)
        dataDescription.getValue(DataDescription.PATH);
    this.field =
        new MultiValueSetField(Guid.toHexString(guid), path);
  }
  public VisualElement getVisualElement() {
    return field;
  }
  public void initVisualElement() {
    field.addValueSet("com.example:ValueSet1.lt");
    field.addValueSet("com.example:ValueSet2.lt");
  }
  public CompoundFilterExpression<FilterExpression<?>> getValue() {
    short[][] selection = field.getValue();
    if (selection != null && selection.length > 0) {
      Map<String, FilterExpression<?>> map =
          new HashMap<String, FilterExpression<?>>();
      if (selection[0] != null && selection[0].length > 0) {
        map.put("attr1", ValueSetFilterExpression.create(
            "com.example:ValueSet1.lt", selection[0]);
      }
      if (selection[1] != null && selection[1].length > 0) {
        map.put("attr2", ValueSetFilterExpression.create(
            "com.example:ValueSet2.lt", selection[1]);
      }
      return CompoundFilterExpression.create(path, map);
    }
    return null;
  }
  public void setValue(
      CompoundFilterExpression<FilterExpression<?>> value) {
    if (value != null) {
      short[][] selection = new short[2][];
      ValueSetFilterExpression expr1 =
          (ValueSetFilterExpression) value.getExpression("attr1");
      if (expr1 != null) {
        selection[0] = expr1.getSelection();
      } else {
        selection[0] = ... // TODO dodać wszystkie wartości VS1
      }
      ValueSetFilterExpression expr2 =
          (ValueSetFilterExpression) value.getExpression("attr2");
      if (expr2 != null) {
        selection[1] = expr2.getSelection();
      } else {
        selection[1] = ... // TODO dodać wszystkie wartości VS2
      }
      field.setValue(selection);
    } else {
      field.setValue(null);
    }
  }
}

FilterExtension

Klauzula WHERE zapytania wyszukiwania jest kompilowana generycznie z wyrażeń filtrujących zdefiniowanych za pomocą funkcji setFilterExpression(), przy czym wyrażenia filtrujące są ze sobą połączone za pomocą funkcji AND. Dodatkowo, klauzula WHERE może zostać rozszerzona o wyrażenie logiczne OQL (połączone z ogólną częścią klauzuli WHERE). Rozszerzenie to można ustawić za pomocą metody setFilterExtension() klas GridSearchSupport lub SearchInterface (patrz sekcje GridSearchSupport i SearchHook). Wyrażenie logiczne OQL musi zostać przekazane do metody setFiterExtension() jako instancja klasy com.cisag.pgm.search.filter.FilterExtension.
W ramach wyrażenia OQL można uzyskać dostęp zarówno do ustawionych wyrażeń filtrujących, jak i atrybutów obiektów biznesowych zdefiniowanych w wyszukiwaniu.
Aby uzyskać dostęp do wyrażenia filtru kolumny w wyrażeniu OQL, odpowiednia nazwa kolumny musi być umieszczona w nawiasach klamrowych.
 
Przykład
new FilterExtension("{attr1} OR {attr2}");
 
Użycie wyrażenia filtrującego w FilterExtension automatycznie prowadzi do usunięcia go z ogólnej części klauzuli WHERE. Przykładowo, gdyby część ogólna w powyższym przykładzie składała się z wyrażeń filtrujących dla attr1, attr2 i attr3, wówczas tylko attr3 pozostałoby w części ogólnej, a cała klauzula WHERE wyglądałaby następująco: ({attr3}) AND ({attr1} OR {attr2}).
 
Uwaga
Tylko wyrażenia filtrujące, które mają wartość niezerową, mogą być używane w wyrażeniu OQL.
Notacja {attr} odwołuje się nie tylko do nazwy kolumny lub wyrażenia filtrującego, ale także do pełnego logicznego wyrażenia OQL (wygenerowanego na podstawie zdefiniowanego wyrażenia filtrującego i metadanych kolumny).
Aby uzyskać dostęp do atrybutu obiektu biznesowego zdefiniowanego w wyszukiwaniu w wyrażeniu OQL, należy użyć zapisu alias:attributeName, przy czym alias dla obiektu biznesowego zdefiniowanego w wyszukiwaniu ma być używany jako alias.
 
Przykład
new FilterExtension("book:employee is not null");
 
Ponadto w wyrażeniu OQL można użyć nazwanych parametrów, których wartości można następnie ustawić za pomocą metod FilterExpression związanych z typem. W wyrażeniu OQL używane nazwy parametrów muszą być umieszczone w nawiasach klamrowych.
 
Przykład
FilterExtension ext =
    new FilterExtension ("book:employee = {paramEmploy-eeGuid}");
ext.setGuid("paramEmployeeGuid",
    partnerLogic.getCurrentPartnerGuid());
 
Uwaga
Nazwy parametrów mogą być dowolnie definiowane. Należy jednak unikać konfliktów z nazwami kolumn. Zalecamy stosowanie odpowiednich konwencji nazewnictwa dla parametrów i kolumn, aby zasadniczo uniknąć konfliktów nazw.
Ważne jest również, aby wartości były faktycznie ustawione dla używanych nazw parametrów.

SearchHook

Zapytaniem do bazy danych można manipulować przed jego wykonaniem (patrz rozdział Manipulowanie zapytaniem do bazy danych). Manipulacji tej można dokonać poprzez zmianę metody preSearch() (patrz: preSearch) lub poprzez implementację SearchHook. SearchHook to Java class, która wywodzi się z abstrakcyjnej klasy bazowej com.cisag.pgm.search.SearchHook i w której obiekt deweloperski Search jest wprowadzany jako hook.
Klasa bazowa com.cisag.pgm.search.SearchHook jest zgodna z tym samym wzorcem, co klasa com.cisag.pgm.objsearch.SearchHook (klasa bazowa dla wyszukiwania OQL), tj. konkretne podklasy definiują w swoim konstruktorze, które hook faktycznie implementują. Obecnie jednak klasa bazowa definiuje tylko jeden taki hook: PRE_SEARCH oraz powiązaną z nim metodę preSearch(SearchInterface). Wewnętrzny interfejs SearchInterface służy jako opakowanie dla bieżącego zapytania do bazy danych i umożliwia wysyłanie zapytań i ustawianie wyrażeń filtrujących oraz FilterExtension.
 
Przykład
public class BookSearchHook extends SearchHook {
  public BookSearchHook() {
    this(0, 0);
  }
  protected BookSearchHook(int activeHooks, intactiveHooks) {
    super(activeHooks|PRE_SEARCH, inactiveHooks);
  }
  @Override
  public void preSearch(SearchInterface si) {
    super.preSearch(si);
    PartnerLogic partnerLogic = PartnerLogic.getInstance();
    FilterExtension ext =
        new FilterExtension("book:employee = {paramEmployeeGuid}");
    ext.setGuid("paramEmployeeGuid",
        partnerLogic.getCurrentPartnerGuid());
    si.setFilterExtension(ext);
  }
}
 
Implementacja SearchHook może być również zarejestrowana jako hook dla więcej niż jednego wyszukiwania. W takim przypadku implementacja powinna również zmienić metodę init klasy bazowej, tak aby miała dostęp do nazwy wyszukiwania i obiektu bazowego wyszukiwania. Odpowiedni obiekt bazowy musi być przechowywany z odpowiednim wyszukiwaniem (patrz rozdział Hook).
 
Uwaga
W przypadku wyszukiwania, SearchHook i zmiana metody preSearch() (patrz: preSearch) z CisCustomisableCockpit (patrz: CisCustomisableCockpit ) lub SearchView (patrz: SearchView) nie powinny być łączone ze sobą, jeśli to możliwe, ponieważ w przeciwnym razie istnieje ryzyko, że jedna implementacja nieumyślnie zignoruje ustawienia drugiej implementacji.

BusinessObjectRegistryHook

Jeśli wartości zestawu wartości są ograniczone przez opis danych, prowadzi to do bardziej złożonych zapytań do bazy danych, ponieważ w tym przypadku wszystkie wartości zdefiniowane w opisie danych są automatycznie uwzględniane w klauzuli WHERE zapytania do bazy danych (patrz rozdział ValueSetFilterExpression). Dzieje się tak również w przypadku, gdy kolumna nie jest używana jako filtr. Jeśli upewniono się, że tylko wartości określone w opisie danych mogą faktycznie wystąpić w danej kolumnie, dana kolumna powinna zostać oznaczona jako kompletna za pośrednictwem hook com.cisag.pgm.appserver.hook.BusinessObjectRegistryHook jako kompletna. W tym przypadku pozwala to uniknąć niepotrzebnego rozszerzania klauzuli WHERE.
Aby oznaczyć zestaw wartości jako kompletny, należy wywołać jedną z metod registerValueSetComplete w metodzie initialise() implementacji hook.
Przykład
 
public class BusinessObjectRegistryHookImpl
    implementuje BusinessObjectRegistryHook {
  public BusinessObjectRegistryHookImpl() {
  }
  public void initialise(BusinessObjectRegistry registry) {
    registry.registerValueSetComplete(
        SalesOrderDetail.class, "accountOriginType");
  }
}
 
Uwaga
Etykietowanie zawsze odnosi się do konkretnego atrybutu obiektu biznesowego, a nie ogólnie do logicznego typu danych lub Data description.
Hook może być używany tylko do wpływania na atrybuty, które należą do tej samej przestrzeni nazw co hook.
DataDescriptionFilter
Klasa DataDescriptionFilter może być użyta do zmiany pewnych właściwości opisu danych w czasie wykonywania. Przykładowo, DataDescriptionFilter może być używany do kontrolowania widoczności kolumny w zależności od funkcji lub ustawienia konfiguracji.
Aby zarejestrować DataDescriptionFilter dla logicznego typu danych, w pełni kwalifikowana nazwa klasy, która implementuje następujący interfejs, musi być przechowywana w powiązanym opisie danych:
com.cisag.pgm.datatype.DataDescriptionFilter
Interfejs ten definiuje metodę getModification(), która musi zwracać odpowiednio skonfigurowaną instancję DataDescriptionModification (patrz sekcja DataDescriptionModification).
 
Przykład
public class ExampleDataDescriptionFilter
    implements DataDescriptionFilter {
  public DataDescriptionModification getModification(
      DataDescription dataDescription) {
    CisSystemManager sm =
        CisEnvironment.getInstance().getSystemManager();
    if (sm.isAvailable("com.example.Example")) {
      return DataDescriptionModification.UNMODIFIED;
    } else {
      return DataDescriptionModification.EXCLUDED;
    }
}
 
DataDescriptionFilter są oceniane nie tylko przez aplikacje typu Lista, ale mają również zastosowanie do atrybutów Widoki obiektów w konfigurowalnych aplikacjach.
DataDescriptionModification
Klasa com.cisag.pgm.datatype.DataDescriptionModification opisuje zmiany, które należy wprowadzić w opisie danych. Instancje DataDescriptionModification można tworzyć i konfigurować za pomocą statycznej metody Factory getInstance(Flags). Klasa zapewnia również często wymagane konfiguracje jako stałe statyczne.
 
Wartość Stała Objaśnienie
UNMODIFIED UNMODIFIED Bez zmian. Obowiązują ustawienia zdefiniowane w opisie danych.
HIDDEN HIDDEN Atrybut lub kolumna nie jest wyświetlana w interfejsie użytkownika.
DISABLED DISABLED Atrybut lub kolumna są wyświetlane jako nieaktywne w interfejsie użytkownika.
READ_ONLY READ_ONLY Atrybutu lub kolumny nie można zmienić w interfejsie użytkownika.
REQUIRED REQUIRED Dla atrybutu wymagany jest wpis.
EXCLUDED EXCLUDED Podobnie jak HIDDEN, ale z tym znaczeniem, że dany atrybut jest ogólnie niedostępny, podczas gdy w przypadku HIDDEN tylko wyświetlanie jest wyłączone.
 
 
Uwaga
Tylko wartości UNMODIFIED i EXCLUDED (lub HIDDEN) są ważne dla aplikacji typu Lista. Wszystkie inne wartości są istotne tylko dla kontrolowania atrybutów w widokach obiektów lub konfigurowanej aplikacji.
RepositoryOnly
W przypadku DataDescriptionFilters system zawsze zakłada, że wymagają one kontekstu z bazą danych OLTP i dlatego wywołuje je tylko wtedy, gdy taki kontekst faktycznie istnieje. Jeśli nie ma kontekstu z bazą danych OLTP, filtr jest ignorowany. Niemniej jednak, DataDescriptionFilter może również implementować interfejs RepositoryOnly, jeśli nie wymaga kontekstu z bazą danych OLTP i chce być wywoływany nawet wtedy, gdy nie istnieje kontekst z bazą danych OLTP.
 
Uwaga
Może wystąpić przypadek, w którym nie istnieje kontekst z bazą danych OLTP, np. jeśli serwer aplikacji jest uruchamiany bez baz danych OLTP (instalacja aktualizacji oprogramowania) lub użytkownik loguje się bez bazy danych OLTP.

Ustawienia

Uprawnienia

Poniższe Capability dotyczą aplikacji typu Lista (w tym SearchView):
 
Capability Objaśnienie
com.cisag.sys.objsearch.IgnoreSearchTimeout Użytkownik może również wykonać wyszukiwanie, jeśli jego czas wykonania przekracza określoną wartość maksymalną (wartość domyślna: 1 minuta).
com.cisag.sys.search.ChangeSettingsForAllUsers Użytkownik może tworzyć, zmieniać i usuwać widoki na poziomie bazy danych.
com.cisag.sys.search.ChangeMetadata Użytkownik może rozszerzyć lub zmniejszyć zestaw kolumn.
 

Właściwości

Na standardowe zachowanie aplikacji typu Lista można wpływać za pomocą następujących właściwości:
 
Właściwość Objaśnienie
com.cisag.pgm.search.gui.GridSearchSupport.
defaultBufferSize
Maksymalna liczba rekordów danych wyświetlanych jako wynik w aplikacji typu Lista.
Domyślną wartością dla liczby obiektów jest 1000. Wartości większe niż 1000 powinny być używane tylko wtedy, gdy serwer aplikacji ma wystarczające zasoby, tj. procesory i pamięć główną, aby obsłużyć dodatkowe obciążenie.
com.cisag.pgm.search.gui.GridSearchSupport.
asynchBufferSize
Maksymalna liczba obiektów otwieranych podczas Aktualizuj (w tle). Domyślna wartość liczby obiektów to 100000.
com.cisag.pgm.search.
gui.GridSearchSupport.maximumDetailRows
Maksymalna liczba obiektów danych szczegółowych, które są otwierane na obiekt główny. Domyślną wartością liczby obiektów jest 10.
 
com.cisag.pgm.gui.DefaultExportLimit
Maksymalna liczba obiektów, które może wyeksportować użytkownik bez uprawnień administratora. Wartość domyślna to 231-1, ale nie więcej niż wartość MaximumExportLimit.
com.cisag.pgm.gui.
MaximumExportLimit
Maksymalna liczba obiektów, które można wyeksportować z aplikacji typu Lista (niezależnie od uprawnień użytkownika). Wartość domyślna to 231-1.
 
Debugowanie
W celu analizy zapytań do bazy danych, dodatkowe informacje mogą być wyświetlane na konsoli za pomocą narzędzia wiersza poleceń Debugowanie klas (dbgcls):
dbgcls -class:com.cisag.sys.kernel.caching.CisComplexOqlCache
Więcej informacji na temat tego narzędzia można znaleźć w dokumentacji: Debugowanie klas.

Czy ten artykuł był pomocny?