Kategorie
java Junior Developer

StringTemplate, czyli proste szablony tekstowe

Nowy projekt to zwykle dobry czas na przejrzenie różnych technologii. Szablony tekstowe w postaci StringTemplate czekały sobie już dobre parę lat na półce do przetestowania. I tak czekał i czekał i … I nigdy nie było dobrej okazji, żeby z niego skorzystać. Aż do dziś.

Szablony tekstowe?

Każdy programista z szablonami tekstowymi spotykał się nie raz. Prosty przykład:

Welcome <name> to the World of <world>

Generalnie problem polega na tym, ze mamy pewien szablon tekstu i chcielibyśmy, żeby ten szablon został wypełniony naszymi zmiennymi. W powyższym przykładzie fajnie by było, gdybyśmy mogli podstawiać sobie zamiast name i world nasze wartości. Takie podstawienie nazywamy interpolacją tekstu (ang. string interpolation).

Nasuwają się pytania. Dlaczego tego potrzebujemy? Czy nie wystarczy np. konkatenacja Stringów, czy też skorzystanie z metody String.format(…)?

Odpowiedź – oczywiście czasem wystarczy, a czasem nie. A gdy przyjdzie nam się spotkać z jakimś dużym szablonem, np. stroną HTML, to … zdecydowanie nie wystarczy. Nie będzie to ani estetyczne, ani efektywne do kodowania. A nasz kod będzie wyglądał raczej jak puszka pandory.

StringTemplate na ratunek

Narzędzia do prostych problemów powinny być proste. I właśnie taki jest StringTemplate. Zobaczmy kilka przykładów.

Przykład #1 Chcielibyśmy szybko podstawić atrybut language w szablonie. Nic prostszego. Tworzymy szablon ST i dodajemy do niego wartość naszego atrybutu language za pomocą add(). Wynik podstawienia otrzymujemy za pomocą metody render().

  @Test
  public void simpleInterpolation() {
    ST template = new ST("Ready for <language> programming language!");

    template.add("language", "Java");
    String result = template.render();

    assertEquals("Ready for Java programming language!", result);
  }

Przykład #2 Tym razem chcielibyśmy przekazać do silnika szablonów bardziej złożony obiekt. Params zawiera dwa pola, tj. left i right. Przekazujemy ten obiekt jako atrybut obj i StringTemplate zrobi już resztę jak w przykładzie #1.

  @Test
  public void interpolationWithParamsObject() {
    Params params = new Params("Left", "Right");
    ST template = new ST("Ready for <obj.left> and <obj.right>");

    template.add("obj", params);
    String result = template.render();

    assertEquals("Ready for Left and Right", result);
  }

  public class Params {
    private String left;
    private String right;

    public Params(String left, String right) {
      this.left = left;
      this.right = right;
    }

    public String getLeft() {
      return left;
    }

    public String getRight() {
      return right;
    }

  }

Przykład #3 Załóżmy tym razem, że chcielibyśmy nasze szablony trzymać oddzielnie, nie wpisywać ich do kodu. W typowym projekcie mamy katalog resources. W nim możemy dodać sobie katalog templates, do którego wrzucimy plik welcome.st:

welcome(name, world) ::= "Welcome <name> to the World of <world>"

Powyżej mamy formalną definicję szablonu welcome, który przyjmuje dwa atrybuty name i world. Zobaczmy, jak można z tego skorzystać w kodzie:

  @Test
  public void classPathNamedTemplate() {
    STGroup group = new STGroupDir("templates");
    ST classPathNamedTemplate = group.getInstanceOf("welcome");
    classPathNamedTemplate.add("name", "Neo");
    classPathNamedTemplate.add("world", "Matrix");

    String result = classPathNamedTemplate.render();

    assertEquals("Welcome Neo to the World of Matrix", result);
  }

Pomaga nam klasa STGroupDir. Dzięki niej wczytywane są szablony leżące w podkatalogu templates w classpath (czyli typowo katalog projektowy resources/templates).

Kiedy mamy taką grupę szablonów zaczytaną, wówczas możemy posłużyć się jej motodą getInstanceOf i pobrać interesujący nas szablon. Dalej operacje przebiegają podobnie, jak w poprzednich przykładach.

Przykład #4 Ok, ja zwykle nie jestem takim formalistą jeśli chodzi o szablony i nie chcę korzystać z tej formalnej definicji, jak w przykładzie #3. Wolę sam kod szablonu. Tym razem niech będzie to raw-welcome-to-template.st:

Welcome <name> to the World of <world>

Różnica podstawowa, nie ma nazwy szablonu i listy argumentów na początku. Jest sam szablon, który nas interesuje.

  @Test
  public void classPathRawTemplate() {
    STGroupDir group = new STRawGroupDir("templates");
    ST classPathNamedTemplate = group.getInstanceOf("raw-welcome-to-template");
    classPathNamedTemplate.add("name", "Gandalf");
    classPathNamedTemplate.add("world", "Moria");

    String result = classPathNamedTemplate.render();

    assertEquals("Welcome Gandalf to the World of Moria", result);
  }

I tu możemy zastosować STRawGroupDir. Dzięki tej klasie nazwa naszego szablonu nazywa się jak plik, w którym się znajduje. Reszta operacji podobna, jak wcześniej. Prawda, że proste?

Co jeszcze może StringTemplate

StringTemplate jest prosty, co widać w powyższych przykładach. To wcale nie oznacza, że nie ma również trochę bardziej zaawansowanych funkcji.

Jeśli przyjrzymy się liście, to zauważymy, że jest w nim trochę możliwości:

  • Mamy do dyspozycji wiele wyrażeń, np. wczytywanie innych szablonów w obecnie procesowanym.
  • Zawiera w sobie kilka funkcji pomocniczych, np. sprawdzenie długości atrybutu tekstowego, pobieranie pierwszego elementu z listy.
  • Dostępne są instrukcje warunkowe, np. if, elseif, else.
  • Jest również wbudowane grupowanie szablonów.

Alternatywne rozwiązania

Jest oczywiście kilka alternatyw. Oto kilka z nich:

  • Velocity Jest to chyba pierwszy silnik szablonów, z którym się zetknąłem w Javie. Osobiście uważam, że jest już tak stary, że nie powinno się go używać. Reasumując nie polecam.
  • Freemarker pozwala na dużo. Mi się nigdy jakoś szczególnie nie podobał.
  • Thymeleaf jest bardzo przyjemnym rozwiązaniem do większych zastosowań i bez większych problemów integruje się ze Springiem.
  • Groovy Markup Templates, jeśli przy okazji korzystasz z Groovy, też fajnie współpracuje ze Springiem.

Podsumowanie

Jeśli czytałeś Java JDK 15 – co nowego? to wiesz, że w Java 15 pojawią się już na stałe String Blocks, czyli wielowierszowe Stringi. W języku nie ma jednak prostego narzędzia do obsługi szablonów tekstowych.

I tu jest m. in. miejsce dla StringTemplate, który wpasowuje się idealnie i pozwala na proste szablony tekstowe.

Daj znać, jak Ci się podoba praca ze StringTemplate. Czekam na Twój komentarz 😀

0 0 votes
Article Rating
Subscribe
Powiadom o
guest
0 komentarzy
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Jestem ciekawy, co myślisz. Dodaj komentarz na dole!x