27 marca 2013

Java EE, Scala, Play 2 i WebSphere Liberty Profile

0 komentarzy
Wielu z Was już z pewnością miało okazję zapoznać się z moimi literackimi (po)tworami, które dotykają tematyki integracji Java EE ze Scalą, a nawet uruchomienia Play Framework 2.1 na WebSphere Liberty Profile. Z moimi poczynaniami poszedłem tak daleko, że wręcz pokusiłem się o kontakt z twórcami produktów, którzy wcale nie oponowali, aby użyczyć mi trochę swojej wiedzy ku wzmocnieniu merytorycznym artykułów.

W ten sposób udało mi się stworzyć trzy artykuły z serii o roboczym tytule "Java EE, Scala, Play 2 i WebSphere Liberty Profile":
Byłbym niezwykle rad, gdyby ktoś zechciał podzielić się wrażeniami z lektury i wskazać miejsca wymagające większego wysiłku umysłowego. Kto pierwszy?

14 marca 2013

Scala 2.10.1 wydana i hard lifting

0 komentarzy
The logo of the Scala language
Właśnie zajrzałem na stronkę scala-lang.org, aby sprawdzić, co nowego i dobrego tam podają, a tu taka niespodzianka - świeżutkie wydanie Scala 2.10.1. Nie żebym czekał specjalnie, bo nie trafiłem na niedogodności w korzystaniu z poprzednich wersji, ale kiedy proszą o pobranie, grzecznie wykonuję.

Ciekawie prezentuje się lista osób, które poświęciły swój czas na rozwój tej wersji z bardzo polskobrzmiącymi nazwiskami.
  • Grzegorz Kossakowski - tego gościa miałem okazję poznać osobiście i mam wrażenie, że wszyscy go teraz znają, więc przedstawiać nie trzeba. Sądziłem, że znajdę go na stronie zespołu TypeSafe, ale niestety nie widnieje na liście, aczkolwiek na swoim profilu na LinkedIn stoi jak byk: "Scalac hacker at Typesafe". Się Grześ zahackował i zapomnieli o nim. Chwała za zasługi!
  • Hubert Plociniczak - o nim wcześniej nie słyszałem, ale ze strony można wyczytać, że "is a member of the EPFL team working on Scala.", więc trudno się dziwić, że ma swoje miejsce na liście zasłużonych. Chwała się należy!
Ostatnie moje dokonania rozwojowe ze Scalą pozwoliły mi na rozwiązanie trudnego zadania z książki Functional Programming in Scala, którą żyję prawie każdego dnia i jestem nią wprost zachwycony. Nieskromnie napiszę, że nie mniej niż swoimi dokonaniami przy jej aktywnym udziale. Przeczytałem ją tyle razy, że mógłbym ją teraz sam napisać.

"Exercises, answers, and hints to go along with the book "Functional Programming in Scala" znajdują się na GitHubie i można samodzielnie popróbować się z funkcyjnymi problemami w Scali.

Oto rozwiązanie pierwszego zadania oznaczonego jako Hard z rozdziału 2. Świadomie nie podaję treści zadania, bo liczę, że wszystko zaszyte jest w sygnaturze. Kto odgadnie, do czego służy funkcja lift3? Niezły mind twister.
object MyModule { 
  def lift[A,B,C,D](f: (B, C) => D)

                   (g: A => B,
                    h: A => C): A => D =
    a => f(g(a), h(a))

  // Exercise 11
  def lift3[A,B,C,D,E](f: (B, C, D) => E)
                      (g: A => B,
                       h: A => C,
                       i: A => D): A => E =
    a => {val f2: (C,D) => E = f(g(a),_,_)
          lift(f2)(h, i)(a)}
}
Początkowo zadanie mnie przygniotło, ale ostatecznie dałem sobie radę i to bez żadnych ściągawek (!) Wystarczyło dokładniej doczytać książkę i...pomyśleć. Jak zwykle zresztą. Teraz Twoja kolej.

09 marca 2013

