Play Framework – Formatowanie JodaTime w templatach

Ostatnio hobbystycznie tworzę projekt w Scali z użyciem Play Framework. Sam framework jest bardzo interesujący, ale jest o nim bardzo wiele napisane w całym internecie dlatego to nie jemu poświęcony jest wpis.

Wpis jest o pewnym małym problemie, na który się dzisiaj natknąłem i niestety internet nie był zbyt pomocny – przynajmniej przy frazach wyszukiwania na jakie wpadłem 🙂

Problem: Chcę wyświetlić na stronie sformatowaną datę, którą mam w postaci obiektu klasy DateTime z biblioteki JodaTime.

Większość znalezionych rozwiązań niestety dotyczy daty zapisanej jako stary dobry java.util.Date. W takim przypadku wystarczy jedynie napisać:

@myDate.format("dd.MM.yyyy")

Oczywiście java.util.Date nie ma takiej metody więc jest tam zastosowana jakaś magia Scali 🙂

Znalazłem pewne rozwiązania dotyczące JodaTime ale były one bardzo brzydkie – wymagały zbyt dużo niepotrzebnego kodu w templacie, a to przecież Scala…

Pomyślałem o zrobieniu własnej Scalowej magii dla dat z JodaTime – w tym przypadku nieocenione okazały się doświadczenia związane z tym co opisałem w poprzednim poście. Utworzyłem sobie nowy obiekt:

