| Základní článek Eclipse |

Souhrn
Ve druhé části tohoto výukového programu JET (Java Emitter Templates) se podíváme na rozhraní API stroje JET. Naučíte se psát moduly plug-in, které pomocí tříd v balíku JET generují zdrojový kód Java.
Jako příklad z reálného světa vytvoříme modul plug-in, který přijme uživatelský vstup a vygeneruje třídu výčtu metody typesafe. Vygenerovaný zdrojový kód je založený na šabloně JET, kterou lze distribuovat s modulem plug-in, což umožňuje uživatelům modulu plug-in upravit vygenerovaný kód úpravou šablony.
Tento článek nabízí rovněž stručné informace o rozhraní JET API.
Příspěvek poskytl Remko Popma, Azzurri Ltd., remko.popma@azzurri.jp, 26. srpna 2003. Použito s oprávněním. Poslední aktualizace: 3. ledna 2007.
Úvod
Některé třídy JET
Modul plug-in, který generuje zdrojový kód
Závěr
Dodatek
Prostředky
|
Překlad a generování Na šablonách JET je zprvu matoucí to, že generování textu probíhá ve dvou krocích: překlad a generování. Prvním krokem je překlad šablony na implementační třídu šablony. Druhý krok pomocí této implementační třídy šablony vygeneruje text. Je-li cílem vaší práce s JET vygenerování zdrojového kódu Java, může být matoucí, že výsledkem kroku překladu šablony je rovněž zdrojový kód Java. Nezapomeňte, že tento zdrojový kód není vygenerovaný text. Zdrojový kód, který je výsledkem kroku překladu je jednoduše jinou formou šablony. Pokud jste již někdy používali stránky JSP a servlety, můžete si představit šablonu JET jako ekvivalentní ke stránce JSP. Šablona JET se přeloží na implementační třídu šablony, stejně jako se stránka JSP přeloží na servlet. Druhý krok, ve kterém implementační třída šablony generuje text, je ekvivalentní vytvoření a vrácení kódu HTML servletem. |
V první části tohoto výukového programu jsme si představili šablony JET a vysvětlili postup převedení projektu na projekt JET tak, aby tvůrce JET Builder automaticky překládal šablony ve vašem projektu na implementační třídy šablony.
Ve druhé části tohoto výukového programu se zaměříme na zápis modulu plug-in, který pomocí tříd v balíku JET vygeneruje zdrojový kód Java. Modul plug-in, který generuje text ze šablony JET, již nemůže při automatickém překladu šablon spoléhat na charakter JET a tvůrce JET Builder. Je tomu tak proto, že charakter JET a tvůrce JET Builder lze použít pouze na projektech pracovního prostoru, nikoli na modulech plug-in. Moduly plug-in musí své šablony překládat pomocí tříd v balíku JET.
Chcete-li spustit příklad, nebo zobrazit zdroj kódu tohoto článku, můžete rozbalit soubor org.eclipse.emf.examples.jet.article2_2.3.0.zip do podadresáře plugins. Chcete-li použít ukázkový modul plug-in, musíte mít nainstalován rámec EMF. Já používám verzi 2.3.0 M4.
Následující oddíl pojednává o některých třídách v balíku org.eclipse.emf.codegen. Podíváme se na jednotlivé kroky při generování zdrojového kódu pomocí JET a na to, jak do nich zapadají třídy stroje JET. Toužíte-li po ukázce kódu, který ukazuje použití těchto tříd v praxi, můžete přejít přímo na oddíl Modul plug-in, který generuje zdrojový kód.
V tomto oddílu se blíže podíváme na některé z tříd v balíku JET. Lze je přibližně rozdělit na dvě skupiny:
Třídy na nižší úrovni nejsou v tomto článku podrobně pojednány. Popis všech tříd v modulu plug-in org.eclipse.emf.codegen viz oddíl Přehled rozhraní JET API níže. Ve zbývající části tohoto oddílu se zaměříme na několik tříd na vyšší úrovni.
Třída JETCompiler je hlavní třídou pro překlad šablon. Tato třída zodpovídá za překlad šablon do zdrojového kódu Java implementační třídy šablony. Skutečný překlad je delegován na další třídy ve stejném balíku. Klienti vytvoří objekt JETCompiler pro konkrétní šablonu a poté voláním metody parse následované metodou generate zapíší zdrojový kód Java pro výslednou implementační třídu šablony do určeného proudu.
Třída JETEmitter poskytuje uživatelům balíku JET pohodlné rozhraní API vysoké úrovně. Metoda generate této třídy kombinuje překlad šablony a generování textu do jednoho kroku. Tato třída JETEmitter se stará o podrobnosti při překladu šablon a kompilaci zdrojového kódu Java přeložené implementační třídy šablony, a vy se tak můžete zaměřit na konečný výstup generátoru.
Na třídu JETEmitter lze pohlížet i tak, že odstraňuje krok překladu, a vy se tak můžete domnívat, že pomocí šablony přímo generujete text. V souladu se Zákonem nespolehlivých abstrakcí se nám však nedaří tomu vždy uniknout a oddíl Nástrahy třídy JETEmitter poukazuje na několik míst, u kterých je třeba být opatrný.
Třídu JETEmitter budeme používat v našem modulu plug-in, proto se jí zde budeme věnovat trochu podrobněji.
Objekt JETEmitter je sestaven s identifikátorem URI šablony použité ke generování textu. Přijatelný je libovolný typ identifikátoru URI, pokud je dostupný popisovač protokolu. To znamená, že lze použít identifikátory URI file:/, identifikátory URI ftp:/ i identifikátory URI http:/. Platforma Eclipse přidává zvláštní popisovače protokolu pro identifikátory URI platform:/base/, platform:/plugin/, platform:/fragment/ a platform:/resource/, takže moduly plug-in mohou k určení souboru šablony použít například identifikátor URI platform:/resource/myproject/myfolder/mytemplate.jet. Poznámka: Ve verzi Eclipse 3.0 byl zaveden do seznamu speciálních protokolů protokol bundleentry. Ten by se měl používat v odkazech na prvky Eclipse, jako např. moduly plug-in a funkce.
V našem příkladu modulu plug-in budeme rozdělovat soubor šablony spolu s modulem plug-in, takže soubor šablony bude umístěn ve složce myplugin/templates pod složkou Eclipse plugins. K vyhledání a vygenerování šablony z této složky lze pak použít tento kód:
String pluginId = "myplugin.id";
String base = Platform.getBundle(pluginId).getEntry("/").toString();
String uri = base + "templates/myTemplate.javajet";
JETEmitter emitter = new JETEmitter(uri);
String generatedText = emitter.generate(new Object[] {parameter});
Po zkonstruování objektu JETEmitter na něm pak klienti zavolají metodu generate, a tím vygenerují text. Metoda generate provede následující kroky:
* .JETEmitters je výchozí název projektu vytvořeného během překladu šablony. Tuto hodnotu lze změnit metodou setProjectName.
Náš ukázkový modul plug-in použije třídu JETEmitter a uloží vygenerovaný text do zdrojového souboru Java v pracovním prostoru. Na níže uvedeném obrázku jsou zobrazeny kroky při generování zdrojového kódu pomocí třídy JETEmitter.
Třída JETEmitter kombinuje překlad šablony a generování textu do jednoho kroku, což z ní činí velmi užitečný nástroj. Je však důležité vědět, co se děje uvnitř, jinak byste se mohli dočkat některých nepříjemných překvapení. Tento oddíl zdůrazňuje některá "úskalí", na která jsem narazil, abyste se nemuseli dopustit stejných chyb.
Použití JET mimo platformu Eclipse není snadné. JET je určeno ke spouštění pouze jako aplikace pracovního prostoru. Všechny aplikace, které používají JET, musí být spuštěny minimálně jako aplikace Eclipse "bez konzoly", aby došlo k inicializaci modulu plug-in. (Výraz bez konzoly odkazuje na spuštění platformy Eclipse bez uživatelského rozhraní.)
To znamená, že použití třídy JETEmitter z jednoduché samostatné aplikace (standardní třída Java s metodou main) nebude fungovat:
// Toto se nezdaří: třídu JETEmitter nelze použít ze samostatné aplikace
public static void main(String[] args) {
JETEmitter emitter = new JETEmitter("/myproject/templates/HelloWorld.txtjet");
// toto vrátí výjimku NullPointerException
String result = emitter.generate(new NullProgressMonitor(), {"hi" });
System.out.println(result);
Všimněte si, že toto omezení se netýká pouze třídy JETEmitter, mnohé z tříd v modulu plug-in org.eclipse.emf.codegen závisejí na dalších modulech plug-in. Další podrobnosti o použití JET ze samostatných aplikací jsou uvedeny níže v oddílu Příloha.
Ve zbývající části tohoto článku budeme předpokládat, že náš kód je spuštěný zevnitř modulu plug-in.
Při předání vlastního prvku metodě JETEmitter.generate formou argumentu můžete obdržet chybu NoClassDefFoundError. Může k tomu dojít, pokud objekt předávaný jako argument není jednou z tříd Java "bootstrap" (třídy bootstrap jsou běhové třídy v souboru rt.jar a třídy internacionalizace v souboru i18n.jar).
Chcete-li zabránit této chybě, musíte při použití třídy JETEmitter určit zavaděč tříd vašeho modulu plug-in. Není-li určen žádný zavaděč tříd, třída JETEmitter použije zavaděč tříd své vlastní třídy, což je obvykle zavaděč tříd pro modul plug-in org.eclipse.emf.codegen, a tento zavaděč tříd toho příliš nevidí. V posledních verzích EMF (od verze 1.1.0 sestavení 20030527_0913VL) má třída JETEmitter konstruktor, který přijímá argument zavaděče tříd.
Pamatujte, že dalším způsobem, jak určit zavaděč tříd, je vytvoření podtřídy JETEmitter ve vašem vlastním projektu. Pokud není určen žádný zavaděč tříd, třída JETEmitter použije zavaděč tříd této podtřídy. (Používáte-li starší verzi EMF, neexistují žádné konstruktory, které by přijímaly argument zavaděče tříd, a proto nebudete mít jinou možnost, než vytvořit v projektu podtřídu JETEmitter.)
Níže uvedený příklad zobrazuje třídu akce, která přeloží a vyvolá vybranou šablonu pomocí třídy JETEmitter. Příklad ukazuje, jak lze třídu JETEmitter konstruovat
pomocí parametru zavaděče tříd nebo pomocí
konstruování anonymní podtřídy.
package org.eclipse.emf.examples.jet.article2.actionexample;
// importy vynechány
public class EmitAction implements IActionDelegate {
protected ISelection selection;
public void selectionChanged(IAction action, ISelection selection) {
this.selection = selection;
action.setEnabled(true);
}
public void run(IAction action) {
List<?> files = (selection instanceof IStructuredSelection)
? ((IStructuredSelection) selection).toList()
: Collections.EMPTY_LIST;
for (Iterator<?> i = files.iterator(); i.hasNext();) {
IFile file = (IFile) i.next();
IPath fullPath = file.getFullPath();
String templateURI = "platform:/resource" + fullPath;
ClassLoader classloader = getClass().getClassLoader();
JETEmitter emitter = new JETEmitter(templateURI, classloader);
// nebo použijte anonymní podtřídu
// emitter = new JETEmitter(templateURI) {}; // všimněte si závorek
try {
IProgressMonitor monitor = new NullProgressMonitor();
String[] arguments = new String[] { "hi" };
String result = emitter.generate(monitor, arguments);
saveGenerated(result, file);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
// metoda saveGenerated vynechána
}
Třída JETEmitter přeloží vaše šablony na zdrojové soubory Java v projektu .JETEmitters a vyvolá tvůrce JavaBuilder, který tyto zdrojové soubory zkompiluje. Pokud vaše šablony používají třídy, které nejsou standardní třídy Java nebo nejsou v modulu plug-in EMF, budete muset přidat tyto třídy do cesty ke třídě projektu .JETEmitters, jinak nebude moci tvůrce JavaBuilder zkompilovat zdrojové soubory implementace šablony. Třída JETEmitter naštěstí nabízí pro tento účel jednoduchý způsob pomocí metody addVariable, která přidá do projektu .JETEmitter proměnnou cesty ke třídě.
Proměnná cesty ke třídě je název platný pro celý pracovní prostor, který se používá na platformě Eclipse k odkazování na adresář nebo soubor JAR. Seznam všech těchto proměnných lze zobrazit pomocí akce nabídky Okno > Předvolby > Java > Proměnné cesty ke třídě. Váš program bude muset přidat proměnnou cesty ke třídě pro každý adresář nebo soubor JAR, potřebný na cestě ke třídě projektu .JETEmitter.
V této části výukového programu JET zapíšeme modul plug-in platformy Eclipse, který pomocí šablony JET vygeneruje zdrojový kód Java pro výčty metody typesafe, které byly poměrně oblíbené před verzí Java 5.0, v níž byly zavedeny do jazyka Java výčty.
Náš modul plug-in musí provést následující úlohy:
V následujících oddílech postupně projdeme jednotlivé kroky.
Podívejme se na třídu výčtů metody typesafe, abychom zjistili, jaký druh zdrojového kódu chceme vygenerovat. Příkladem výčtu metody typesafe je níže uvedená třída Digit.
// příklad výčtu metody typesafe
package x.y.z;
public class Digit {
public static final Digit ZERO = new Digit(0, "nula");
public static final Digit ONE = new Digit(1, "jedna");
public static final Digit TWO = new Digit(2, "dvě");
public static final Digit THREE = new Digit(3, "tři");
// ... public static final Digit NINE = new Digit(9, "devět");
private static final Digit[] ALL =
{ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE};
private final int value;
private final String name;
private Digit(int value, String name) {
this.value = value;
this.name = name;
}
public static Digit lookup(int key) {
for (int i = 0; i < ALL.length; i++) {
if (key == ALL[i].getValue()) { return ALL[i]; }
}
// vyhledání se nezdařilo:
// nemáme výchozí třídu Digit, takže vyvoláme výjimku
throw new IllegalArgumentException("Neexistuje číslice pro " + key);
}
public int getValue() { return value; }
public int getName() { return name; }
public String toString() { return getName(); }
}
Podívejme se na tuto třídu blíže. Třída Digit má především několik
instancí - konstanty
ZERO, ONE, TWO atd. Každá instance je definována svým názvem proměnné Java, "ZERO",
"ONE", "TWO"... a hodnotami pro jednotlivé
atributy
třídy výčtu. Většina výčtů metody typesafe má minimálně jeden atribut. Třída
Digit má dva atributy: celočíselnou hodnotu value a řetězec name.
Naše ukázková třída Digit má rovněž metodu vyhledání
lookup, která vrátí instanci, jejíž atribut value se rovná zadanému parametru int. Metoda lookup zavádí koncept klíčových atributů. Řada výčtů metody typesafe má minimálně jeden atribut, který jedinečně rozlišuje jednotlivé instance.
Všimněte si, že klíčové atributy nejsou nezbytné: Java VM zaručuje, že každý nově konstruovaný objekt bude jedinečný, takže je možné mít výčty metody typesafe, které nemají vůbec žádné atributy, a jednoduše rozlišovat jejich instance pomocí operátoru identity instance ==. Tento postup lze sice bez obav použít, ale často je pohodlné mít klíčový atribut, který jedinečně identifikuje instanci, a metodu lookup, který vyhledá instanci pro zadanou hodnotu klíče.
Naše šablona má metodu lookup, takže se musíme rozhodnout, co dělat, pokud
pro zadanou hodnotu klíče není nalezena žádná instance. V zásadě existují tři možnosti: vrácení výjimky, vrácení označené "výchozí" instance nebo vrácení hodnoty null. To, která možnost je nejlepší, závisí na aplikaci, v níž je třída použita, takže rozhodnutí bychom zřejmě měli nechat na uživateli.
Nyní si po podrobnějším prostudování výčtů metody typesafe shrňme, co lze ve výčtu metody typesafe upravit:
Jednoduchý model pro upravitelné části výčtu metody typesafe může vypadat přibližně takto:
| TypesafeEnum |
| getInstances() : Instance[] getAttributes() : Attribute[] getKeyAttributes() : Attribute[] getDefaultInstance() : Instance getPackageName() : String getClassName() : String |
| Instance |
| getName() : String getAttributeValues() : Properties getAttributeValue(Attribute) : String isDefault() : boolean |
| Atribut |
| getName() : String getType() : String isKey() : boolean |
V následujícím oddílu pomocí těchto tříd převedeme naši třídu Digit na šablonu JET pro výčty metody typesafe.
Když nyní máme model, můžeme vzít třídu Digit a nahradit veškerý kód specifický pro třídu Digit skriptlety a výrazy JET, které volají třídy našeho modelu. Výsledná šablona by mohla vypadat přibližně takto:
<%@ jet package="translated" imports="java.util.* org.eclipse.emf.examples.jet.article2.model.*" class="TypeSafeEnumeration" %> <% TypesafeEnum typesafeEnum = (TypesafeEnum) argument; %> package <%=typesafeEnum.getPackageName()%>; /** * Tato konečná třída implementuje výčet metody typesafe * v platných instancích <%=typesafeEnum.getClassName()%>. * Instance této třídy jsou neměnné. */ public final class <%=typesafeEnum.getClassName()%> { <% for (Iterator<Instance> i = typesafeEnum.instances(); i.hasNext(); ) { %> <% Instance instance = i.next(); %> // definice instance public static final <%=typesafeEnum.getClassName()%> <%=instance.getName()%> =new <%=typesafeEnum.getClassName()%>(<%=instance.constructorValues()%>); <% } %> <% for (Iterator<Attribute> i = typesafeEnum.attributes(); i.hasNext(); ) { %> <% Attribute attribute = i.next(); %> // deklarace atributu
private final <%=attribute.getType()%> m<%=attribute.getCappedName()%>; <% } %> /** * Soukromý konstruktor. */
private <%=typesafeEnum.getClassName()%>(<%=typesafeEnum.constructorParameterDescription()%>) { <% for (Iterator<Attribute> i = typesafeEnum.attributes(); i.hasNext(); ) { %> <% Attribute attribute = i.next(); %>
m<%=attribute.getCappedName()%> = <%=attribute.getUncappedName()%>; <% } %> } // metody přistupujícího objektu metody getter <% for (Iterator<Attribute> i = typesafeEnum.attributes(); i.hasNext(); ) { %> <% Attribute attribute = i.next(); %> /** * vrátí <%=attribute.getName()%>. * * @return <%=attribute.getName()%>. */ public <%=attribute.getType()%> get<%=attribute.getCappedName()%>() { return m<%=attribute.getCappedName()%>; } <% } %> // metoda lookup vynechána... }
Jak vidíte, šablona volá některé metody, které v dříve uvedeném jednoduchém příkladu nebyly přítomny. Přidali jsme několik pohodlnějších metod, např. metody
Attribute.getCappedName()
a
getUncappedName(). Tyto metody pomáhají udržovat šablonu jednoduchou.
Dalším příkladem metody přidané do modelu jsou metody
TypesafeEnum.constructorParameterDescription()
a
Instance.constructorValues(). Implementace metody constructorValues je zobrazena níže.
// class Instance
/**
* Pohodlnější metoda, která vrátí hodnoty atributů této instance,
* v pořadí očekávaném konstruktorem této instance. *
* @return čárkami oddělený seznam všech hodnot atributů této instance,
* naformátovaný jako attrib1-value, attrib2-value (, ...)
*/
public String constructorValues() {
StringBuffer result = new StringBuffer();
for (Iterator<Attribute> i = getType().attributes(); i.hasNext(); ) {
Attribute attribute = i.next();
result.append(getAttributeValue(attribute));
if (i.hasNext()) {
result.append(", ");
}
}
return result.toString();
}
Metoda constructorValues prochází ve smyčkách atributy výčtu metody typesafe, vyhledává hodnoty pro jednotlivé atributy v instanci a zřetězuje tyto hodnoty do řetězce s použitím čárky jako oddělovače. Například v naší třídě výčtu metody typesafe Digit by tato metoda pro instanci "ZERO" vrátila hodnoty "0, \"nula\"".
Hodnotami atributů bychom mohli procházet v šabloně, šablona by se tím však stala mnohem hůře čitelná. Díky vložení této logiky do modelu je šablona mnohem čitelnější a snáze se udržuje. Na druhou stranu jsme přišli o určitou flexibilitu, protože uživatelé již nemohou tuto logiku upravit upravením šablony. Toto je kompromis, který je třeba učinit. To, která z možností je lepší, závisí na vaší šabloně a aplikaci.
Nyní již máme model a šablonu, k dokončení našeho modulu plug-in však potřebujeme ještě dvě věci: potřebujeme grafické uživatelské rozhraní pro shromáždění hodnot od uživatele, kterými naplníme náš model, a potřebujeme vyvolat naši šablonu s naplněným modelem k vygenerování zdrojového kódu a uložení tohoto zdrojového kódu do umístění v pracovním prostoru.
Začněme grafickým uživatelským rozhraním. Pracovní plocha nabízí několik průvodců, kteří umí něco podobného tomu, co máme v úmyslu, například průvodce vytvořením nové třídy, průvodce novým rozhraním nebo průvodce novým testovacím případem JUnit. Zřejmě bude dávat smysl, když bude naše grafické uživatelské rozhraní vypadat podobně jako tito průvodci a bude přístupné ze standardních umístění nabídky a panelu nástrojů.
Náš průvodce má tři stránky. První stránka, která je zobrazena níže, vypadá jako zjednodušená verze průvodce vytvořením nové třídy. Ve skutečnosti použijeme stejný rámec jako průvodce vytvořením nové třídy, a sice balík org.eclipse.jdt.ui.wizards. Na první stránce shromáždíme název balíku a název třídy výčtu metody typesafe a umístění, do kterého by se měl uložit výsledek.
Druhá stránka shromáždí informace o atributech třídy výčtu metody typesafe. Každý atribut má název a typ a může být jedním z klíčových atributů. Druhá stránka našeho průvodce je zobrazena níže:

Třetí a poslední stránka průvodce, která je zobrazena níže, shromáždí informace o instancích výčtu metody typesafe. Uživatel zadá název instance a u každé instance poskytne hodnoty všech atributů.
A konečně jedna z instancí může být "výchozí" instance, což je instance vrácená metodou lookup, pokud není nalezena žádná instance pro zadané hodnoty klíčového atributu.

Když nyní máme grafické uživatelské rozhraní k naplnění modelu, můžeme konečně využít to, co jsme se naučili v první části tohoto článku a vygenerovat pomocí naší šablony zdrojový kód.
Když uživatel stiskne v průvodci tlačítko Dokončit, zavolá se v průvodci metoda performFinish. Níže uvedený kód ukazuje, jak
pomocí
vlastní podtřídy JETEmitter
přidáme soubor JAR našeho modulu plug-in do cesty ke třídě projektu .JETEmitters před tím, než na třídě JETEmitter
zavoláme metodu generate. Vygenerovaný zdrojový kód výčtu metody typesafe se
uloží do uživatelem zadaného umístění v pracovním prostoru.
// class NewTypesafeEnumCreationWizard
protected void finishPage(IProgressMonitor monitor)
throws InterruptedException, CoreException {
String pluginId = "org.eclipse.emf.examples.jet.article2";
String base = Platform.getBundle(pluginId).getEntry("/").toString();
String relativeUri = "templates/TypeSafeEnumeration.javajet";
JETEmitter emitter = new JETEmitter(base + relativeUri, getClass().getClassLoader());
emitter.addClasspathVariable("JET_TUTORIAL", pluginId);
TypesafeEnum model = mPage1.getTypesafeEnumModel();
IProgressMonitor sub = new SubProgressMonitor(monitor, 1);
String result = emitter.generate(sub, new Object[] { model });
monitor.worked(1);
IFile file = save(monitor, result.getBytes());
selectAndReveal(file);
openResource(file);
}
Náš níže uvedený poslední úsek kódu zobrazuje část konfiguračního souboru plugin.xml, ve kterém registrujeme průvodce jako příspěvek na pracovní plochu.
<extension point="org.eclipse.ui.newWizards">
<wizard
name="Typesafe Enum"
icon="icons/newenum_wiz.gif"
category="org.eclipse.jdt.ui.java"
id="org.eclipse.emf.examples.jet.article2.ui.NewTypesafeEnumCreationWizard">
<description>
Vytvořte výčet metody typesafe
</description>
<class class="org.eclipse.emf.examples.jet.article2.ui.NewTypesafeEnumCreationWizard">
<parameter name="javatype" value="true"/>
</class>
</wizard>
</extension>
Nyní se náš průvodce aktivuje, když uživatel vybere na pracovní ploše volbu Soubor > Nový > Jiný > Java > Výčet metody typesafe, jak je patrné z níže uvedeného obrázku.
Všimněte si, že jsme atribut
javatype v prvku rozšíření průvodce v souboru plugin.xml nastavili na hodnotu true. To způsobí, že se průvodce zobrazí jako akce v panelu nástrojů v perspektivě Java, jak je patrné z níže uvedeného obrázku.
Při použití JET se musíte rozhodnout, zda chcete rozdělovat šablony s aplikací nebo rozdělovat pouze implementační třídy šablony.
Pokud je vaším cílem zjednodušení možností generování textu vaší aplikace, je použití charakteru JET a tvůrce JET Builder k automatickému překladu šablon dobrou volbou. Podrobnosti viz Výukový program JET, část 1. V takovém případě musíte rozdělovat pouze přeložené implementační třídy šablony s aplikací, nikoli šablony samotné.
Pokud u vaší aplikace naopak záleží na tom, aby měli uživatelé nad generovaným textem maximální kontrolu, budete možná chtít rozdělovat s aplikací samotné soubory šablon. V takovém případě budete muset přeložit tyto šablony pokaždé, když budete generovat text. Příkladem tohoto typu aplikace je modul plug-in, který jsme zapsali v tomto článku.
Tento článek vysvětlil, jaké třídy jsou dostupné v balíku JET k dosažení těchto cílů, a ukázal, jak tyto třídy používat s modulem plug-in platformy Eclipse. Níže uvedená příloha nabízí přehled rozhraní JET API a ukazuje, jak jej lze využít v aplikacích bez konzoly a v samostatných aplikacích.
| Třída | Popis |
|---|---|
| CodeGen |
Třída CodeGen může přeložit šablonu JET na zdrojový kód Java a volitelně sloučit zdrojový kód Java implementace šablony s existující třídou Java. Třídu CodeGen lze použít jako aplikaci Eclipse bez konzoly. Metoda run očekává parametr pole řetězců o dvou nebo třech prvcích:
|
| CodeGenPlugin | Třída modulu plug-in pro balík JET. |
| Třída | Popis |
|---|---|
| IJETNature | Rozhraní rozšiřující org.eclipse.core.resources.IProjectNature. Definuje některé z vlastností charakteru JET. Implementováno třídou JETNature. Používá jej modul plug-in org.eclipse.emf.codegen.ui jako filtr pro stránky vlastností projektu. |
| JETAddNatureOperation | org.eclipse.core.resources.IWorkspaceRunnable pro přidání charakteru JET do projektu v pracovním prostoru. Používá ji AddJETNatureAction v modulu plug-in org.eclipse.emf.codegen.ui. |
| JETBuilder | Tato třída rozšiřuje org.eclipse.core.resources.IncrementalProjectBuilder. Při vyvolání její metody build se deleguje na třídu JETCompileTemplateOperation překlad všech šablon v projektu pracovního prostoru, které se změnily od předchozího sestavení. Šablony se musejí nacházet v jedné ze složek určených jako kontejnery šablon v charakteru JET projektu. |
| JETCharDataGenerator | Zodpovídá za část procesu překladu šablony. Generuje řetězce pro znaková data přítomná v souboru šablony. Používá ji třída JETCompiler. |
| JETCompiler | Toto je hlavní třída pro překlad šablon. Tato třída zodpovídá za překlad šablon do zdrojového kódu Java implementační třídy šablony. Skutečný překlad je delegován na další třídy v tomto balíku. Třída JETParser se používá k analýze šablony na prvky šablony. Třída JETCompiler implementuje rozhraní JETParseEventListener a registruje se u syntaktického analyzátoru, aby jí bylo oznámeno, že analyzátor rozeznal prvek šablony. V případě každého rozeznaného prvku šablony třída JETCompiler pomocí třídy JETGenerator přeloží prvek šablony na zdrojový kód Java. Po dokončení syntaktické analýzy šablony třída JETCompiler pomocí třídy JETSkeleton sestaví prvky zdrojového kódu Java do jediné kompilační jednotky (třídy Java). |
| JETCompileTemplateOperation | Tato třída implementuje org.eclipse.core.resources.IWorkspaceRunnable, takže se může provádět jako dávková operace v rámci pracovního prostoru. Tato operace přijímá jako parametry konstruktoru projekt pracovního prostoru, minimálně jeden kontejner šablony a volitelně seznam specifických souborů šablony. Při vyvolání její metody run přeloží pomocí třídy JETCompiler soubory šablony v zadaných složkách projektu pracovního prostoru na zdrojové soubory Java pro implementační třídy šablony. Tuto operaci lze volitelně nakonfigurovat tak, aby po dokončení spustila úplné sestavení projektu ke kompilaci zdrojových souborů Java na soubory .class. |
| JETConstantDataGenerator | Zodpovídá za část procesu překladu šablony. Rozšiřuje třídu JETCharDataGenerator pro generování deklarací konstant pro řetězce, které mají v souboru šablony přítomná znaková data. |
| JETCoreElement | Rozhraní pro hlavní prvky syntaxe JET (direktiva, výraz, skriptlet a řídicí znak uvozovek). Používá ji třída JETParser. |
| JETEmitter | Tato třída poskytuje uživatelům tohoto balíku pohodlné rozhraní API vysoké úrovně. Metoda generate této třídy přeloží šablonu na zdrojový kód Java, zkompiluje tento zdrojový kód do implementační třídy šablony, požádá třídu šablony o vygenerování textu a nakonec vrátí vygenerovaný výsledek. Tato třída vytvoří v pracovním prostoru projekt Java s názvem .JETEmitters, přeloží šablonu do tohoto projektu a ke zkompilování zdrojového kódu jednoduše zavolá metodu build na projektu .JETEmitters. Pokud se překlad nebo kompilace nezdaří, dojde k výjimce JETException. Třída Java implementace šablony je "provedena" voláním její metody generate. |
| JETException | Rozšiřuje org.eclipse.core.runtime.CoreException, ale poskytuje pohodlnější konstruktory. |
| JETExpressionGenerator | Zodpovídá za část procesu překladu šablony. Rozšiřuje třídu JETScriptletGenerator pro překlad výrazů JET (<%= ... %> atd.) do zdrojového kódu Java. |
| JETGenerator | Rozhraní pro generátory: třídy, které umí přeložit část šablony JET na prvek zdrojového kódu Java. |
| JETMark | Stavový objekt používaný třídou JETParser k označení bodů ve vstupním proudu znaků JET a delegaci zpracování částí proudu na další objekty. |
| JETNature |
Tato třída implementuje třídu IJETNature tak, aby mohla konfigurovat projekt pracovního prostoru s charakterem JET. Po přidání tohoto charakteru do projektu přidá tvůrce JET Builder do popředí specifikace sestavení projektu. Tento charakter definuje dvě vlastnosti:
Tyto vlastnosti používá tvůrce JET Builder při provádění sestavení. |
| JETParseEventListener | Rozhraní pro objekty, které umí zpracovat části vstupního proudu znaků JET. |
| JETParser | Třída hlavního syntaktického analyzátoru. Má několik vnitřních tříd pro rozeznání hlavních prvků syntaxe JET (direktiva, výraz, skriptlet a řídicí znak uvozovek). Při rozeznání hlavního prvku syntaxe JET je skutečné zpracování prvku delegováno na třídu JETParseEventListener. |
| JETReader | Vstupní vyrovnávací paměť pro syntaktický analyzátor JET. Poskytuje metodu stackStream, kterou mohou ostatní volat s proudem znaků do souboru začlenění. Poskytuje také mnoho dalších pohodlnějších metod pro syntaktický analyzátor. |
| JETScriptletGenerator | Zodpovídá za část procesu překladu šablony. Překládá skriptlety JET (<% ... %> atd.) do zdrojového kódu Java. |
| JETSkeleton | Tato třída poskytuje rozhraní pro sestavení prvků zdrojového kódu Java do jedné kompilační jednotky Java (třídy Java). Prvky zdrojového kódu Java jsou sestaveny podle definice kostry třídy. Kostru lze použít k přidání často opakovaného kódu do přeložené implementační třídy šablony. Tato třída poskytuje výchozí vlastní definici kostry implementační třídy šablony, ale může také sestavit prvky Java pomocí vlastní kostry. Skutečná syntaktická analýza a generování zdrojového kódu Java se delegují na třídy v balíku org.eclipse.jdt.core.jdom. |
| Třída | Popis |
|---|---|
| JControlModel | Model řízení, který poskytuje slovníky a pravidla k řízení procesu sloučení. |
| JMerger | Třída pro slučování zdrojových souborů Java. Analyzuje zdrojový kód pomocí implementace rozhraní v balíku org.eclipse.emf.codegen.merge.java.facade. Tuto třídu může používat kód aplikace. |
| JPatternDictionary | Slovník podpisů a uzlů Java. |
| Třída | Popis |
|---|---|
| PropertyMerger | Třída pro slučování souborů vlastností. Tuto třídu může používat kód aplikace. |
Třída org.eclipse.emf.codegen.CodeGen může přeložit šablonu JET na zdrojový kód Java a volitelně sloučit zdrojový kód Java implementace šablony s existující třídou Java. Třídu CodeGen lze použít jako aplikaci Eclipse bez konzoly ("bez konzoly" znamená, že se nespustí grafické uživatelské rozhraní Eclipse). Složka plugins/org.eclipse.emf.codegen/test v instalaci Eclipse obsahuje několik skriptů pro spuštění třídy CodeGen jako aplikace Eclipse bez konzoly. Tyto skripty jsou ve formátu Unix.
Níže je uveden příklad skriptu pro systém Windows. Všimněte si, že třídě CodeGen předáváme dva argumenty:
Pokud již cílová cesta obsahuje výsledek předchozího překladu a chcete sloučit výsledek nového překladu se stávajícím, můžete zadat jako třetí argument soubor modelu řízení JMerge. Složka plugins/org.eclipse.emf.codegen/test v instalaci Eclipse obsahuje příklad souboru merge.xml.
@echo off set ECLIPSE_HOME=C:\eclipse-2.1\eclipse set WORKSPACE=%ECLIPSE_HOME%\workspace set OPTS=-Xmx900M -Djava.compiler=NONE -verify -cp %ECLIPSE_HOME%\startup.jar set MAIN=org.eclipse.core.launcher.Main -noupdate -data %WORKSPACE%set TEMPLATE_URI=test.javajet
set TARGET_FOLDER=C:\temp\jetstandalone\MyProject set ARGUMENTS=%TEMPLATE_URI% %TARGET_FOLDER% echo Před spuštěním tohoto skriptu vypněte Eclipse. java %OPTS% %MAIN% -application org.eclipse.emf.codegen.CodeGen %ARGUMENTS%
Autor: Knut Wannheden (knut.wannheden@paranor.ch)
Binární soubor: jetc-task.jar.
Zdroj: JETCTask.java.
Několik poznámek:
Zde je jednoduchý soubor sestavení Ant (cesta ke třídě taskdef předpokládá, že máte Eclipse 3.3 a EMF 2.3.0):
<project default="jetc_multiple_templates">
<property name="eclipse.plugins.dir" location="C:\eclipse-SDK-3.3M4-win32\eclipse\plugins" />
<taskdef name="jetc" classname="ch.paranor.epla.structure.JETCTask">
<classpath>
<pathelement location="jetc-task.jar" />
<fileset dir="${eclipse.plugins.dir}">
<include name="org.eclipse.core.boot_*.jar" />
<include name="org.eclipse.core.resources_*.jar" />
<include name="org.eclipse.core.runtime_*.jar" />
<include name="org.eclipse.jdt.core_*.jar" />
<include name="org.eclipse.emf.codegen_*.jar" />
</fileset>
</classpath>
</taskdef>
<!-- Příklad použití 1: -->
<!-- Soubor šablony zadejte do atributu "template". -->
<!-- Pomocí atributů "class" a "package" můžete přepsat -->
<!-- atributy "class" a "package" v souboru šablony. -->
<target name="jetc_single_template">
<mkdir dir="jet-output" />
<jetc template="test.xmljet"
package="com.foo"
class="Test"
destdir="jet-output" />
<javac srcdir="jet-output" destdir="classes" />
</target>
<!-- Příklad použití 2: -->
<!-- Překlad celé skupiny souborů šablon najednou. -->
<!-- Při použití sady souborů nelze použít atributy "class" a "package". -->
<target name="jetc_multiple_templates">
<mkdir dir="jet-output" />
<jetc destdir="jet-output">
<fileset dir="jet-templates" includes="*.*jet" />
</jetc>
<javac srcdir="jet-output" destdir="classes" />
</target>
</project>
Substitutes for Missing C Constructs (Autor: Joshua Bloch)
Java Tip 122: Beware of Java typesafe enumerations (Autor: Vladimir Roubtsov)Java Tip 133: More on typesafe enums (Autor: Philip Bishop)
Java a veškeré ochranné známky a loga odvozená z názvu Java jsou ochranné známky nebo registrované ochranné známky společnosti Sun Microsystems ve Spojených státech a případně v dalších jiných zemích.