Różnorodność kulturowa w Stanach - wrażenia z Chicago i Milwaukee

1 komentarzy
Wywiało mnie na drugi koniec Oceanu Atlantyckiego do Chicago i Milwaukee.


Organizacja wyjazdu trwała niewiele ponad 2 godziny (!) Szczerze powiedziawszy spodziewałem się dużo gorszego wyniku, a nawet przewidywałem całkowitą klęskę przedsięwzięcia. Zdumieniu mojemu nie było końca, kiedy wszystko domknąłem bez specjalnego stresu. W zasadzie więcej było ciekawości, co kryje przed sobą tak karkołomne załatwianie spraw w IBM i kiedy miałem w ręku rezerwacje, odetchnąłem z ulgą, że wciąż żyję - serce waliło mi z wrażeń!

A wszystko działo się przy słonecznym piątku, kiedy po rannej wizycie w biurze, zdecydowałem się na wcześniejszy powrót do domu i po malutku domykałem tydzień.

A tu bach! Jest piątek, godzina 14:00 i dostaję przez korporacyjny komunikator zapytanie o możliwość przyjazdu do Stanów. Właśnie odebrałem wizę amerykańską, więc nadarzyła się okazja przetrzeć szlaki.

Początkowo mnie ścisnęło, kiedy pomyślałem sobie, że na załatwienie spraw wyjazdowych mam kilka godzin - złożyć wniosek o wyjazd służbowy, zebrać podpisy, załatwić bilety, hotele, a wszystko przy założeniu wyjścia pracownika od spraw wyjazdowych przed 17. Sprawę włożyłem na półkę - "wyzwania".

Udało się bez specjalnego napinania. Dziękuję wszystkim zaangażowanym. Dojrzałem społecznie, bo współpraca popłaca, nawet jeśli jej wynikiem nie jest nauka kolejnej nowinki technologicznej, a zwykłe rezerwacje.

Wylot w południe następnego dnia, sobota, 2 marca bezpośrednim lotem do Chicago, IL. Miałem kartę kredytową, paszport z wizą, więc wszystko gotowe na wyjazd. Nie zapomniałem również o kompie i rzeczach.

Odprawa ekspresowa. Kolejne zdumienie. I po chwili jestem na pokładzie.

I tu zdumienie-porażka - nie ma telewizorków w siedzeniach naprzeciwko. Co ja będę robił?! Spodziewałem się, że przy przelotach ponad 6 godzin, samoloty wyposażone w telewizory w oparciach to norma. A tu takie zaskoczenie. Kolejny raz potwierdza się powiedzonko, że zawód jest wypadkową oczekiwań, a że były niemałe, więc i zawód niemały. Zabolało.

Później, podczas lotu, zapytałem o to obsługę i dowiedziałem się, że w LOTcie to dopiero Dreamlinery będą z takim wyposażeniem. Czekam zatem cierpliwie na ich przylot. Trzeba będzie uważać przy planowaniu kolejnych przelotów i zwrócić również i na to uwagę.

Zabrałem się za lekturę książki Functional Programming in Scala, więc po kilku godzinach lektury wyszło całkiem pozytywnie. W końcu miałem mnóstwo czasu na lekturę. 9 godzin rozłożyłem na czytanie, obiad, czytanie, spanie, chodzenie po pokładzie i zaczepianie kogo popadnie, śniadanie i znowu chodzenie po pokładzie. Wtedy też spotkałem polaka z Chicago, który zarabiał na życie jako kierowca, jak to się wyraził, truka (ang. truck). Nawet zapytałem go o tę wymowę i powiedział, że to polski żargon w Chicago na duże auta ciężarowe. Zrozumiałe. Nauki końca nie było widać.

Polaków na pokładzie cała masa. Zacząłem się nawet obawiać o stan osobowy w kraju przy takim zatłoczeniu na pokładzie.

