Funkcje ułatwiają wykonywanie często wykonywanych operacji, takich jak np. sumowanie wartości. Zapobiega to wielokrotnej duplikacji kodu. Funkcje powinny być niezależne oraz jak najbardziej ogólne i elastyczne (możliwość wykorzystania ich w jak największej liczbie przypadków).
System pozwala na rejestrowanie i wykorzystywanie predefiniowanych funkcji zarówno po stronie serwera jak i po stronie przeglądarki.
Rejestracja funkcji
Rejestracja na serwerze
Rejestracja funkcji dostępnych w systemie odbywa się podczas uruchomienia systemu oraz dynamicznie dla dynamicznych zasobów, takich jak wtyczki. Za rejestrację i przechowywanie funkcji odpowiada FunctionRegistry. Podczas rejestracji wyszukiwane są najpierw wszystkie klasy oznaczone adnotacją @Functions, następnie w tych klasach wyszukiwane są metody oznaczone adnotacją @Function i zostają one zarejestrowane jako funkcje.
Poniższy przykład przedstawia definicję prostej funkcji zwracającej wartość wyższej liczby.
@Functions public class BasicFunctions { @Function public Integer maxOf( Integer a, Integer b ) { return Math.max( a, b ); } }
Zarejestrowana funkcja posiada nazwę, zwracany typ oraz definicję parametrów -> Function.
Rejestracja po stronie przeglądarki
Używanie stworzonej funkcji po stronie przeglądarki (na formularzu zadania) wymaga od programisty dostarczenia jej implementacji w języku JavaScript. Taka implementacja wykorzystywana jest podczas dynamicznych obliczeń po stronie przeglądarki klienta.
Funkcje rejestrowane są z wykorzystaniem API PW.fn.Functions (skrócona nazwa PW.Functions
):
// funkcja "max" która zwraca obiekt typu "float" i przyjmuje 2 parametry typu "float" PW.Functions.register("max", "float", ["float", "float"], function(value1, value2){ return Math.max(value1, value2); });
Podczas rejestracji funkcji niezbędne jest zdefiniowanie:
- nazwy (można zarejestrować wiele funkcji o tej samej nazwie ale musza mieć one inne typy parametrów)
- typu zwracanej wartości
- typów parametrów
- implementacji funkcji
Tak zarejestrowana funkcja zostanie wywołana tylko dla wywołań z argumentami które będą pasowały do zdefiniowanych typów (albo będą tego samego typu albo mogą zostać przekonwertowane).
Oczywiście tak przygotowany skrypt musi zostać wstrzyknięty w obszar formularza zadania. Więcej o wstrzykiwaniu skryptów tutaj.
Funkcje ogólne
Jeżeli podczas rejestracji funkcji nie zdefiniujemy typów parametrów, to funkcja będzie traktowana jako implementacja ogólna. Taka implementacja zostaje wywołana jeżeli nie będzie żadnej innej implementacji funkcji, która może zostać wywołana z aktualnymi parametrami. Parametry przekazane do takiej funkcji są instancjami klasy PW.component.Arg.
// brak zdefiniowanych typów PW.Functions.register("isVariable", "boolean", function(arg1){ // arg1 jest obiektem klasy PW.component.Arg return !arg1.isArray && arg.value.is("variable"); });
Rejestracja we wtyczce
Rejestracja funkcji we wtyczce jest taka sama jak zostało to opisane powyżej, jednak należy dodatkowo zaznaczyć, że wtyczka ta udostępnia funkcje do zarejestrowania. W tym celu należy w pliku suncode-plugin.xml dodać wpis:
<functions key="functions" />
Taki wpis spowoduje automatyczne wyszukanie wszystkich zdefiniowanych funkcji.
Wykorzystanie funkcji po stronie przeglądarki
Obecnie funkcje mogą być wykorzystywane na formularzu zadania.
Pobranie funkcji odbywa się za pomocą obiektu PW.core.fn.Functions:
var fn = Functions.getFunction("maxOf",[1,4]);
Mając pobraną funkcję z parametrami (PW.core.fn.Function) możemy ją wywołać:
fn.call(); //returns 4;
W powyższym przykładzie pobrania funkcji, parametry zostały podane w sposób uproszczony, czyli same wartości bez konkretnego typu. Podczas konwersji typów, mechanizm sam zgadnie typy tych wartości. Gdyby została tam podana wartość tekstowa np.: "zmienna_1", to zostałoby to rozpoznane jako typ string. Jednak w przypadku, gdybyśmy chcieli, aby w tym miejscu znalazła się wartość zmiennej formularzowej o id zmienna_1, to w takim przypadku trzeba podać wartość razem z typem:
var fn = Functions.getFunction("maxOf",[{type: "variable",value: "zmienna_1"}, 4]); //lub var fn = Functions.getFunction("maxOf",[ServiceFactory.getVariableService().get("zmienna_1"), 4]);
Istnieje również możliwość podania innej funkcji w parametrze:
var fn = Functions.getFunction('maxOf',[{type:"function",value:{name:"maxOf",parameters:[{type:"float",value:8},{type:"float",value:4}]}} ,3]);
W powyższym przykładzie najpierw zostanie wywołana wewnętrzna funkcja maxOf z parametrami 8 i 4, która zwróci wynik 8 i następnie zostanie wywołana funkcja zewnętrzna maxOf z parametrami 8 i 3, która zwróci wynik 3. Wydaje się jednak, że prostszym i "czystszym" rozwiązaniem jest wywołanie samemu wewnętrznej funkcji:
var fn = Functions.getFunction('maxOf',[Functions.getFunction('maxOf',[8,4]),3]);