object Extensions {
class JodaTimeWrapper[T <: ReadableInstant](t:T) {
  def format(pattern: String): String = {
    val fmt = DateTimeFormat.forPattern(pattern)
    fmt.print(t)
  }
  implicit def convertToJodaTimeWrapper[T <: ReadableInstant](t:T) =
    new JodaTimeWrapper(t)
}

I potem w pliku *.scala.html jedynie:

@import views.Extensions._
@myJodaTimeDate.format("dd.MM.yyyy")

Job done. A o co właściwie chodzi? Analogicznie jak w przypadku DSLa dla SWT utworzyłem klasę JodaTimeWrapper, która na zadanym obiekcie pozwala wywołać metodę format(). Potem tylko deklaracja implicit konwertera typu ReadableInstant na nasz wrapper. Kluczowe jest tutaj właśnie słówko implicit. Dzięki niemu kompilator w trakcie pracy jest w stanie stwierdzić, że obiekt typu DateTime jest również obiektem typu ReadableInstant (bo jest to jego klasa nadrzędna) i wie, że może sobie ten obiekt przekonwertować na JodaTimeWrapper i stamtąd już potrafi wywołać metodę format().

Reklamy

Scala – SWT i DSL

Motywacja

No i stało się. Poszedłem za radą Xiona i zerknąłem bliżej na Scalę. Przez ostatnie tygodnie trochę o niej poczytałem i napisałem kilka niewielkich rzeczy dla nabrania wprawy ale pisanie zwykłego kodu w Scali (poza składnią) nie różni się mocno od dowolnego innego języka (choć to też zależy od tego czy chce się pojechać po bandzie z możliwościami funkcyjnymi Scali 🙂 )

W trakcie zabawy natknąłem się na różne artykuły odnośnie tworzenia Domain Specific Language (DSL). Wówczas nie zwróciłem na to uwagi dlatego, że wykorzystywane tam konstrukcje wydawały się pochodzić zupełnie z innego świata. Ostatnio pomyślałem, że prędzej czy później trzeba będzie wypłynąć na głęboką wodę i się tym zająć. Lepiej więc wcześniej niż później 🙂

Pierwsze próby

Chcąc upiec dwie pieczenie na jednym ogniu zdecydowałem się na implementację DSL dla systemu GUI SWT. Oczywiście na początku nie wiedziałem zupełnie czego chcę ani tym bardziej – jak to osiągnąć. Dlatego postąpiłem tak jak każdy, kto osiąga taki punkt swojego życia – zapytałem Google…

Po kilku mało interesujących przykładach natrafiłem na coś wartościowego. Pomysł jest bardzo interesujący i pokazał mi w którą stronę można się udać. Na podstawie informacji o szczegółach implementacji, które zostały tam pokazane postanowiłem odtworzyć ten system i po około godzinie prób i błędów miałem już okienko i kilka przycisków na ekranie.

Pomysł z wykorzystaniem kilku dwóch list parametrów (jednej do podania listy funkcji ustawiających daną kontrolkę i drugiej określającej rodzica kontrolki) jest dosyć ciekawy. Jedyna rzecz jaka mi przeszkadzała to pewien brak porządku. Wywołania funkcji, które określają zachowanie kontrolki nadrzędnej mogą być poprzeplatane deklaracjami kontrolek dzieci:

group (
  text(&quot;Name&quot;),
  gridLayout(2, false),
  gridData(FILL, FILL, true, true),
  label(text(&quot;First&quot;)), edit(text(&quot;Bullet&quot;)),
  label(text(&quot;Last&quot;)), edit(text(&quot;Tooth&quot;))
),

W powyższym przykładzie text, gridLayout oraz gridData dotyczą elementu group natomiast wszystkie label i edit to już definicje nowych obiektów.

I wreszcie coś swojego

Stwierdziłem, że spróbuję napisać to jakoś inaczej. I po kilku godzinach kombinowania i eksperymentowania z różnymi aspektami języka udało mi się napisać działający kod, który generuje takie samo okienko jak w powyższym wpisie:

val shell = new SwtBuilder {
  shell text &quot;User Profile&quot; gridLayout (2, true)
  group text &quot;Name&quot; gridLayout 2 gridData (SWT.FILL, SWT.FILL, true, true) withChildren {
    label text &quot;First&quot;;
    edit text &quot;Bullet&quot; gridData (SWT.FILL, SWT.CENTER, true, true) name &quot;first&quot;
    label text &quot;Last&quot;;
    edit text &quot;Tooth&quot; gridData (SWT.FILL, SWT.CENTER, true, true) name &quot;last&quot;
  }
  group text &quot;Gender&quot; fillLayout (SWT.HORIZONTAL, 5) gridData (SWT.FILL, SWT.FILL, true, true) withChildren {
    radio text &quot;Male&quot;
    radio text &quot;Female&quot;
  }
  group text &quot;Role&quot; fillLayout (SWT.HORIZONTAL, 5) gridData (SWT.FILL, SWT.FILL, true, true) withChildren {
    checkbox text &quot;Student&quot;
    checkbox text &quot;Employee&quot;
  }
  group text &quot;Experience&quot; fillLayout (SWT.HORIZONTAL, 5) gridData (SWT.FILL, SWT.FILL, true, true) withChildren {
    spinner
    label text &quot;years&quot;
  }&lt;/p&gt;
&lt;p&gt;  button text &quot;Save&quot; gridData (SWT.RIGHT, SWT.CENTER, true, true) onSelect {
    (ctls get &quot;first&quot;, ctls get &quot;last&quot;) match {
      case (Some(f:Text), Some(l:Text)) =&gt;
        println(&quot;Name: &quot; + f.getText + &quot; &quot; + l.getText)
      case _ =&gt; println(&quot;Something is wrong!&quot;)
    }
  }
  button text &quot;Close&quot; gridData (SWT.LEFT, SWT.CENTER, true, true) onSelect {
    shell close
  }
} shell

Wynik działania powyższego kodu

Część tajemnicy tkwi w tym, że scala zezwala na wywoływanie metod z argumentami bez kropki:

val label = new Label(parent, SWT.NONE)
label.setText(&quot;Hello World&quot;) // te dwie linie
label setText &quot;Hello World&quot;  // dają ten sam efekt

Pozostała część twki konwertowaniu w locie obiektów SWT do różnych wrapperów dodających wszelkie metody typu text lub gridData. Funkcje tworzące nowe kontrolki (label, button itp.) tak na prawdę tworzą jedynie nowe obiekty danej klasy:

def button() : Button = new Button(context.value, SWT.PUSH)
def button(style:Int) : Button = new Button(context.value, style)

Jeśli w tym momencie pomyślałeś, że zamiast pisać funkcję dwa razy z różnymi parametrami można ją napisać raz z parametrem domyślnym to… nie 🙂 Zależy mi na tym, żeby w kodzie było jak najmniej bezsensownych nawiasów. Jeśli utworzę tylko jedną wersję funkcji to jeśli nie chcę podać stylu musiałbym dodać parę pustych nawiasów – paskudne!

Wracając do rozwiązywania tajemnicy działania… W klasie SwtBuilder zdefiniowane są klasy typu:

class SetTextWrapper[T &lt;: {def setText(text:String)}](val subject:T) {
  def text(t:String) : T = {
    subject.setText(t)
    return subject
  }
}

Taka klasa bierze jako argument konstruktora dowolną klasę typu T, który posiada metodę setText(text:String) i pozwala wywołać na niej swoją metodę text, która ustawia tekst w obiekcie klasy T. Kluczowe jest tutaj zwracanie oryginalnego obiektu, a powód wyjaśni się za chwilę. Do pary potrzebujemy jeszcze automatycznego konwertera, który będzie używany przez kompilator kiedy ten uzna, że konwersja jest potrzebna:

implicit def convertToSetTextWrapper[T &lt;: {def setText(text:String)}] (t:T) = new SetTextWrapper(t)

To mówi kompilatorowi, że wszystkie obiekty posiadające posiadające typ T mogą być automatycznie (słowo kluczowe implicit) owinięte obiektem klasy SetTextWrapper. Dzięki takiej konstrukcji możliwe jest napisanie:

(new Label(parent, SWT.NONE)).text(&quot;Test&quot;).text(&quot;Hello&quot;).text(&quot;World&quot;)

Zaczynając od początku, mamy obiekt klasy Label. Następnie kompilator spotyka wywołanie metody text, której taki obiekt nie posiada. Wówczas używając zdefiniowanej wyżej funkcji konwertującej owija obiekt w SetTextWrapper i teraz może już spokojnie wywołać metodę text. Ta z kolei zwraca znów obiekt typu Label i historia zaczyna się od początku dla następnego wywołania text.

Biorąc pod uwagę, że mamy zdefiniowaną metodę label(), która generuje nowy obiekt typu Label oraz to, że scala pozwala pisać bez kropek i nawiasów możemy napisać powyższą linię kodu bardziej podobnie do pierwszego przykładu:

label text &quot;Test&quot; text &quot;Hello&quot; text &quot;World&quot;

Oczywiście nic nie stoi na przeszkodzie aby używać pierwszej notacji. Ma ona tę zaletę, że można rozpisać właściwości obiektu na kilka linii zamiast pisać jedną strasznie długą:

group()
  .text(&quot;Name&quot;)
  .gridLayout(2)
  .gridData(SWT.FILL, SWT.FILL, true, true)
  .withChildren {
    label text &quot;First&quot;;
    edit text &quot;Bullet&quot; gridData (SWT.FILL, SWT.CENTER, true, true) name &quot;first&quot;
    label text &quot;Last&quot;;
    edit text &quot;Tooth&quot; gridData (SWT.FILL, SWT.CENTER, true, true) name &quot;last&quot;
  }

A jak działa withChildren?

O! Cieszę się, że pytasz 🙂 Otóż… działa na takiej samej zasadzie jak to co opisano powyżej:

class CompositeWrapper[T &lt;: Composite](c :T) {
    def withChildren(f: =&gt; Unit) : T = {
      context.withValue(c) { f }
      return c
    }
  }

Mamy więc pewien wrapper, który jako argument konstruktora przyjmuje obiekty typu T, które tym razem są obiektami dziedziczącymi (pośrednio bądź bezpośrednio) po typie Composite. Łatwo się domyślić, że typ ten pozwala na pokazywanie kontrolek „w sobie” czyli może być on rodzicem np. dla obiektu klasy Button. Do pary oczywiście jest odpowiednia metoda konwertująca ale wygląda analogicznie to pokazanej wcześniej więc jej nie pokażę.

Jak widać wrapper posiada jedną metodę withChildren, która przyjmuje jako parametr funkcję bezargumentową, a zwraca znów typ T. Szalenie interesujący jest tutaj obiekt context. Jest to obiekt klasy DynamicVariable, który za pomocą metody withValue pozwala w czasie wykonania bloku kodu podanego jako drugi argument podmienić wartość context.value na wartość podaną jako pierwszy argument. Zmienna context.value to oczywiście aktualnie używany rodzic dla wszystkich kontrolek w danym bloku. Zatem jeśli mam zdefiniowaną metodę:

def label() : Label = new Label(context.value, SWT.NONE)

I wykonamy kod:

context.withValue(new Composite(otherParent, compositeStyle)) {
  label text &quot;First&quot;
  label text &quot;Second&quot;
}

To powstaną dwa obiekty Label oraz komponent Composite, który jednocześnie będzie rodzicem dla tych dwóch etykiet.

Ok, a co ze zdarzeniami?

Kolejne świetne pytanie! Samo utworzenie interfejsu to jedynie część sukcesu. Przede wszystkim powinniśmy mieć możliwość jakoś dostać się do naszych kontrolek. Na tę okoliczność przygotowałem specjalny… wrapper 🙂 Wrapper ten działa dla wszystkich obiektów typu Widget (oczywiście ma

konwerter do pary). Pozwala na nadanie kontrolce nazwy i zapisanie jej w mapie obiektów pod daną nazwą (która jest obiektem Scalowej klasy Map).

W taki sposób możemy sobie spokojnie nazwać element, a po utworzeniu GUI w prosty sposób się do niego odwołać. Pozostaje jeszcze kwestia zdarzeń typu wciśnięcie przycisku. W SWT rejestruje się w danym obiekcie implementację interfejsu SelectionListener, i odbywa się to oczywiście dzięki metodzie addSelectionListener. Dlatego możemy utworzyć specjalny wrapper dla wszystkich obiektów posiadających tę metodę (oczywiscie razem z konwerterem):

class SetSelectionWrapper[T &lt;: {def addSelectionListener(listener:SelectionListener)}](val subject:T) {
  def onSelect(f: =&gt; Unit) : T = {
    subject.addSelectionListener(new SelectionAdapter() {
      override def widgetSelected(e:SelectionEvent) : Unit = f
    })
    return subject
  }
}&lt;/p&gt;
&lt;p&gt;implicit def convertToSetSelectionWrapper[T &lt;: {def addSelectionListener(listener:SelectionListener)}](t:T) = new SetSelectionWrapper(t)

I teraz już nie ma żadnych przeszkód by napisać:

button text &quot;Save&quot; gridData (SWT.RIGHT, SWT.CENTER, true, true) onSelect {
  (ctls get &quot;first&quot;, ctls get &quot;last&quot;) match {
    case (Some(f:Text), Some(l:Text)) =&gt;
      println(&quot;Name: &quot; + f.getText + &quot; &quot; + l.getText)
    case _ =&gt; println(&quot;Nobody Expects The Spanish Inquisition!&quot;)
  }
}

Dodam tylko, że ctls to wspomniana wcześniej mapa nazwanych kontrolek.

Podsumowanie

Ogromną zaletą takiego rozwiązania jest duża elastyczność. Nic nie stoi na przeszkodzie aby w bloku kodu dla withChildren używać dowolnych konstrukcji języka – jest to w końcu zwykły kod. Dodatkowo, jakoby za darmo, kompilator w trakcie pracy wytknie nam wszystkie błędy (np. próbę ustawienia tekstu dla kontrolki, która tego nie obsługuje).

Cóż. Jak się okazuje – nie taki diabeł straszny jak go malują. Po przezwyciężeniu początkowej niechęci do składni Scali i poświęceniu sporej ilości czasu na zrozumienie o co tam w ogóle chodzi nauka nowych rzeczy przychodzi już dużo łatwiej a błędy kompilatora stają się twoimi przyjaciółmi 🙂

Let’s make things Groovy

Jakiś czas temu przeczytałem artykuł (http://steve-yegge.blogspot.com/2007/12/codes-worst-enemy.html) w którym autor dość nieprzychylnie wyrażał się o Javie jako języku programowania. Mimo, że nie całkiem zgadzam się ze wszystkim co zostało tam napisane to jednak zacząłem zwracać uwagę na to ile kodu wymaga Java nawet do najprostszych rzeczy. Zerknijmy sobie na prosty przykład – bardzo standardowe „Hello World”:

public class Main {
  public static void main(String[] args) {
    System.out.println("Hello World!");
  }
}

Już patrząc na samą instrukcję System.out.println() odnosi się wrażenie, że to jest dużo pisania jak na tak prostą funkcję. Dla porównania możemy spojrzeć na kod Pythona:

print "Hello World"

Super! Krótko zwięźle i na temat. Problem w tym, że my znamy się dobrze na Javie i potrafimy używać przeróżnych bibliotek do tego języka, a w dodatku nie znosimy nazewnictwa metod w takiej_postaci() preferując raczej postaćTakową(). No dobra, może nie wszyscy mają takie uprzedzenia ale ja na pewno tak 🙂 (na pewno nie wydaje się wam, że Python ma za dużo __podkreśleń__?). A jeśli to nie jest problem to może brak standardu dla dokumentacji Pythonowej? Lub to, że część bibliotek dla Pythona dokumentacji nie ma wcale lub sprowadza się ona po prostu do listy wszystkich klas i metod, które czasem są opatrzone lapidarnym komentarzem? (mówię o czymś w stylu: get_none() – w dokumentacji przeczytamy returns None…)

Jeśli chcecie teraz zaproponować mi Jythona to dajcie sobie spokój. Byłem, sprawdziłem, nie podoba mi się 🙂 Najbardziej odrzuca mnie czas startu ale samo tempo pracy również nie powala. Możecie mi wierzyć, że bardzo chciałem sprawić by Python stał się moim językiem ojczystym do programowania, ale ostatecznie zawsze wracałem do Javy.

Potem odkryłem Groovy

O Groovy słyszałem już wcześniej. Nikt jednak nie wydawał się podekscytowany tym językiem i nikt nie zwracał na niego większej uwagi. W takim razie, pomyślałem, ja też nie będę. Ostatnio jednak w wyniku poszukiwania języka innego niż Java ale działającego na JVM (chodziło mi o kompatybilność z bibliotekami) postanowiłem zabrać Groovy na jazdę testową.

To była chyba najbardziej inspirująca zabawa z kodem odkąd zacząłem programowanie! W ciągu weekendu przeczytałem wszystkie interesujące mnie rzeczy dotyczące języka (głównie – co jest tu innego niż w Javie, wszelkie metaprogramowanie oraz ciekawostki na które się natknąłem jak np. DSL w Groovy). Najciekawsze jednak było to, że czułem się jakbym odkrył nowe możliwości w czymś co znam już od dawana. Pisałem po prostu w Javie ale wykorzystywałem dodatkowe możliwości, które zupełnie zmieniły styl kodu i wygodę programowania.

Co jest wartego uwagi w Groovy?

Przede wszystkim – jest to język dynamicznie typowany, choć trzeba zaznaczyć, że jest to nieco ciekawsze niż w przypadku Pythona gdyż możemy podawać typy analogicznie jak w Javie:

// Przykład 1
def greet(String who) {
  println "Hello $who!"
}

def greet2(who) {
  println "Hello $who!"
}

greet("Groovy")
greet2 "Groovy"
greet2 12345

W tym przykładzie widać kilka nietypowych dla Javy konstrukcji, ale zacznijmy od tematu omawianego wcześniej. Mamy zdefiniowane dwie funkcje. Pierwsza z nich posiada zastrzeżenie, że argument who musi być typu String. Próba wywołania takiej metody z argumentem innego typu skończy się wyrzuceniem odpowiedniego wyjątku (oczywiście w czasie wykonania programu). Druga funkcja nie posiada takiego ograniczenia i w tym wypadku możemy podawać właściwie dowolne obiekty. Oczywiście możemy również określić typ zwracany przez funkcję podając typ pomiędzy słowem kluczowym def a jej nazwą.

Dodatkowo w funkcjach i metodach nie musimy używać słowa return (zazwyczaj) gdyż ostatnia wartość jest tą zwracaną. To oznacza, że jeśli napiszemy funkcję, która kończy się linijką zawierającą po prostu liczbę: 25 to ta liczba będzie wartością całej funkcji (chyba, że wcześniej użyto słowa kluczowego return).

Kolejne niespodzianki czekają na nas już w linii numer 2! Zamiast przydługiego System.out.println mamy po prostu println. Mimo, że bardzo przypomina to trochę rozwiązanie zastosowane w języku Python wcale takie nie jest. Tak na prawdę wywoływany jest stary dobry System.out.println. Groovy przy starcie globalnie importuje statycznie wspomnianą metodę. Efekt jest dokładnie taki jakbyśmy napisali na początku każdej klasy Javy:

import static System.out.println;
// wówczas można tej metody używać tak:
println("Hello World");

Dodatkowo Groovy automatycznie importuje kilka przydatnych rzeczy do każdego kompilowanego pliku:

import java.lang.*;
import java.util.*;
import java.net.*;
import java.io.*;
import java.math.BigInteger;
import java.math.BigDecimal;
import groovy.lang.*;
import groovy.util.*;

Idąc dalej w drugiej linijce kodu zauważymy, że argument tej metody nie znajduje się w nawiasie. Jest to oczywiście uproszczenie wprowadzone w Groovy, żeby pozbyć się niepotrzebnych znaków z kodu przez co ma on stać się czytelniejszy. Oczywiście nic nie stoi na przeszkodzie, żeby nawiasów używać. Możemy zatem napisać: println(„Hello World”). Jedyny zgrzyt może pojawić się gdy chcemy wywołać funkcję lub metodę nie posiadającą argumentów. Wówczas kod wygląda identycznie jak dostęp do pola klasy, a jeśli Groovy go nie znajdzie to rzuci wyjątkiem informującym, że takiego pola nie ma. Ja preferuję pisanie nawiasów wszędzie (z wyjątkiem println [nie wiem czemu]) dlatego, że w innym wypadku moim zdaniem kod wygląda niespójnie.

Kontynuując naszą podróż po linijce numer 2 natrafimy na $who w środku stringa. Jest to kolejny cukierek od Groovy. Możemy w ten sposób wstawiać dowolne wartości do ciągu znaków. Co jest jeszcze ciekawsze? Oczywiście to, że dodając klamry możemy w ten sposób wykonać w międzyczasie dowolny kod Groovy:

println "Test: ${def b = 30; ++b}"
// Wyświetli: "Test: 31"

Docierając do końca 2 linijki możemy zauważyć brak średnika. Jest on oczywiście opcjonalny. Niektórym się to podoba, innym nie, ale to nie ma większego znaczenia – każdy pisze jak mu się podoba 🙂 Warto jednak pamiętać, że średnik rozdziela instrukcje i, tak jak w powyższym przykładzie, czasami może się przydać.

Coś więcej?

Hmm… znajdzie się więcej 🙂 Prawdziwą siłą Groovy są domknięcia (ang.: Closures – tak po polsku brzmi jakoś głupawo). Domknięcie jest, ujmując to najprościej, kawałkiem kodu, który nie ma nazwy i może być wywołany zupełnie jak funkcja. Deklaruje się je przy pomocy klamer {}. Do czego można je wykorzystać? Całkiem fajny przykład podał Andrew Montalenti w swoim poście i pozwolę sobie go przytoczyć:

["Rob", "Christopher", "Joe", "John"].findAll{ 
  it.size() < = 4 
}.each { 
  println it 
}
// ==> prints "Rob", "Joe" and "John"

Groovy dodaje kilka przydatnych metod do pracy na tablicach i listach (m. in. findAll i each). Być może niektórym z was przypomina to trochę jQuery – i dobrze 🙂 W tym przykładzie po prostu mówimy, że chcemy znaleźć wszystkie elementy tablicy, których długość jest mniejsza od 4, a potem każdy z nich wyświetlić. Tak na prawdę ten opis jest zupełnie zbędny bo nawet źrednio rozgarnięty programista nawet nie znający Groovy zrozumie o co tu może chodzić 🙂

Closures w Groovy są traktowane trochę specjalnie. Jeśli metoda jako ostatni argument posiada właśnie domknięcie to może ono być podane poza nawiasem tej metody. To znaczy, że mając:

// metoda wywoluje domknięcie z parametrem s
def metoda(String s, Closure c) {
	c(s);
}

Możemy użyć takiej metody na dwa sposoby:

metoda("hello", { println it })

metoda("hello") { println it }

Polecam jednak odwiedzić stronę Groovy i poczytać więcej. Jeśli ktoś zna już Javę to nauka Groovy jest stosunkowo bezbolesna. Właściwie jest to wręcz przyjemność bo możemy zamienić często długi kod javy na krótsze odpowiedniki w Groovy.

A wydajność?

Są pewne argumenty przeciw używaniu Groovy. Jak każdy język dynamiczny posiada on pewien narzut obliczeniowy, który powoduje, że jest on wolniejszy niż zwykła Java. Jak dotąd w celu zwiększenia wydajności pewnej częśći kodu można było użyć adnotacji @CompileStatic. Niestety użycie jej mogło spowodować, że kod zachowa się nieco inaczej niż byłoby to w Groovy.

Na nasze szczęście przyszła Java 7, a wraz z nią JVM uzyskał nową funkcję o nazwie InvokeDynamic. Powinna ona pozwolić Groovy na znaczne zbliżenie się do wydajności Javy. Na razie nie ma tego wiele, ale niektórzy twierdzą, że jego wykorzystanie faktycznie poprawiło znacząco predkość.

Nie obiecuję, że jeszcze napiszę coś o Groovy bo zawsze kiedy coś podobnego obiecuję to ostatecznie i tak tego nie robię 🙂 Dlatego po prostu miejmy nadzieję, że się uda 😉

Własny odtwarzacz audio/wideo w kilku linijkach

Aktualnie zajmuję się coraz intensywniej przygotowywaniem się do utworzenia projektu związanego z moją pracą magisterską (czy wręcz będącym jego podstawą 😉 ). Jego kluczową częścią jest odtwarzanie multimediów dlatego dzisiaj sprawdziłem w jaki sposób najlepiej to ugryźć. Oczywiście im bardziej dostępne dla ludzi (wieloplatformowe) tym lepiej.

Na początek pomyślałem sobie, by napisać plugin do programu Totem. Jest to Linuksowy program do odtwarzania filmów i utworów. Kiedy rozpocząłem swoje poszukiwania odpowiednich tutoriali w tym temacie okazało się, jest z tym raczej krucho. Bardzo słaba dokumentacja i wsparcie od kogokolwiek skłoniła mnie jednak do poszukania czegoś innego.

Jak zwykle w takich sytuacjach pierwsze kroki kieruję w stronę Pythona. Po prostu wiem, że on potrafi zazwyczaj wszystko co potrzeba. W związku z tym, że jakiś czas temu uczyłem się PyQt4 pomyślałem, że może i to przyjdzie mi z pomocą.

Oczywiście nie zawiodłem się 🙂 Okazało się bowiem, że Qt ma przepiękne wsparcie dla odtwarzania audio/video poprzez Phonon. Po kilku minutach przypatrywania się genialnej dokumentacji Qt (ma świetne przykłady) napisałem kilka linijek w interaktywnej konsoli Pythona i moim oczom ukazało się okienko z odtwarzaniem filmu! Linijki te wyglądały tak:

>>> import sys
>>> from PyQt4.QtGui import QApplication
>>> from PyQt4.phonon import Phonon
>>> app = QApplication(sys.argv)
>>> player = Phonon.VideoPlayer(Phonon.VideoCategory)
>>> source = Phonon.MediaSource(„/sciezka/do/pliku”)
>>> player.setVisible(True)
>>> player.play(source)

W ten sposób na ekranie pojawiło się proste okienko z odtwarzaniem filmu. Przy pomocy tego narzędzia można napisać prosty program, który uruchomiony z linii poleceń rozpocznie odtwarzanie filmu:

import sys
from PyQt4.QtGui import QApplication
from PyQt4.phonon import Phonon

if __name__ == '__main__':    
    app = QApplication(sys.argv)
    app.setApplicationName("Simple Video Player")
    player = Phonon.VideoPlayer(Phonon.VideoCategory)
    
    source = Phonon.MediaSource(sys.argv[1])
    player.play(source)
    player.setVisible(True)
    
    app.exec_()

W taki sposób możemy stworzyć prosty odtwarzacz. To jednak nie wszystko! W pakiecie dostajemy zupełnie za darmo odtwarzanie strumieni sieciowych. Jeśli do MediaSource podamy URL to player po prostu rozpocznie strumieniowanie i odtwarzanie filmu czy pliku dźwiękowego. Oczywiście podając URL statycznego pliku graficznego również ujrzymy go na ekranie. Problem pojawia się jedynie z przezroczystością w plikach PNG, ale do moich zastosowań zupełnie wystarczy mi to co działa dobrze.

Coraz bardziej się przekonuję o tym, że Python+Qt4 to duet nie do zastąpienia. Wszystko o czym pomyślę można osiągnąć pisząc w zasadzie bardzo niewiele linijek i na dodatek wszystko da się uruchomić na różnych systemach operacyjnych. Pięknie! 🙂

Ostatnie prace

Dawno nie zajrzałem, ani nie napisałem 😉

Wszystko dlatego, że specjalnie nie miałem o czym pisać ponieważ zajmowałem się głównie rzeczami uczelnianymi, a kto chce słuchać o podstawach sieci neuronowych albo javowych technologiach? 😉

W międzyczasie przypatrywałem się grze Minecraft. Mimo tego, że na pierwszy rzut oka wygląda okropnie (w sumie to na każdy następny rzut oka wygląda równie słabo), a gra nie ma za specjalnie celu wielu ludzi nie może zrozumieć dlaczego, w ogóle taka gra istnieje i ma się dobrze… Początkowo też nie byłem przekonany ale po jakimś czasie okazało się, że gra jest niezwykle grywalna! Niemały wpływ na taki stan rzeczy ma fakt, że świat zbudowany z klocków można całkowicie przemodelować przy użyciu kilofa, łopaty i siekiery (które trzeba najpierw sobie zrobić).

Tak czy inaczej nie chciałem zachwalać Minecrafta bo czy ktoś go lubi czy nie to już kwestia indywidualna. Postanowiłem stworzyć coś podobnego do Minecrafta tylko idącego w nieco innym kierunku bardziej skupiając się na drugiej części nazwy – craftingu. Oczywiście zanim będzie można zabrać się w ogóle za tworzenie czegokolwiek potrzebujemy głównie świata, w którym będziemy pracować 😉 Bez tego ciężko pozyskać surowce.

W czasie świąt udało mi się zmusić silnik jMonkey Engine 3 do wyświetlania świata złożonego z bloczków. Efekt można podziwiać na poniższym screenie:

W chwili obecnej możliwe jest niszczenie i dostawianie bloków różnych typów. Dodatkowo ostatnim osiągnięciem jest oświetlenie per-blokowe 😉

Oświetlenie zrealizowane jest przy pomocy kolorów wierzchołków, które są mieszane z teksturą przy pomocy prostego pixel shadera. Brakuje tutaj porządnego algorytmu do zaciemniania miejsc zakrytych przed źródłem światła innymi blokami.

Ostatnimi dwiema rzeczami, które potrzebuję do osiągnięcia pełni szczęścia w tej chwili jest utworzenie generatora terenu, który potrafiłby „dogenerować” kawałki terenu jeśli byłby potrzebny oraz zaimplementowanie jakiejś prostej fizyki by móc się poruszać po świecie.

Do tej pory do fizyki używałem zintegrowanego z jME3 silnika fizyki jBullet, ale wydaje się to być trochę wyciąganiem armaty na muchę…. Pomijam już fakt, że przy zmienianiu geometrii świata trzeba też było generować na nowo CollisionShape dla danego kawałka terenu, a to zabiera kilka cennych kwantów czasu co jest niedopuszczalne! 😉

Tak więc… nie leniłem się ostatnio choć może tak to wyglądało kiedy nic nie pisałem :p Jeśli macie jakiś pomysł jak stworzyć generator terenu, który można tu wykorzystać to jestem otwarty na propozycje 🙂

Przypuszczam, że w przyszłości pojawi się jeszcze jakiś wpis na temat tworzonego przeze mnie… czegoś 😉

Java Scripting with JavaScript

Jakiś czas temu pisałem o tym jaki to Jython jest świetny ze względu na to, że potrafi się świetnie integrować z Javą itd. Nadal uważam, że to dobra rzecz, ale po pewnym czasie używania tegoż zaczęła mnie irytować pewna sprawa – czas uruchamiania!

Na początku było tak, że za każdym razem przy uruchamianiu aplikacji kompilowałem skrypty. Myślałem wówczas, że tworzenie interpretera i kombinowanie da się później wyeliminować kompilując skrypty do plików *.class. Tak się niestety nie stało.

Ostatnio usiłując poprawić czas uruchamiania aplikacji skompilowałem najpierw te kilka skryptów testowych do plików binarnych klas Javy i spróbowałem je załadować zupełnie pomijając tworzenie obiektu klasy PythonInterpreter. Życie nie okazało się jednak łaskawe i jak się okazało Jython utworzył mi klasę NazwaKlasy$py.class. Wszelkie próby załadowania tej klasy bez użycia Jythona skończły się niepowodzeniem… Sam Jython oczywiście potrafił bez problemów wczytać ten plik ale zajmowało to tyle samo czasu ile wcześniej (ok. 5 sekund), zatem startowanie aplikacji nie przyspieszyło.

Postanowiłem zatem poszukać pomocy gdzie indziej – zmienić język skryptowy. Po krótkim poszukiwaniu stwiedziłem, że chyba najbardziej trafnym językiem skryptowym dla Javy będzie.. JavaScript 🙂
Szybko zabrałem się za pisanie testowych skryptów i kodu uruchamiającego. Kiedy opanowałem samo wykonywanie, przyszedł czas na kompilację i ładowanie. Po kilkudziesięciu minutach z dokumentacją i przykładowymi kawałkami kodu udało mi się stworzyć program kompilujący plij *.js do pliku *.class. Okazało się to być znacznie prostrze niż przypuszczałem gdyż wystarczył taki fragmencik:

String script = readSource("scripts/base.js");
CompilerEnvirons env = new CompilerEnvirons();
ClassCompiler classCompiler = new ClassCompiler(env);
Object[] classes = classCompiler.compileToClassFiles(script, "scripts/base.js", 1, "Base");

I tyle! Funkcja readScript(..) wczytuje oczywiście z pliku kod skryptu. Wówczas tablica obiektów classes zawiera nazwy klas oraz skompilowane wersje binarne następujące kolejno po sobie. Wystarczy już tylko wpisać dane do odpowiednich plików *.class:

for(int i=0; i < classes.length; i+=2) {
 String name = (String)classes[i];
 byte[] bytes = (byte[])classes[i+1];

 File out = new File("scripts/" + name + ".class");
 FileOutputStream os = new FileOutputStream(out);
 os.write(bytes);
 os.close();
}

Wersja binarna może być już łatwo odczytana przez Javę:

URL[] urls = new URL[] { new File("/path/to/scripts").toURI().toURL() };
URLClassLoader loader = new URLClassLoader(urls);
Class cl = Class.forName("Base", true, loader);

Na koniec aby wykonac skrypt można utworzyć obiekt przy pomocy klasy oraz wywołać funkcję exec():

Script scr = (Script) cl.newInstance();
Context cx = Context.enter();
Scriptable scope = cx.initStandardObjects();
scr.exec(cx, scope);

Na zakończenie powiem tylko, że czasowo kompilacja kilku skryptów nie robi praktycznie żadnej różnicy w działaniu programu. Jest to zatem duży plus dla silnika JavaScriptu od Mozilli 🙂 Warto również dodać, że nie traci się spejcalnie na funkcjonalności ponieważ z poziomu skryptów JS można również korzystać z klas Javy 😉

Później napiszę jeszcze jak ze skryptu wywoływać konkretne funkcje, bo to w sumie nadaje się na osobny wpis 😉

Co jest najważniejsze w programowaniu?

Regedit na swoim blogu napisał notkę, w której podał adres ciekawego artykułu na temat etapów przejściowych dotyczących każdego programisty.

Wiele prawdy jest we wspomnianym artykule, jednak wydaje mi się, że to czy ktoś da się złapać w pułapkę czy nie zależy jedynie od niego samego. Tarczą obronną powinna być skromność, chęć do dalszej nauki oraz doskonalenia technik. Z programowaniem jest jak z majsterkowaniem. Pierwsza rzecz to posiadanie narzędzi (technik), a druga to ich umiejętne wykorzystanie. Niestety umiejętność taką nabywa się z całymi tysiącami linii złego kodu 😉

Jeszcze innym problemem jest rozumienie swojej pracy. Każdy człowiek, który ma do wykonania jakąkolwiek pracę, powinien zawsze pamiętać o tym co jest celem tej pracy. Inaczej możemy osiągnąć coś takiego:

Powszechne jest przekonanie o tym, że programista pisze kod. Nie jest to jednak prawda. Programista pisze program. Dla ludzi. Często nie związanych ani trochę z informatyką. Wydaje mi się, że często bywa tak, że koder poświęca więcej uwagi temu aby kod był piękny niż temu, żeby tworzone narzędzie było użyteczne. Oczywiście nie mówię, że jakikolwiek kod jest dobry 🙂 Kod powinien być tak dobry jak się da. Problem w tym aby wybrać złoty środek. Taki złoty podział pomiędzy jakością kodu, a jakością programu, ponieważ nie zawsze idą one w parze 🙂

Wracając do wspomnianego na początku artykułu: nie mogę się jednoznacznie przyporządkować do żadnej z tych grup. Pewnie, że niektóre opisane tam rzeczy brzmią znajomo 😉 Mimo to problem autora leży w pewności siebie. Ja nigdy takiej pewności nie miałem i zawsze podchodziłem do swoich rozwiązań z pewną nieufnością sądząc, że da się to zrobić lepiej. Oczywiście zazwyczaj miałem rację 😉 Patrzę teraz na kod pisany zaledwie rok czy dwa lata temu i już widzę, że był słaby. Zapewne patrząc za rok na kod, który piszę teraz również zobaczę, że można było lepiej. No cóż… Taka już rola różnych doświadczeń 😉