Doleciałem do lotniska Chicago-O'Hare. Przysiągłbym, że na pokładzie mignął mi piosenkarz, którego nazwiska jednak nie pamiętam, a który (pewnie dla przyspieszenia przejścia granicznego) stał się nieoczekiwanie potrzebującym...wózka inwalidzkiego (!) One przechodziły przez inne bramki, do których kolejki były kilkuminutowe, a nie tak jak moje godzinne. Sposób warty wdrożenia przy następnym razie, bo kolejka do mojego przejścia koszmarna - ponad godzina oczekiwania (przy całkowitym braku dostępu do sieci, co sprawiało, że trzeba było cierpliwie stać i się rozglądać!).

I tu kolejne zaskoczenie - obsługa mówi po polsku! Aż mnie przetrąciło i nawet pomyślałem, czy przypadkiem wciąż może jestem w Polsce. Niesamowite wrażenie!

Do hotelu trafiłem międzynarodowo. Taksówkarz okazał się być pakistańczykiem z Islamabadu. Przyjechał do Stanów z rodziną i jakoś trafił do Chicago. On i ja straszne gaduły, więc przekrzykiwaliśmy się, aby dojść do głosu. Ciekawa podróż.

Przejeżdżając ulicami Chicago dostrzegłem znajomy budynek Chicago Board of Trade Building, który nie tak dawno widziałem w filmie dokumentalnym o mafiozach i ich związkach w Chicago. W końcu byłem w mieście Al Capone.

W hotelu Palmer House też mnie powaliła przestronność lobby i malowidła na suficie. Zdumienia nie było końca.


Zwiedzając hotel trafiłem na basen. Niezbyt imponujący po tym, kiedy kolega z zespołu rekomendował mi wybór tego właśnie ze względu na basen. Zapomniałem jednak, że po ostatnim zabiegu chirurgicznym pływalnia mnie nie interesuje zupełnie. Następnym razem.

Tu kolejne zaskoczenie. Po krótkiej rozmowie z panią obsługującą basen, zwróciłem uwagę na jej akcent, który wydał mi się niezwykle znajomy. Pani okazała się być bułgarką, która przyjechała do Stanów na zieloną kartę.

Wieczorny spacer po mieście był niezwykle nieprzyjemny - pobliskie jezioro Michigan nie dawało zapomnieć o swojej bliskości i wespół z zimową porą prowokowały temperaturę do zejścia poniżej zera. Było naprawdę zimno!

Po godzinnym spacerze trafiłem do pobliskiej restauracji, w której pozwoliłem sobie na zamówienie sałatki cezar z kurczakiem. I to był strzał w dziesiątkę, bo porcja okazała się być tak ogromnych rozmiarów, że zdołałem wdusić w siebie jedynie połowę. Chylę czoła przed zawodnikami, którym udaje się opanowąć całą. To musi boleć.

Na drugi dzień dołączył duńczyk, aby później połączyć siły z amerykaninem. W ten sposób w poniedziałek byliśmy już w pełnym składzie do podjęcia się wyzwań służbowych.


O pracy nie ma co wspominać. Przemilczmy. Ale wysoko było. Zresztą wiele budynków w Chicago sprawiało, że swoją wielkością ulice stanowiły swoiste kaniony. Niezapomniane wrażenie.


Jeszcze w niedzielę pozwolililiśmy sobie z duńczykiem na spacer do "pobliskiego" centrum handlowego.


Co mnie najbardziej porywało na mieście, to możliwość darmowej nauki angielskiego (wystarczyło wejść do sklepu i doskonalenie angielskiego w gratisie), a także smakowitość owoców - mango, bananów, a nawet jabłek. Co oni robią z tymi jabłkami, że są takie słodkie?! Banany i mango zrozumiałe, bo bliżej im od drzewa do sklepów, więc smakowały inaczej, ale jabłka?!

Bodaj we wtorek amerykanin zaciągnął nas na ribs - żeberka, z których Chicago słynęło.


Nie powiem, były przepyszne, ale wielkość porcji omal mnie zabiła. Jak można tyle zjeść?! Jak doświadczyłem, niektórym się udaje.

