Wednesday, 2 September 2009

FreeMarker SimpleHash problem

Обработка шаблонов (Template processing) довольно часто встречается в нынешнее время в разнообразных проектах. Некоторые языки программирования даже совмещают написание логики с "шаблонизатором", что правда зачастую затрудняет поддержку логики. Я же в последнее время пользуюсь открытой библиотекой FreeMarker, которая не привязана к конкретным (особенно веб-ориентированым) технологиям и представляет удобный инструмент и богатый шаблонный язык разметки для Java проектов.

Подробнее о FreeMarker можно узнать по приведенной ссылке, а я сегодня хотел остановиться на некоторой особенности данной библиотеки. Работа с фримаркером начинается с настройки конфигурации, подготовки модели данных и собственно шаблона. Фримаркер - это инструмент отображения вашей модели, потому каждый объект модели оборачивается в специальный класс-обёртку, который не позволяет изменять модель данных из шаблона, а также закрывает доступ к небезопасным методам, которые могут кардинально изменить ход исполнения вашего приложения. Обёртками управляет инстанс ObjectWrapper класса.
Собственно проблема: java.util.Map оборачивается в обёртку, которая оперирует ТОЛЬКО ключами типа String. И если вдруг в Вашей модели попадается что-то вроде java.util.Map<Integer, SomeObject> - ну что ж Вам не повезло... Из данной ситуации есть выход - сменить ObjectWrapper (в поставке от FreeMarker их два) , либо написать свою реализацию, что немного затратно и неудобно. Смена фабрики обёрток ни к чему хорошему не приводит - появляются другие проблемы. Для знакомых с синтаксисом фримаркера:

<#list map?keys as key>
${key}=${map[key]}
<#list>

Начинает возвращать не только ключи мапы, а и названия методов, что очень не удобно для итерирования и естественно выдаёт исключение в ${map[key]}

Выход оказался вполне удобоваримый: исключительно настройками можно добиться нужного поведения:


this.configuration = new Configuration(); this.configuration.setDefaultEncoding("UTF-8");
this.configuration.setURLEscapingCharset("UTF-8"); this.configuration.setBooleanFormat("true,false");
this.configuration.setDateFormat( DateFormatUtils.ISO_DATETIME_FORMAT.getPattern() );
this.configuration.setWhitespaceStripping(true);
this.configuration.setLocalizedLookup(true);
this.configuration.setClassForTemplateLoading(this.getClass(), "/your/package/here");

if ( null != autoincludes )
this.configuration.setAutoIncludes(autoincludes);

BeansWrapper objectWrapper = BeansWrapper.getDefaultInstance();
objectWrapper.setStrict(true);
objectWrapper.setExposeFields(true);
objectWrapper.setSimpleMapWrapper(true);
this.configuration.setObjectWrapper(objectWrapper);


Собственно основное внимание должно быть уделено использованию BeansWrapper и objectWrapper.setSimpleMapWrapper(true);
После этого итерирование по мапе с ключами в виде чисел сводиться к немного изменённому синтаксису (обратите внимание на использование круглых скобок вместо квадратных):

<#list map?keys as key>
${key}=${map(key)}
<#list>

item

No comments:

Post a Comment