GSP

GSP - это генератор текста по шаблонам gsp.

Генератор по шаблонам gsp принципиально устроен так же, как и другие общеупотребительные генераторы: берется текст на некотором входном языке и на его основе генерируется текст. Входной язык поддерживает подстановку значений переменных, операторы ветвления, циклы и т.д.

В качестве входного языка шаблонов используется формат GSP (Groovy Server Pages) из проекта http://grails.org для обеспечения нормального редактирования в IDE, которые поддерживают gsp.

Синтаксис шаблонов gsp

Текст шаблона представляет собой тело метода onRender() класса jandcode.core.web.gsp.BaseGsp на языке groovy.

Парзер gsp преобразует текст шаблона в текст на groovy по следующим правилам:

  • ANYTEXT преобразуется в out('ANYTEXT') (вывести фрагмент текста)
  • ${ANYTEXT} преобразуется в out(ANYTEXT) (вывести значение выражения)
  • <%ANYTEXT%> преобразуется в ANYTEXT (вставить фрагмент groovy-кода)
  • <%=ANYTEXT%> преобразуется в out(ANYTEXT) (вывести значение выражения)
  • <%--ANYTEXT--%> преобразуется в /*ANYTEXT*/ (коментарий)
  • %{--ANYTEXT--}% преобразуется в /*ANYTEXT*/ (коментарий)
  • <xx:yy/> преобразуется в outTag('xx/yy') (вызов функции outTag)
  • <xx:yy atr="value" atr2="value2"/> преобразуется в outTag('xx/yy',atr:value,atr2:value) (вызов функции outTag)
  • <xx:yy atr="value" atr2="value2"> body </xx:yy> преобразуется в outTag('xx/yy',atr:value,atr2:value) { body } (вызов функции outTag с closure в качестве тела)

Каждая строка шаблона преобразуется в строку метода, что позволяет однозначно идентифицировать место возникновения ошибки в шаблоне (в отличии от оригинального gsp, где нет соответствия между сгенерированным классом и оригинальным шаблоном).

К примеру, из такого шаблона:

<%@ page import="jandcode.commons.datetime.*; jandcode.commons.*; jandcode.commons.groovy.*" %>
<%
  def d = XDateTime.create("2011-11-23")
  def func1 = { p1 ->
    out("P1=${p1}")
  }
%>
<div>
  Hello! Date: ${d}
</div>

<% for (i in 1..5) { %>
item: ${i}
<% } %>

<jc:tag1 arg1="1" arg2="${d}">
  body1
  <% func1("one") %>
</jc:tag1>

End

генерируется вот такой класс:

import jandcode.commons.datetime.*; import jandcode.commons.*; import jandcode.commons.groovy.*;
public class G__gsp_syntax1__app1___inc__90b3e070f3b3653b5d4cfc9b2c61e144 extends jandcode.core.web.gsp.BaseGsp {
protected void onRender() {


 def d = XDateTime.create("2011-11-23")
 def func1 = { p1 ->
   out("P1=${p1}")
 }

;;out("<div>\n");
;;out("  Hello! Date: ");;;out(d);;;out("\n");
;;out("</div>\n");
;;out("\n");
for (i in 1..5) {
;;out("item: ");;;out(i);;;out("\n");
}
;;out("\n");
outTag('jc/tag1' ,arg1:"1" ,arg2:d) {
;;out("  body1\n");
func1("one")
}
;;out("\n");
;;out("End\n");

}
}

обработка которого приведет к такому результату:

<div>
  Hello! Date: 2011-11-23
</div>

item: 1
item: 2
item: 3
item: 4
item: 5

tag1 out with: arg1=1, arg2=2011-11-23
End

В этом примере jc/tag1 - это gsp с текстом:

tag1 out with: arg1=${args['arg1']}, arg2=${args['arg2']}

Локальные переменные в шаблоне

Т.к. текст шаблона преобразуется в текст метода groovy, то определение переменной внутри фрагментов кода, определяет локальную переменную метода:

<%
    // локальные переменные метода шаблона
    String s = ""
    def a = []
%>

Для определения функций внутри шаблона изпользуются groovy closure (т.к. метод внутри метода объявить нельзя):

<%
    def pw2 = {a->
        return a*a
    }
%>
5^2 = ${pw2(5)}

Для определения рекурсивной функции внутри шаблона, необходимо сначала определить переменную для функции, а потом присваивать ей closure (особенность groovy):

<%
    def recursiveFunc
    recursiveFunc = {a->
       recursiveFunc(a+1)
    }
%>