Abstract
В этой статье описывается архитектура генератора кода на основе шаблонов.
Проект
Этот проект я осуществил, когда работал в InLine Software, она же JFX, она же ObjectVenture. См. Модель-Представление-Характеристика.
Задача
Одним из элементов системы был генератор кода на основе шаблонов.
Сложности
Нашему инструменту необходимо было поддерживать несколько мета-моделей требовавших генерации исходного кода. Среди этих моделей была то, которую мы назвали Extended-J2EE, которая расширила модель программирования J2EE такими понятиями, как Отношение, Событие, Ограничение, Объект-значение и т. д. Другая была абстрактная модель, основанная на Шаблонах дизайна. Каждый шаблон дезайна был реализован с помощью набора шаблонов для генерации кода, а также Характеристик (опять же, см. Модель-Представление-Характеристика. Моя работа заключалась в разработке основы для этой части системы.
Решение
Я разработал язык программирования JCML, который использовался для кодирования шаблонов исходного кода. Шаблон состоял из элементов, привязанных к Features (характеристикам) модели. Плагины могли предоставлять свои дополнения или замены для стандартных шаблонов, которые мы включали в систему. Интерпретатор JCML, который я разработал, разрешал возможные конфликты между различными поставщиками программ для генерации кода и выполнял фактическую генерацию кода. Интерпретатор вызывался реализациями соответствующих Features или FeatureSets.
Вот пример программы на JCML. Это стандартный шаблон для Объекта-значения, который представляет собой обычный JavaBean, который имитирует API EJB EntityBean. Эта программа создает код как для интерфейса так и для его реализации Объекта-значения.
Переменные, упомянутые в шаблоне: className, attributeList и т. д., являются аттрибутами управляющей Feature.
<?xml version="1.0"?> <jcml package="com.inline.j2ee.enhanced" model="J2EE"> <feature type="ejb1.1-valueObject"> <use-if path="../codeStyle" value="standard"/> <item name="interface"> <copyright/> <s-package-declaration>className</s-package-declaration> import java.io.Serializable; import java.util.*; /** * <s-unqualified-name>className</s-unqualified-name> is an * object that can hold a data snapshot of the <s>beanName</s> * EJB. * * @author <author/> * @version <version/> */ interface <s-unqualified-name>className</s-unqualified-name> { <for var="a" path="attributeList"> <if-equal path="a/included" value="true"> <then> public <s>a/type</s> <s>a/getterName</s>(); <if-equal path="a/editable" value="true"> <then> public void <s>a/setterName</s>( <s>a/type</s> <s>a/name</s>); </then> </if-equal> </then> </if-equal> </for> } </item> <item name="implementation"> <copyright/> <s-package-declaration>implClassName</s-package-declaration> /** * <s-unqualified-name>implClassName</s-unqualified-name> * is an implementation of the ValueObject interface * <s>className</s>. */ public class <s-unqualified-name>implClassName</s-unqualified-name> implements <s>className</s> { <for var="a" path="attributeList"> <if-equal path="a/included" value="true"> <then> private <s>a/type</s> <s>a/name</s>; </then> </if-equal> </for> <for var="a" path="attributeList"> <if-equal path="a/included" value="true"> <then> public <s>a/type</s> <s>a/getterName</s>() { return <s>a/name</s>; } public void <s>a/setterName</s>( <s>a/type</s> <s>a/name</s>){ this.<s>a/name</s> = <s>a/name</s>; } </then> </if-equal> </for> } </item> </feature> </jcml>
JCML успешно использовался для реализации расширенной модели J2EE, а также шаблонов дизайна. После того, как я оставил Inline/JFX, Билл Уиллис и другие расширили JCML с помощью PCML (языка разметки кода шаблона).
[ПОПРАВКА: февраль 2021 года. В Google я использовал систему шаблонов, которая следует аналогичной парадигме, Google Closure Templates или Soy. См. несколько примеров для сравнения. Я не имел никакого отношения к созданию Soy, просто эта система вызвала воспоминания пятнадцатилетней давности.]