Eclipse-artikel |
Oversigt
I del 2 af dette øveprogram til JET (Java Emitter Templates) ser vi nærmere på JET-programmets API. Vi vil gennemgå, hvordan du skriver plugins, der bruger klasserne i JET-pakken til at generere Java-kildekode.
Som et praktisk eksempel vil vi oprette en plugin, der ud fra brugerinput genererer en typesikker enumerationsklasse. Den genererede kildekode er baseret på en JET-skabelon, der kan distribueres sammen med plugin'en, så brugerne af denne plugin kan tilpasse den genererede kode ved at redigere skabelonen.
Artiklen indeholder også en kort oversigt over JET API'et.
Bidragyder: Remko Popma, Azzurri Ltd., remko.popma snabel-a azzurri punktum jp, 26. august 2003. Bruges med tilladelse. Sidst opdateret: 3. januar 2007.
Introduktion
Nogle JET-klasser
En plugin, der genererer kildekode
Konklusion
Appendiks
Ressourcer
Omdannelse i forhold til generering Et aspekt ved JET-skabeloner kan virke forvirrende til at begynde med. Det er, at generering består af to trin: omdannelse og generering. Det første trin består i at omdanne skabelonen til en skabelonimplementeringsklasse. Det andet trin består i at bruge denne skabelonimplementeringsklasse til at generere teksten. Hvis dit mål med JET er at generere Java-kode, kan det være forvirrende, at resultatet af skabelonomdannelsestrinnet også er Java-kildekode. Husk, at denne kildekode ikke er den genererede tekst. Den kildekode, der er resultatet af omdannelsestrinnet, er blot en anden udgave af skabelonen. Hvis du har brugt JSP og miniservere (servlets) før, kan du betragte en JET-skabelon som svarende til en JSP-side. En JET-skabelon omdannes til en skabelonimplementeringsklasse, ligesom en JSP-side omdannes til en miniserver. Det andet trin, hvor skabelonimplementeringsklassen genererer tekst, svarer til, at miniserveren opretter og returnerer HTML. |
Del 1 af dette øveprogram introducerede JET-skabeloner og beskrev, hvordan du kan konvertere et projekt til et JET-projekt for at få JET-byggeprogrammet til automatisk at omdanne skabeloner i dit projekt til skabelonimplementeringsklasser.
I del 2 af øveprogrammet vil vi fokusere på at skrive en plugin, der bruger klasserne i JET-pakken til at generere Java-kildekode. En plugin, der genererer tekst fra en JET-skabelon, kan ikke få omdannet skabeloner automatisk ved hjælp af JET-naturen og JET-byggeprogrammet. Det skyldes, at JET-naturen og JET-byggeprogrammet kun arbejder med projekter i arbejdsområdet, ikke med plugins. Plugins er nødt til at bruge klasserne i JET-pakken til at omdanne deres egne skabeloner.
Du kan pakke org.eclipse.emf.examples.jet.article2_2.3.0.zip ud i dit plugins-underbibliotek, hvis du vil udføre eksemplet eller se kildekoden, der bruges i denne artikel. Du skal have EMF installeret for at bruge plugin-eksemplet. Artiklens forfatter har brugt version 2.3.0 M4.
Næste afsnit omhandler nogle af klasserne i pakken org.eclipse.emf.codegen. Vi vil gennemgå de trin, der kræves for at generere kildekode med JET, og hvordan JET-programklasserne passer ind. Hvis du er utålmodig efter at se noget kode, der viser, hvordan klasserne bruges i praksis, kan du gå direkte til En plugin, der genererer kildekode.
I dette afsnit ser vi nærmere på nogle af klasserne i JET-pakken. De kan stort set inddeles i to grupper:
Klasserne på lavt niveau beskrives ikke nærmere i denne artikel. En beskrivelse af alle klasserne i plugin'en org.eclipse.emf.codegen finder du i afsnittet Oversigt over JET API nedenfor. I resten af dette afsnit fokuserer vi på et par af klasserne på højt niveau.
JETCompiler er kerneklassen til omdannelse af skabeloner. Klassen har ansvaret for at omdanne skabeloner til Java-kildekoden til en skabelonimplementeringsklasse. Den egentlige omdannelse delegeres til andre klasser i samme pakke. Klienter opretter et JETCompiler-objekt til en bestemt skabelon og kalder derefter metoden parse efterfulgt af metoden generate for at skrive Java-kildekoden til den resulterende skabelonimplementeringsklasse til en angivet strøm.
JETEmitter er et praktisk API på højt niveau for brugere af JET-pakken. Metoden generate i denne klasse kombinerer skabelonomdannelse og tekstgenerering i ét trin. JETEmitter tager sig af alle detaljerne i forbindelse med omdannelse af skabeloner og kompilering af Java-kildekoden til den omdannede skabelonimplementeringsklasse, så du kan fokusere på det endelige output fra generatoren.
Man kan også opfatte JETEmitter som en afskærmning omkring omdannelsestrinnet, så det virker, som om du kan generere tekst direkte ud fra en skabelon. Ifølge loven om lækkende abstraktioner (Law of Leaky Abstractions) kan vi ikke altid slippe afsted med det, og i afsnittet JETEmitter-faldgruber nedenfor nævnes et par steder, hvor du skal være forsigtig.
JETEmitter er den klasse, vi vil bruge i vores plugin, så vi går lidt mere i dybden her.
Et JETEmitter-objekt konstrueres med URI'en for den skabelon, der bruges til at generere tekst. Alle URI-typer kan angives, blot en protokolbehandler er til rådighed. Det betyder, at både URI'er med typen file:/, ftp:/ og http:/ kan bruges. Eclipse tilføjer særlige protokolbehandlere til URI'erne platform:/base/, platform:/plugin/, platform:/fragment/ og platform:/resource/, så plugins kan bruge en URI som platform:/resource/myproject/myfolder/mytemplate.jet til at angive en skabelonfil. Bemærk: I Eclipse 3.0 er bundleentry tilføjet til listen med særlige protokoller. Den bør bruges i referencer til Eclipse-elementer som f.eks. plugins og funktioner.
I vores plugin-eksempel vil vi distribuere vores skabelonfil sammen med plugin'en, så skabelonfilen vil blive placeret i folderen myplugin/templates under Eclipse-folderen plugins. Derefter kan følgende kode bruges til at finde og generere en skabelon fra denne folder:
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});
Når der er konstrueret et JETEmitter-objekt, kalder klienter generate for det for at generere tekst. Metoden generate udfører følgende trin:
* .JETEmitters er standardnavnet på det projekt, der oprettes under skabelonomdannelsen. Værdien kan ændres med metoden setProjectName.
Vores plugin-eksempel vil bruge JETEmitter og gemme den genererede tekst som en Java-kildefil i arbejdsområdet. I figuren nedenfor vises de trin, som generering af kildekode ved hjælp af JETEmitter består af.
Klassen JETEmitter kombinerer skabelonomdannelse og tekstgenerering i ét trin, hvilket gør den til et meget praktisk værktøj. Det er imidlertid vigtigt at vide, hvad der foregår bag kulisserne. Ellers kan du få nogle ubehagelige overraskelser. I dette afsnit beskrives nogle almindelige "faldgruber", så du ikke begår samme fejl.
Det er ikke let at bruge JET uden for Eclipse. JET er kun designet til at blive udført som et arbejdsområdeprogram. Ethvert program, der bruger JET, skal som minimum udføres som et "hovedløst" Eclipse-program, så der foretages initialisering af plugin'en. (Betegnelsen hovedløs dækker over udførelse af Eclipse uden brugergrænsefladen).
Det betyder, at det ikke virker, hvis du prøver at bruge JETEmitter fra et simpelt enkeltstående program (en Java-standardklasse med en main-metode):
// This fails: cannot use JETEmitter from a standalone application public static void main(String[] args) { JETEmitter emitter = new JETEmitter("/myproject/templates/HelloWorld.txtjet"); // this will throw a NullPointerException String result = emitter.generate(new NullProgressMonitor(), {"hi" }); System.out.println(result);
Bemærk, at det ikke kun er en begrænsning for klassen JETEmitter. Mange af klasserne i plugin'en org.eclipse.emf.codegen har afhængigheder af andre plugins. I afsnittet Appendiks nedenfor er der flere oplysninger om brug af JET fra enkeltstående programmer.
I resten af denne artikel forudsætter vi, at koden udføres fra en plugin.
Du kan få en fejl af typen Ingen klassedefinition er fundet, hvis du overfører et tilpasset objekt som argument til metoden JETEmitter.generate. Det kan ske, hvis det objekt, du overfører som argument, ikke tilhører en af "bootstrap"-klasserne i Java ("bootstrap"-klasserne er runtime-klasserne i rt.jar og internationaliseringsklasserne i i18n.jar).
Du kan undgå fejlen ved at angive klasseindlæseren til din plugin, når du bruger JETEmitter. Hvis der ikke angives en klasseindlæser, bruger JETEmitter sin egen klasses klasseindlæser. Det er normalt klasseindlæseren til plugin'en org.eclipse.emf.codegen, og denne klasseindlæser kan ikke se ret meget. I nyere versioner af EMF (fra version 1.1.0 build 20030527_0913VL) har JETEmitter en konstruktør med en klasseindlæser som argument.
Bemærk, at en anden måde at angive en klasseindlæser på er at oprette en underklasse af JETEmitter i dit eget projekt. Hvis der ikke angives en klasseindlæser, vil JETEmitter bruge denne underklasses klasseindlæser. (Hvis du bruger en ældre version af EMF, er der ingen konstruktører, der kan modtage et klasseindlæserargument, og du har ikke andet valg end at oprette en underklasse af JETEmitter i dit eget projekt).
I eksemplet herunder vises en funktionsklasse, der omdanner og kalder en valgt skabelon ved hjælp af JETEmitter. Eksemplet viser, hvordan JETEmitter kan konstrueres med en klasseindlæserparameter eller ved at
konstruere en anonym underklasse.
package org.eclipse.emf.examples.jet.article2.actionexample; // imports omitted 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); // or: use an anonymous subclass
// emitter = new JETEmitter(templateURI) {}; // notice the brackets 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); } } } // saveGenerated method omitted }
JETEmitter omdanner dine skabeloner til Java-kildefiler i projektet .JETEmitters og kalder JavaBuilder for at få kompileret disse kildefiler. Hvis dine skabeloner bruger klasser, der ikke er Java-standardklasser eller ikke findes i EMF-plugin'en, må du tilføje disse klasser til .JETEmitters-projektets classpath. Ellers kan JavaBuilder ikke kompilere kildefilerne til skabelonimplementering. Heldigvis gør JETEmitter det let via metoden addVariable, som tilføjer en classpath-variabel til .JETEmitters-projektet.
En classpath-variabel er et navn, der refererer til en JAR-fil eller et bibliotek, og som gælder i hele arbejdsområdet. Listen med alle variablerne af denne type kan ses med menupunktet Vindue > Indstillinger > Java > Classpath-variabler. Dit program skal tilføje en classpath-variabel for hver JAR-fil eller hvert bibliotek, der skal findes i classpath til .JETEmitters-projektet.
I denne del af øveprogrammet til JET vil vi skrive en plugin, der bruger en JET-skabelon til at generere Java-kildekode til typesikre enumerationer, som var ret populære før Java 5.0, hvor enums blev indført i sproget.
Vores plugin skal udføre følgende opgaver:
I de næste afsnit gennemgår vi ovennævnte trin et for et.
Lad os se på en typesikker enumerationsklasse for at undersøge, hvilken slags kildekode vi vil generere. Klassen Digit herunder er et eksempel på en typesikker enum.
// an example typesafe enum package x.y.z; public class Digit {public static final Digit ZERO = new Digit(0, "zero"); public static final Digit ONE = new Digit(1, "one"); public static final Digit TWO = new Digit(2, "two"); public static final Digit THREE = new Digit(3, "three"); // ... public static final Digit NINE = new Digit(9, "nine"); 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]; } } // lookup failed: // we have no default Digit, so we throw an exception
throw new IllegalArgumentException("No digit exists for " + key); } public int getValue() { return value; } public int getName() { return name; } public String toString() { return getName(); } }
Lad os undersøge klassen nærmere. For det første har klassen Digit flere forekomster - konstanterne
ZERO, ONE, TWO osv. Hver forekomst defineres af dens Java-variabelnavn, "ZERO", "ONE", "TWO"..., og værdien af hver
attribut for enumerationsklassen. De fleste typesikre enums har en eller flere attributter. Klassen
Digit har to attributter: value, der er et heltal, og name, som er en streng.
Vores Digit-eksempelklasse har også en lookup-metode, som returnerer den forekomst, hvis value-attribut er lig med den angivne int-parameter. Opslagsmetoden lookup introducerer begrebet nøgleattributter. Mange typesikre enums har en eller flere attributter, så der entydigt kan skelnes mellem forekomsterne.
Bemærk, at nøgleattributter ikke er et krav. Java VM garanterer, at ethvert netop oprettet objekt er entydigt, og det er derfor muligt at have typesikre enumerationer uden nogen attributter overhovedet og blot skelne mellem forekomsterne med identitetsoperatoren ==. Det fungerer fint, men det er ofte praktisk at have en nøgleattribut, der identificerer en forekomst entydigt, og en lookup-metode, som finder forekomsten for en angivet nøgleværdi.
Vores skabelon har en lookup-metode, så vi skal beslutte, hvad der skal gøres, hvis der ikke bliver fundet en forekomst for den angivne nøgleværdi. Der er grundlæggende tre muligheder: afsend en undtagelse, returnér en fastlagt "standardforekomst", eller returnér NULL. Hvilken mulighed der er den bedste, afhænger af det program, som klassen bruges i, så vi skal nok lade det være op til brugeren.
Vi har nu undersøgt typesikre enums nærmere og kan sammenfatte det, der kan tilpasses i en typesikker enumeration:
En simpel model for de dele af en typesikker enum, der kan tilpasses, kan se sådan ud:
TypesafeEnum |
getInstances() : Instance[] getAttributes() : Attribute[] getKeyAttributes() : Attribute[] getDefaultInstance() : Instance getPackageName() : String getClassName() : String |
Forekomst |
getName() : String getAttributeValues() : Properties getAttributeValue(Attribute) : String isDefault() : boolean |
Attribut |
getName() : String getType() : String isKey() : boolean |
I næste afsnit vil vi bruge disse klasser til at konvertere vores Digit-klasse til en JET-skabelon til typesikre enumerationer.
Nu hvor vi har en model, kan vi tage vores Digit-klasse og erstatte al Digit-specifik kode med JET-minikommandofiler og udtryk, som kalder vores modelklasser. Resultatskabelonen kan se sådan ud:
<%@ jet package="translated" imports="java.util.* org.eclipse.emf.examples.jet.article2.model.*" class="TypeSafeEnumeration" %> <% TypesafeEnum typesafeEnum = (TypesafeEnum) argument; %> package <%=typesafeEnum.getPackageName()%>; /** * This final class implements a type-safe enumeration * over the valid instances of a <%=typesafeEnum.getClassName()%>. * Instances of this class are immutable. */ public final class <%=typesafeEnum.getClassName()%> { <% for (Iterator<Instance> i = typesafeEnum.instances(); i.hasNext(); ) { %> <% Instance instance = i.next(); %> // instance definition public static final <%=typesafeEnum.getClassName()%> <%=instance.getName()%> =new <%=typesafeEnum.getClassName()%>(<%=instance.constructorValues()%>); <% } %> <% for (Iterator<Attribute> i = typesafeEnum.attributes(); i.hasNext(); ) { %> <% Attribute attribute = i.next(); %> // attribute declaration
private final <%=attribute.getType()%> m<%=attribute.getCappedName()%>; <% } %> /** * Private constructor. */
private <%=typesafeEnum.getClassName()%>(<%=typesafeEnum.constructorParameterDescription()%>) { <% for (Iterator<Attribute> i = typesafeEnum.attributes(); i.hasNext(); ) { %> <% Attribute attribute = i.next(); %>
m<%=attribute.getCappedName()%> = <%=attribute.getUncappedName()%>; <% } %> } // getter accessor methods <% for (Iterator<Attribute> i = typesafeEnum.attributes(); i.hasNext(); ) { %> <% Attribute attribute = i.next(); %> /** * Returns the <%=attribute.getName()%>. * * @return the <%=attribute.getName()%>. */ public <%=attribute.getType()%> get<%=attribute.getCappedName()%>() { return m<%=attribute.getCappedName()%>; } <% } %> // lookup method omitted... }
Som det ses, kalder skabelonen nogle metoder, der ikke fandtes i den simple model, vi introducerede tidligere. Vi har tilføjet et par bekvemmelighedsmetoder, f.eks. metoderne Attribute.getCappedName() og
getUncappedName(). Den type metoder hjælper med at holde skabelonen enkel.
Vi har også tilføjet andre metoder til modellen, herunder metoderne TypesafeEnum.constructorParameterDescription() og
Instance.constructorValues(). Implementeringen af metoden constructorValues er vist nedenfor.
// class Instance /** * Convenience method that returns the attribute values of this instance, * in the order expected by the constructor of this instance. * * @return a comma-separated list of all attribute values of this instance, * formatted like 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(); }
Metoden constructorValues gennemløber den typesikre enums attributter, slår hver attributs værdi op i forekomsten og sammenkæder disse værdier adskilt med komma i en streng. I vores typesikre Digit-enum-klasse ovenfor ville metoden returnere "0, \"zero\"" for forekomsten "ZERO".
Vi kunne have gennemløbet attributværdierne i skabelonen, men det ville have gjort skabelonen meget sværere at læse. Når denne logik skubbes ind i modellen, bliver skabelonen mere læsevenlig og lettere at vedligeholde. På den anden side har vi mistet fleksibilitet, fordi brugeren ikke længere kan tilpasse denne logik ved at redigere skabelonen. Det er en afvejning, man må foretage. Det bedste valg afhænger af din skabelon og dit program.
Vi har nu en model og en skabelon og mangler to dele mere, før vores plugin er færdig. Vi skal have en brugergrænseflade, der indsamler værdier fra brugeren, som vores model skal udfyldes med, og vi skal kalde vores skabelon med den udfyldte model for at generere kildekode og gemme denne kildekode et sted i arbejdsområdet.
Vi begynder med brugergrænsefladen. Arbejdsbænken stiller et par guider til rådighed, der gør noget i den stil, vi har tænkt os, f.eks. guiderne Ny klasse, Ny grænseflade og Ny JUnit-testcase. Det giver formentlig mening at få vores brugergrænseflade til at ligne disse guider og give adgang til den fra standardplaceringerne på menu og værktøjslinje.
Vores guide har tre sider. Den første side, der er vist nedenfor, ligner en forenklet udgave af guiden Ny klasse. Faktisk bruger vi samme struktur, som guiden Ny klasse bruger, nemlig pakken org.eclipse.jdt.ui.wizards. På den første side indsamler vi pakkenavnet og klassenavnet for den typesikre enum og det sted, hvor resultatet skal gemmes.
Side 2 indsamler oplysninger om den typesikre enum-klasses attributter. Hver attribut har et navn og en type og kan være en af nøgleattributterne. Side 2 i guiden er vist nedenfor:
Den tredje og sidste side i guiden, som vises herunder, indsamler oplysninger om den typesikre enums forekomster. Brugeren indtaster forekomstnavnet og angiver værdier for alle attributterne for hver forekomst.
En af forekomsterne kan være standardforekomsten "Default", som er den forekomst, der returneres af lookup-metoden, hvis der ikke bliver fundet en forekomst for de angivne nøgleattributværdier.
Vi har nu en brugergrænseflade, der kan udfylde vores model, og kan endelig bruge det, vi gennemgik i den første del af artiklen, og generere kildekode med vores skabelon.
Når en bruger klikker på Afslut i guide, kaldes metoden performFinish i vores guide. Koden herunder viser, hvordan vi bruger en tilpasset underklasse af JETEmitter til at
tilføje JAR-filen til vores plugin til .JETEmitters-projektets classpath, før vi
kalder generate på JETEmitter. Den genererede kildekode til den typesikre enum
gemmes på den placering i arbejdsområdet, som brugeren angav.
// 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); }
Det sidste kodestykke nedenfor viser den del af vores plugin.xml-konfigurationsfil, hvor vi registrerer vores guide som et bidrag til arbejdsbænken.
<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>
Create a Typesafe Enumeration
</description>
<class class="org.eclipse.emf.examples.jet.article2.ui.NewTypesafeEnumCreationWizard">
<parameter name="javatype" value="true"/>
</class>
</wizard>
</extension>
En bruger kan nu aktivere guiden ved at vælge Fil > Ny > Andet > Java > Typesafe Enum fra arbejdsbænken som vist på billedet herunder.
Bemærk, at vi har angivet attributten javatype til "true" i udvidelseselementet wizard i filen plugin.xml. Det bevirker, at vores guide vises som en mulighed på værktøjslinjen i perspektivet Java som illustreret på billedet herunder.
Når du bruger JET, skal du beslutte, om du vil distribuere dine skabeloner sammen med programmet eller kun distribuere skabelonimplementeringsklasserne.
Hvis dit mål er at forenkle dit programs tekstgenereringsfunktioner, er det et godt valg at bruge JET-naturen og JET-byggeprogrammet til at omdanne dine skabeloner automatisk. Der er flere oplysninger i JET-øveprogram del 1. I det tilfælde behøver du blot at distribuere de omdannede skabelonimplementeringsklasser sammen med programmet, ikke selve skabelonerne.
Hvis det derimod er vigtigt for programmet, at brugerne har fuld kontrol over den genererede tekst, vil du måske distribuere selve skabelonfilerne sammen med programmet. I så fald skal disse skabeloner omdannes, hver gang der genereres tekst. Den plugin, vi har skrevet i denne artikel, er et eksempel på denne type program.
Denne artikel har beskrevet, hvilke klasser der er til rådighed i JET-pakken til dette formål, og vist, hvordan disse klasser bruges sammen med en Eclipse-plugin. Appendikset herunder indeholder en oversigt over JET API'et og viser, hvordan det kan bruges i hovedløse og enkeltstående programmer.
Klasse | Beskrivelse |
---|---|
CodeGen |
Klassen CodeGen kan omdanne en JET-skabelon til Java-kildekode og eventuelt flette Java-kildekoden til skabelonimplementering sammen med en eksisterende Java-klasse. CodeGen kan bruges som et hovedløst Eclipse-program. Metoden run forventer en parameter med typen strengarray bestående af to eller tre elementer:
|
CodeGenPlugin | Plugin-klassen til JET-pakken. |
Klasse | Beskrivelse |
---|---|
IJETNature | Grænseflade, der udvider org.eclipse.core.resources.IProjectNature. Definerer nogle af egenskaberne for en JET-natur. Implementeres af JETNature. Bruges som filter for projektegenskabssiderne af plugin'en org.eclipse.emf.codegen.ui. |
JETAddNatureOperation | En org.eclipse.core.resources.IWorkspaceRunnable-forekomst til tilføjelse af JET-naturen til et projekt i arbejdsområdet. Bruges af AddJETNatureAction i plugin'en org.eclipse.emf.codegen.ui. |
JETBuilder | Denne klasse udvider org.eclipse.core.resources.IncrementalProjectBuilder. Når dens build-metode kaldes, delegerer den til JETCompileTemplateOperation for at få omdannet alle skabeloner i arbejdsområdeprojektet, der er ændret siden sidste bygning. Skabelonerne skal være placeret i en af de foldere, der er angivet som Skabelonopbevaring i projektets JET-natur. |
JETCharDataGenerator | Er ansvarlig for en del af processen med omdannelse af skabeloner. Genererer strenge for de tegndata, der findes i skabelonfilen. Bruges af JETCompiler. |
JETCompiler | Dette er kerneklassen til omdannelse af skabeloner. Klassen har ansvaret for at omdanne skabeloner til Java-kildekoden til en skabelonimplementeringsklasse. Den egentlige omdannelse delegeres til andre klasser i denne pakke. JETParser bruges til at analysere skabelonen og opdele den i skabelonelementer. JETCompiler implementerer grænsefladen JETParseEventListener og registrerer sig i parseren for at få besked, når parseren genkender et skabelonelement. For hvert genkendt skabelonelement bruger JETCompiler en JETGenerator-forekomst til at omdanne skabelonelementet til Java-kildekode. Når skabelonanalysen er færdig, bruger JETCompiler en forekomst af JETSkeleton til at foretage assemblering af Java-kildekodeelmenterne, så de udgør en enkelt kompileringsenhed (en Java-klasse). |
JETCompileTemplateOperation | Denne klasse implementerer org.eclipse.core.resources.IWorkspaceRunnable, så den kan udføres som en batchfunktion i arbejdsområdet. Funktionen skal have et arbejdsområdeprojekt, et eller flere skabelonopbevaringssteder og eventuelt en liste med bestemte skabelonfiler som konstruktørparametre. Når dens run-metode kaldes, bruger den JETCompiler til at omdanne skabelonfilerne i arbejdsområdeprojektets angivne foldere til Java-kildefiler til skabelonimplementeringsklasser. Funktionen kan valgfrit konfigureres, så den udløser en fuldstændig bygning af projektet, når den er færdig, for at kompilere Java-kildefilerne til .class-filer. |
JETConstantDataGenerator | Er ansvarlig for en del af processen med omdannelse af skabeloner. Udvider JETCharDataGenerator for at generere konstanterklæringer for de strenge med tegndata, der findes i skabelonfilen. |
JETCoreElement | Grænseflade til centrale JET-syntakselementer (direktiv, udtryk, minikommandofil (scriptlet) og escape af anførselstegn). Bruges af JETParser. |
JETEmitter | Denne klasse er et praktisk API på højt niveau for brugere af denne pakke. Metoden generate i denne klasse omdanner en skabelon til Java-kildekode, kompilerer kildekoden til en skabelonimplementeringsklasse, anmoder skabelonklassen om at generere tekst og returnerer til sidst det genererede resultat. Klassen opretter et Java-projekt ved navn .JETEmitters i arbejdsområdet, omdanner skabelonen ind i dette projekt og kalder build på projektet .JETEmitters for at få kompileret kildekoden. Hvis omdannelsen eller kompileringen mislykkes, afsendes undtagelsen JETException. En Java-klasse til skabelonimplementering "udføres" ved, at dens generate-metode kaldes. |
JETException | Udvider org.eclipse.core.runtime.CoreException, men stiller mere praktiske konstruktører til rådighed. |
JETExpressionGenerator | Er ansvarlig for en del af processen med omdannelse af skabeloner. Udvider JETScriptletGenerator for at omdanne JET-udtryk (i <%= ... %>) til Java-kildekode. |
JETGenerator | Grænseflade til generatorer: Klasser, der er i stand til at omdanne en del af en JET-skabelon til et element i Java-kildekoden. |
JETMark | Et tilstandsobjekt, som bruges af JETParser til at markere punkter i JET-tegninputstrømmen og delegere behandlingen af dele af strømmen til andre objekter. |
JETNature |
Denne klasse implementerer IJETNature, så den kan konfigurere et arbejdsområdeprojekt med JET-naturen. Når denne natur tilføjes til et projekt, tilføjer den et JET-byggeprogram øverst i byggespecifikationen til projektet. Naturen definerer to egenskaber:
Disse egenskaber bruges af JET-byggeprogrammet under en bygning. |
JETParseEventListener | Grænseflade til objekter, der er i stand til at behandle dele af en JET-tegninputstrøm. |
JETParser | Hovedparserklassen. Har flere indre klasser, der genkender centrale JET-syntakselementer (direktiv, udtryk, minikommandofil (scriptlet) og escape af anførselstegn). Når et centralt JET-syntakselement er genkendt, delegeres den egentlige behandling af elementet til JETParseEventListener. |
JETReader | En inputbuffer til JET-parseren. Leverer metoden stackStream, som andre kan kalde med tegnstrømmen til en inkluderingsfil. Stiller også mange andre bekvemmelighedsmetoder til rådighed for parseren. |
JETScriptletGenerator | Er ansvarlig for en del af processen med omdannelse af skabeloner. Omdanner JET-minikommandofiler (scriptlets) (i <% ... %>) til Java-kildekode. |
JETSkeleton | Denne klasse leverer en grænseflade til assemblering af Java-kildekodeelementer i en enkelt Java-kompileringsenhed (en Java-klasse). Der foretages assemblering af Java-kildekodeelementer i henhold til en klasseskeletdefinition. Et skelet kan bruges til at tilføje genbrugskode til en omdannet skabelonimplementeringsklasse. Klassen stiller en tilpasset standardskeletdefinition til skabelonimplementeringsklasser til rådighed, men kan også foretage assemblering af Java-elementer ved hjælp af et tilpasset skelet. Den egentlige analyse og generering af Java-kildekode delegeres til klasser i pakken org.eclipse.jdt.core.jdom. |
Klasse | Beskrivelse |
---|---|
JControlModel | En kontrolmodel, der leverer ordbøger og regler, som driver en fletteproces. |
JMerger | En klasse til fletning af Java-kildefiler. Bruger implementering af grænsefladerne i pakken org.eclipse.emf.codegen.merge.java.facade til at analysere kildekoden. Klassen kan bruges af programkode. |
JPatternDictionary | En ordbog med signaturer og Java-noder. |
Klasse | Beskrivelse |
---|---|
PropertyMerger | En klasse til fletning af egenskabsfiler. Klassen kan bruges af programkode. |
Klassen org.eclipse.emf.codegen.CodeGen kan omdanne en JET-skabelon til Java-kildekode og eventuelt flette Java-kildekoden til skabelonimplementering sammen med en eksisterende Java-klasse. CodeGen kan bruges som et hovedløst Eclipse-rogram ("hovedløst" betyder, at brugergrænsefladen i Eclipse ikke startes). Folderen plugins/org.eclipse.emf.codegen/test i Eclipse-installationen indeholder nogle scripts, der kan bruges til at starte klassen CodeGen som et hovedløst Eclipse-program. Disse scripts er i UNIX-format.
Nedenfor ses et eksempel på et script til Windows. Bemærk, at der overføres to argumenter til klassen CodeGen:
Hvis målstien allerede indeholder et resultat af en tidligere omdannelse, og du vil flette det nye omdannelsesresultat med det eksisterende, kan du angive kontrolmodelfilen JMerge som det tredje argument. Folderen plugins/org.eclipse.emf.codegen/test i Eclipse-installationen indeholder et eksempel på en merge.xml-fil.
@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 Shut down Eclipse before running this script. java %OPTS% %MAIN% -application org.eclipse.emf.codegen.CodeGen %ARGUMENTS%
Forfatter: Knut Wannheden (knut.wannheden snabel-a paranor.ch)
Binært format: jetc-task.jar.
Kilden: JETCTask.java.
Nogle bemærkninger:
Her er en simpel Ant-byggefil (taskdef-classpath forudsætter, at du har Eclipse 3.3 og 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> <!-- Usage example 1: --> <!-- Specify the template file in the "template" attribute. --> <!-- You can use the "class" and "package" attributes to override the --> <!-- "class" and "package" attributes in the template file. --> <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> <!-- Usage example 2: --> <!-- Translate a bunch of template files at once. --> <!-- You cannot use the "class" and "package" attributes when using a fileset. --> <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 (By Joshua Bloch)
Java Tip 122: Beware of Java typesafe enumerations (By Vladimir Roubtsov)Java Tip 133: More on typesafe enums (By Philip Bishop)
Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States, other countries, or both.