W środę wyjechaliśmy do Milwaukee, aby tam kontynuować turnee warsztatowe z WebSphere.

W trasie dotarliśmy na stację benzynową, na której, w 7 Eleven, nie mogłem oprzeć się, aby nie skosztować bananów, jabłek i Mango Smootie. Przepyszne! Co jednak zwróciło moją uwagę, to napis "Speak English or get the hell out!". Obsługa okazała się jednak być latynoska, a osoba za mną zamawiała po...meksykańsku! Nikt nie trafił do piekła (the hell).

Milwaukee jakieś "przewietrzone" - niewiele ludzi na ulicach, które swoją wielkością przypominały polskie autostrady. I te przestronne samochody.


Trafiliśmy z warsztatem na 31 piętro, na którym widok zapierał dech w piersiach (na zdjęciu, po prawej widać jezioro Michigan).


Choćby dla tego widoku warto było doskonalić się w WebSphere :-)

Milewaukee to kraina Harley-Davidsona, ale o muzeum dowiedziałem się za późno, aby wpaść na chwilę. Nie powiem, mieliśmy sporo czasu, aby to zrobić, tylko jakoś nikomu nie przyszło do głowy, aby poszukać czegoś wartościowego "na mieście". Następnym razem, jak mówią.

Powrót do Chicago był kolejną nauką życia - po raz pierwszy miałem okazję telefonicznie anulować rezerwację, aby otworzyć inną w innym hotelu, bliżej lotniska Chicago-O'Hare - tego samego, na którym lądowałem kilka dni temu. Niby nic nadzwyczajnego, ale przy hałasie w samochodzie, rozmowie i ledwosłyszalnym głosie pani z obsługi, uważam to za wyczyn.


Obługa hotelu ponownie różnorodna na maksa. Podczas śniadania zamieniłem słowo z meksykanką, którą prosiłem o wskazanie wartościowej restauracji meksykańskiej. W restauracji, której właścicielem był serb, pracowała bułgarka z zieloną kartą. A na koniec dnia miałem styczność z bośniakiem. Czyż nie nazbyt "kolorowo"?!

Mojej styczności z wielokulturowym Chicago nie było końca - taksówkarz do HIP Shopping Mall okazał się być palestyńczykiem (z Ramallah), a podczas zakupów trafiłem na polskie małżeństwo, które mieszkało na stałe w Chicago. Zwracanie uwagi na detale kulturowe w Stanach nie jest dobrym zajęciem, bo należy oczekiwać ciągłego pobudzenia.

Właśnie tutaj, w Chicago, dotarło do mnie, że Stany są jedynym znanym mi krajem, w którym żyje tak wiele różnych narodowości. Aż trudno uwierzyć, że dają radę. Mnóstwo różnorodności może przyprawić o ból głowy, ale powiedzmy, że jestem lekko zmęczony jedynie.

Pora udać się na zasłużony odpoczynek. Pospać trzeba, bo od ekscytacji serce wysiądzie.

26 lutego 2013

Android 4.1.2 na Samsung Galaxy S II - aktualizacja poszła gładko

9 komentarzy
Pamiętam, jak wypatrywałem jego nadejścia, aż w końcu się doczekałem. I nie zapomnę mojego zdumienia, kiedy wyjątkowo wcześnie rozpoczynając poranek, 22 lutego zobaczyłem powiadomienie o oczekującej aktualizacji. Nie przypuszczałem, że może to być Android 4.1.2 "Jelly Bean", bo czas aktywnego wypatrywania miałem już dawno za sobą. Przegapiłem również wiadomość o dostępnej aktualizacji, która podobno była już w sieci od 18 lutego.

I w końcu do mnie dotarło!

"I teraz co?!", pomyślałem. Grzecznie przeczytałem informacje z ekranu mojego "najulubieńszego" Galaxy S II i zacząłem zastanawiać się nad kopią bezpieczeństwa. Najbardziej szkoda było mi stracić zdjęć i nagrań, więc szybko przeniosłem je w bezpieczne miejsce. Reszta mogła iść do kosza, gdyby podczas aktualizacji doszło do skasowania zawartości telefonu. A co mi tam.


Ekranów z instrukcją aktualizacji oprogramowania nie widać było końca. Najpierw ten wyżej, później następny...


...aż w końcu pojawił się ostatni.


Podekscytowany odłożyłem jednak telefon na bok i zabrałem się za śniadanie. "Jak się człowiek spieszy, to się diabeł cieszy."



I warto było czekać. Aktualizacja przeszła gładko (podobnie jak poprzednia do ICS - Android 4.0.3), więc do tej pory nie narzekam.

Od strony użytkownika zauważyłem pewne przyspieszenie działania telefonu i jakby siła sygnału wzmocniła się. Zwolniła aplikacja do robienia zdjęć i teraz trzeba trochę dłużej odczekać zanim kamera jest gotowa do działania. Zwiększyła się liczba opcji w panelu w belce i pojawiło się kilka znaczących zmian w samym korzystaniu z systemu. Jestem zadowolony z przejścia na Jelly Bean. Polecam!

25 lutego 2013

wpis match { case BlogEntry("Klasy przypadku aka case classes") => true }

7 komentarzy
Jaki jest Twój rytuał tworzenia klas? David Pollak w swojej książce Beginning Scala (Apress, 2009) napisał, że jego obejmuje pisanie metod toString, hashCode oraz equals (wszystkie z java.lang.Object). Jako powód powołał się na Joshua Bloch, który w Effective Java (Prentice Hall, 2008) napisał, że jest to dobra praktyka, która umożliwia obiektom uczestniczyć w kontrakcie określonym przez tablice haszujące (ang. hash tables) oraz przy ich wypisywaniu. Słusznie (aczkolwiek moje skromne programowanie nie obejmowało takich szczegółów).

Może Wasze również? Jeśli tak, warto przyjrzeć się mechanizmowi klas przypadków (ang. case classes), dla których kompilator Scali generuje implementacje wspomnianych metod z pudełka oraz, co z pewnością ważniejsze, uczestniczą w mechaniźmie dopasowywania wzorców (ang. pattern matching).

Załóżmy, że mamy taką klasę X w Scali.
case class X(x: Int)
Na pierwszy rzut oka, jakby znajoma konstrukcja (szczególnie dla programistów javowych) do tworzenia definicji klas. Co rzuca się w oczy, to brak nawiasów klamrowych po definicji klasy, jeśli ciało będzie odpowiadało temu, które utworzy kompilator Scali (poniżej wyjaśnienie, czego należy oczekiwać).

Po kompilacji otrzymujemy zestaw użytecznych metod w klasie bez konieczności pisania ich własnoręcznie (!)
$ scalac X.scala

$ ls X*
X$.class X.class X.scala

$ javap -classpath . X
Compiled from "X.scala"
public class X implements scala.Product,scala.Serializable {
  public static <A extends java/lang/Object> scala.Function1<java.lang.Object, A> andThen(scala.Function1<X, A>);
  public static <A extends java/lang/Object> scala.Function1<A, X> compose(scala.Function1<A, java.lang.Object>);
  public int a();
  public X copy(int);
  public int copy$default$1();
  public java.lang.String productPrefix();
  public int productArity();
  public java.lang.Object productElement(int);
  public scala.collection.Iterator<java.lang.Object> productIterator();
  public boolean canEqual(java.lang.Object);
  public int hashCode();
  public java.lang.String toString();
  public boolean equals(java.lang.Object);
  public X(int);
}
Tworzenie obiektów klas przypadków jest możliwe bez słówka kluczowego new oraz domyślnie wszystkie parametry konstruktora stają się atrybutami obiektu jedynie do odczytu. Klasa przypadku jest niezmienna.
scala> case class X(x: Int)
defined class X

scala> val x = new X(5)
x: X = X(5)

scala> x
res0: X = X(5)

scala> val y = X(5)
y: X = X(5)

scala> y
res1: X = X(5)

scala> x == y
res2: Boolean = true

scala> x equals y
res3: Boolean = true

scala> x.equals(y)
res4: Boolean = true
I kontynuując dalej poszukiwania udogodnień związanych z klasami przypadku w Scala REPL:
scala> case class X(x: Int)
defined class X

scala> X.[wciśnij TAB]
andThen apply asInstanceOf compose isInstanceOf toString unapply        

scala> val x = X(5)
x: X = X(5)

scala> x.
asInstanceOf canEqual copy isInstanceOf productArity productElement productIterator productPrefix toString x

scala> x.x
res0: Int = 5

scala> x.toString
res1: String = X(5)

scala> x.hashCode
res2: Int = -1267080172

scala> x.equals(x)
res3: Boolean = true

scala> x.equals(X(5))
res4: Boolean = true
Siłę klas przypadku można dostrzeć przy dopasowywaniu wzorców, z którymi stanowią nierozerwalną parę w Scali.

Martin Odersky, Lex Spoon oraz Bill Venners w swojej książce Programming in Scala, Second Edition (Artima, 2011) napisali, że klasy przypadku są odpowiedzią Scali na umożliwienie dopasowywania wzorców (znanego z języków funkcyjnych) na obiektach bez konieczności pisania nużącego, ale niezbędnego kodu wspierającego. Wystarczy przed nazwą klasy napisać "case" i po krzyku.

Tak na marginesie, jakby się tak zastanowić nad potencjalną genezą nazwy "case classes", to przy znajomości dopasowywania wzorców w Scali - konstrukcja match-case - możnaby wysnuć wniosek, że one są dla siebie stworzone. Nawet nazwy je łączą. O pomyłkę trudno.
scala> X(10) match {
     |   case X(10) => "Obiekt X z 10 wewnątrz"
     |   case _ => "Coś bliżej niezidentyfikowanego"
     | }
res5: String = Obiekt X z 10 wewnątrz

scala> X(3) match {
     |   case X(num) => s"Obiekt X z ${num}"
     |   case _ => "Coś bliżej niezidentyfikowanego"
     | }
res6: String = Obiekt X z 3
I inne temu podobne, które dotyczą dopasowywania wzorców. Ciekawy mechanizm.

Na zakończenie warto wspomnieć o uproszczeniach w tworzeniu nowych obiektów na bazie istniejących z utworzoną metodą copy.
scala> case class Y(a: Int, b: String, c: X)
defined class Y

scala> val y = Y(5, "Five", x)
y: Y = Y(5,Five,X(5))

scala> y.copy(a=500)
res6: Y = Y(500,Five,X(5))

scala> y.copy(a=500, b="Five Hundred")
res7: Y = Y(500,Five Hundred,X(5))

scala> y.copy(c=X(500))
res8: Y = Y(5,Five,X(500))
Niskim kosztem (tworzenia klas przypadków) otrzymujemy stosunkowo bogatą funkcjonalność. To musi się podobać!

Warto przeczytać:

21 lutego 2013

Natchniony po lekturze Zippers in Scala

4 komentarzy
Zasiądź wygodnie i przygotuj się do kilku krótkich zadanek programistycznych w Scali. Nie powinny zająć więcej niż 10 minut (dla mniej wprawnych rąk).

Zacznij od implementacji funkcji def peak(list: List[Int]): Option[Int], która zwróci pojedynczy element listy, który jest większy niż jego sąsiedzi, np. peak(List(1, 2, 3, 5, 2)) zwróciłoby Some(5), ale już peak(List.range(0, 5)) zwróci None.

Już? Gotowe? Test przechodzi? Zatem kolejne zadanko.

Następnie przejdź do implementacji funkcji def raisePeak(list: List[Int]): Option[List[Int]], która znajdzie "peak" wyżej, ale zwróci nową listę z tym elementem zwiększonym o jeden.

Już? Nie za szybko?! :-)

Na pewno dostrzegłeś, że funkcja raisePeak znacząco się skomplikowała i to może być ten moment, kiedy zechcesz wejść w świat struktury danych zwanej Zipper. Zanim jednak zdecydujesz się na własną implementację w Scali proponuję zajrzeć do wpisu Zippers in Scala. Ciekawie poprowadzony "wykład", w którym znajdziesz nie tylko rozwiązania tych dwóch problemów powyżej, ale również daje pogląd na problem, który rozwiązują zipper'y.

"Zippers in Scala" nazwałbym inspirującą lekturą, która kształtuje mój światopogląd scalowy. Mnie natchnął do dalszego zgłębienia tematu zipper'ów. Ktoś już nad tym ślęczał? Jakie wrażenia?

20 lutego 2013

Pierwszy projekt w Scali - obróbka XMLi przy migracji z WLI do IBM BPM

3 komentarzy
Chwała każdemu, komu udaje się dopiąć swego i wdrożyć nowy język w projekcie, aby produkt ciężkiej pracy umysłowej komuś ułatwił życie (niechby to był sam klient, albo koledzy programiści z zespołu).

A niechby to był taki malutki projekcik, jak ten mój, dzisiejszy do obrabiania XMLi. Nic nadzwyczajnego, ale czego oczekiwać od nowicjusza scalowego, który karierę w tym języku liczy w dniach (a nie tygodniach, czy miesiącach)?! Każdy przecież kiedyś był początkujący w rzeczach, w których teraz wiedzie prym. Ja właśnie zaczynam swoje pierwsze kroki w Scali i twory przypominają prawdziwe potwory, ale od czegoś zacząć należy!

Wierzę, że z pomocą Grześka, teamon'a oraz dmilith'a nauka Scali będzie tylko przyjemnością!

Pora na odsłonę mojego dzieła. Komentarze mile widziane.
package pl.japila.wli.transformations.control

import scala.xml.{ XML, Node, Elem }
import scala.xml.transform.{ RuleTransformer, RewriteRule }

object WLIControlMigrationMain {
  // FIXME: Remove it once the script reads input args or we find them a better place
  val pkg = "pl.japila.wli.transformations.control".replace(".", "/")
  
  def main(args: Array[String]) {
    // The file comes from the BPEL Exporter in WLI
    val processCtrlXml = XML.load(getClass.getResourceAsStream(s"/${pkg}/process_ctrl.wsdl"))
    val portType = (processCtrlXml \ "portType" \ "@name").text

    // The file is ours - the template for a JMS import
    val importXmlTemplate = XML.load(getClass.getResourceAsStream(s"/${pkg}/import-xml.template"))
    // The file name and the name of the import component must be alike
    val importComponentName = portType + "_MB_Publish_control"
    println(s"+++\n+++ Save the following file as ${importComponentName}.import:\n+++")
    println(importXmlTemplate.toString
      .replace("{importComponentName}", importComponentName)
      .replace("{portType}", portType))
    println(s"+++\n+++ Save the following file as process.component:\n+++")
    wireComponentWithImport(importComponentName)
  }

  def wireComponentWithImport(importComponentName: String) {
    val processComponentXML = XML.load(getClass.getResourceAsStream(s"/${pkg}/process.component"))
    val wire = XML.loadString(s"<wire target='${importComponentName}'/>")
    val wiredProcessComponentXML = new RuleTransformer(
                                     new AddChildrenTo("reference", wire))
                                       .transform(processComponentXML).head
    println(wiredProcessComponentXML)
  }

  // http://stackoverflow.com/questions/2199040/scala-xml-building-adding-children-to-existing-nodes
  def addChild(n: Node, newChild: Node) = n match {
    case Elem(prefix, label, attribs, scope, child @ _*) =>
      Elem(prefix, label, attribs, scope, child ++ newChild: _*)
    case _ => sys.error("Can only add children to elements!")
  }

  class AddChildrenTo(label: String, newChild: Node) extends RewriteRule {
    override def transform(n: Node) = n match {
      case Elem(_, `label`, _, _, _*) => addChild(n, newChild)
      case _ => n
    }
  }
}