© Copyright Azzurri Ltd. 2003, 2004. Με την επιφύλαξη παντός δικαιώματος
 Άρθρο του Eclipse Corner

Πρόγραμμα εκμάθησης JET Μέρος 2 (Σύνταξη κώδικα που δημιουργεί κώδικα)

Σύνοψη

Στο Μέρος 2 του προγράμματος εκμάθησης JET (Java Emitter Templates), θα ασχοληθούμε με το API της μηχανής JET. Θα μάθετε πώς να συντάσσετε πρόσθετες λειτουργίες που χρησιμοποιούν τις κλάσεις στο πακέτο JET για τη δημιουργία πρωτογενούς κώδικα Java.

Ως ένα ρεαλιστικό παράδειγμα, θα δημιουργήσουμε μια πρόσθετη λειτουργία που δέχεται καταχωρήσεις πληροφοριών από τον χρήστη και δημιουργεί μια κλάση απαρίθμησης ασφαλούς είδους. Ο δημιουργημένος πρωτογενής κώδικας βασίζεται σε ένα πρότυπο JET και μπορεί να διανεμηθεί με την πρόσθετη λειτουργία, επιτρέποντας στους χρήστες της πρόσθετης λειτουργίας να προσαρμόσουν τον δημιουργημένο κώδικα τροποποιώντας το πρότυπο.

Αυτό το άρθρο παρέχει μια σύντομη παραπομπή στο JET API.

Συνεισφορά από Remko Popma, Azzurri Ltd., remko.popma at azzurri dot jp, 26 Αυγούστου 2003. Χρησιμοποιείται με άδεια. Τελευταία ενημέρωση: 3 Ιανουαρίου 2007.


Περιεχόμενα

Εισαγωγή
Μερικές κλάσεις JET
Μια πρόσθετη λειτουργία που δημιουργεί πρωτογενή κώδικα
Συμπέρασμα
Παράρτημα
Πόροι

Εισαγωγή

Μετατροπή και δημιουργία

Ένα χαρακτηριστικό των προτύπων JET που αρχικά προκαλεί σύγχυση είναι ότι για τη δημιουργία κειμένου χρειάζονται δύο βήματα: μετατροπή και δημιουργία. Το πρώτο βήμα είναι η μετατροπή του προτύπου σε μια κλάση υλοποίησης προτύπου. Το δεύτερο βήμα είναι η χρήση αυτής της κλάσης υλοποίησης προτύπου για τη δημιουργία κειμένου.

Αν ο στόχος σας είναι να δημιουργήσετε με το JET πρωτογενή κώδικα Java, το γεγονός ότι το βήμα μετατροπής του προτύπου έχει επίσης ως αποτέλεσμα τη δημιουργία πρωτογενούς κώδικα Java ενδεχομένως να προκαλέσει σύγχυση. Αυτός ο πρωτογενής κώδικας δεν είναι το δημιουργημένο κείμενο. Ο πρωτογενής κώδικας που δημιουργείται κατά το βήμα μετατροπής είναι απλά μια άλλη μορφή του προτύπου.

Αν έχετε χρησιμοποιήσει στο παρελθόν JSP και μικροεφαρμογές εξυπηρετητή, θεωρήστε ένα πρότυπο JET ισοδύναμο με μια σελίδα JSP. Ένα πρότυπο JET μετατρέπεται σε κλάση υλοποίησης προτύπου, με τον ίδιο τρόπο που μια σελίδα JSP μετατρέπεται σε μικροεφαρμογή εξυπηρετητή. Το δεύτερο βήμα, όπου η κλάση υλοποίησης προτύπου δημιουργεί κείμενο, είναι ισοδύναμο με μια μικροεφαρμογή εξυπηρετητή που δημιουργεί και επιστρέφει HTML.

Το Μέρος 1 αυτού του προγράμματος εκμάθησης παρουσίασε τα πρότυπα JET και εξήγησε τον τρόπο μετατροπής ενός έργου σε έργο JET ώστε το εργαλείο δόμησης JET να μετατρέψει αυτόματα τα πρότυπα του έργου σας σε κλάσεις υλοποίησης προτύπων.

Στο δεύτερο μέρος αυτού του προγράμματος εκμάθησης, θα εστιάσουμε στη σύνταξη μιας πρόσθετης λειτουργίας που χρησιμοποιεί τις κλάσεις του πακέτου JET για τη δημιουργία πρωτογενούς κώδικα Java. Μια πρόσθετη λειτουργία που δημιουργεί κείμενο από πρότυπο JET δεν μπορεί πλέον να στηρίζεται στη φύση JET και στο εργαλείο δόμησης JET για την αυτόματη μετατροπή προτύπων. Αυτό οφείλεται στο ότι η φύση JET και το εργαλείο δόμησης JET λειτουργούν μόνο σε έργα χώρου εργασίας και όχι σε πρόσθετες λειτουργίες. Οι πρόσθετες λειτουργίες πρέπει να χρησιμοποιούν τις κλάσεις του πακέτου JET για τη μετατροπή των προτύπων τους.

Για την εκτέλεση του παραδείγματος ή την εμφάνιση του πρωτογενούς κώδικα για αυτό το άρθρο μπορείτε να αποσυμπιέσετε το αρχείο org.eclipse.emf.examples.jet.article2_2.3.0.zip στον υποκατάλογο plugins. Για τη χρήση του παραδείγματος πρόσθετης λειτουργίας, πρέπει να έχετε εγκατεστημένο το EMF. Στο παρόν πρόγραμμα εκμάθησης χρησιμοποιείται η έκδοση 2.3.0 M4.

Η επόμενη ενότητα θα ασχοληθεί με μερικές από τις κλάσεις στο πακέτο org.eclipse.emf.codegen. Θα ακολουθήσουμε τα απαραίτητα βήματα για τη δημιουργία πρωτογενούς κώδικα με το JET και θα εξετάσουμε τον ρόλο των κλάσεων της μηχανής JET στην όλη διαδικασία. Αν ανυπομονείτε να δείτε κώδικα που παρουσιάζει στην πράξη τη χρήση αυτών των κλάσεων, μπορείτε να μεταβείτε απευθείας στην ενότητα Μια πρόσθετη λειτουργία που δημιουργεί πρωτογενή κώδικα.

Μερικές κλάσεις JET

Σε αυτή την ενότητα, θα εξετάσουμε προσεκτικά ορισμένες από τις κλάσεις του πακέτου JET. Μπορούν κατά προσέγγιση να χωριστούν σε δύο κατηγορίες:

Οι κλάσεις χαμηλότερου επιπέδου δεν εξετάζονται στο παρόν άρθρο. Για μια περιγραφή όλων των κλάσεων στην πρόσθετη λειτουργία org.eclipse.emf.codegen, δείτε την ενότητα Επισκόπηση του JET API παρακάτω. Στη συνέχεια της ενότητας, θα εστιάσουμε σε μερικές από τις κλάσεις υψηλότερου επιπέδου.

org.eclipse.emf.codegen.jet.JETCompiler

Η κλάση JETCompiler είναι η κεντρική κλάση για τη μετατροπές προτύπων. Η κλάση αυτή είναι υπεύθυνη για τη μετατροπή προτύπων σε πρωτογενή κώδικα Java μιας κλάσης υλοποίησης προτύπου. Η ίδια η μετατροπή ανατίθεται σε άλλες κλάσεις στο ίδιο πακέτο. Οι πελάτες δημιουργούν ένα αντικείμενο JETCompiler για ένα συγκεκριμένο πρότυπο, και καλούν τη μέθοδο parse και στη συνέχεια τη μέθοδο generate για την εγγραφή του πρωτογενούς κώδικα για την παραγόμενη κλάση υλοποίησης προτύπου σε μια καθορισμένη ροή.

org.eclipse.emf.codegen.jet.JETEmitter

Η κλάση JETEmitter παρέχει ένα εύχρηστο API υψηλού επιπέδου για χρήστες του πακέτου JET. Η μέθοδος generate αυτής της κλάσης συνδυάζει μετατροπή προτύπου και δημιουργία κειμένου σε μια κίνηση. Η κλάση JETEmitter χειρίζεται όλες τις λεπτομέρειες της μετατροπής προτύπων και της μεταγλώττισης του πρωτογενούς κώδικα Java της κλάσης υλοποίησης προτύπου, επιτρέποντάς σας να συγκεντρωθείτε στα τελικά δεδομένα εξόδου της γεννήτριας.

Θα μπορούσε να θεωρηθεί ότι η κλάση JETEmitter αφαιρεί το βήμα μετατροπής, κάνοντας τη διαδικασία να φαίνεται σαν απευθείας δημιουργία κειμένου με ένα πρότυπο. Σύμφωνα με το νόμο Law of Leaky Abstractions, αυτό δεν μπορεί να ισχύει πάντα. Η ενότητα Πιθανά σφάλματα κατά τη χρήση της κλάσης JETEmitter παρακάτω αναφέρει ορισμένα σημεία στα οποία πρέπει να είστε προσεκτικοί.

Η κλάση JETEmitter είναι η κλάση που θα χρησιμοποιούμε στην πρόσθετη λειτουργία μας και για αυτό θα ασχοληθούμε μαζί της λίγο περισσότερο.

Ένα αντικείμενο JETEmitter δομείται με το uri του προτύπου που χρησιμοποιείται για δημιουργία κειμένου. Όσο υπάρχει διαθέσιμη ρουτίνα χειρισμού πρωτοκόλλου, κάθε είδος uri είναι αποδεκτό. Αυτό σημαίνει ότι μπορούν να χρησιμοποιηθούν uri file:/, ftp:/ και http:/. Το Eclipse προσθέτει ειδικές ρουτίνες χειρισμού πρωτοκόλλων για uri platform:/base/, platform:/plugin/, platform:/fragment/ και platform:/resource/, συνεπώς οι πρόσθετες λειτουργίες μπορούν να χρησιμοποιούν ένα uri όπως το platform:/resource/myproject/myfolder/mytemplate.jet για να προσδιορίσουν ένα αρχείο προτύπου. Σημείωση: Η έκδοση Eclipse 3.0 εισήγαγε το bundleentry στη λίστα των διαθέσιμων πρωτοκόλλων του. Πρέπει να χρησιμοποιείται σε παραπομπές στοιχείων του Eclipse όπως πρόσθετες λειτουργίες και λειτουργίες.

Στο παράδειγμα πρόσθετης λειτουργίας μας, θα διανείμουμε το αρχείο προτύπου μαζί με την πρόσθετη λειτουργία, συνεπώς το αρχείο προτύπου θα βρίσκεται στον φάκελο myplugin/templates στο φάκελο plugins του Eclipse. Ο ακόλουθος κώδικας μπορεί να χρησιμοποιηθεί για τον εντοπισμό και τη δημιουργία ενός προτύπου από αυτόν το φάκελο:

 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});

Μετά την κατασκευή ενός αντικειμένου JETEmitter, οι πελάτες καλούν το generate για τη δημιουργία κειμένου. Η μέθοδος generate θα εκτελέσει τα ακόλουθα βήματα:

  1. Θα δημιουργήσει ένα έργο με όνομα .JETEmitters στο χώρο εργασίας
  2. Θα προετοιμάσει το έργο ορίζοντας τη φύση Java και προσθέτοντας μεταβλητές διαδρομής κλάσεων στη διαδρομή κλάσεών του
  3. Θα μετατρέψει το πρότυπο σε αρχείο πρωτογενούς κώδικα υλοποίησης προτύπου Java στο έργο .JETEmitters
  4. Θα δομήσει το έργο για τη μεταγλώττιση του πρωτογενούς κώδικα υλοποίησης προτύπου σε ένα αρχείο .class Java
  5. Θα καλέσει τη μέθοδο generate στην κλάση υλοποίησης προτύπου Java που υπέστη μετατροπή και θα επιστρέψει το δημιουργημένο κείμενο ως σειρά χαρακτήρων

* Το .JETEmitters είναι το προεπιλεγμένο όνομα του έργου που δημιουργείται κατά τη μετατροπή του προτύπου. Αυτή η τιμή μπορεί να αλλάξει με χρήση της μεθόδου setProjectName.

Το παράδειγμα πρόσθετης λειτουργίας μας θα χρησιμοποιήσει το JETEmitter και θα αποθηκεύσει το δημιουργημένο κείμενο σε ένα αρχείο πρωτογενούς κώδικα στον χώρο εργασίας. Το παρακάτω σχήμα απεικονίζει τα βήματα για τη δημιουργία πρωτογενούς κώδικα με τη χρήση του JETEmitter.

Χρήση του JETEmitter για τη δημιουργία κειμένου από μια πρόσθετη λειτουργία

Πιθανά σφάλματα κατά τη χρήση της κλάσης JETEmitter

Η κλάση JETEmitter συνδυάζει μετατροπή προτύπου και δημιουργία κειμένου σε ένα και μόνο βήμα, κάτι που την κάνει ένα ιδιαίτερα χρήσιμο εργαλείο. Ωστόσο, είναι σημαντικό να γνωρίζετε τι συμβαίνει κάτω από την επιφάνεια ώστε να αποφύγετε δυσάρεστες εκπλήξεις. Αυτή η ενότητα επισημαίνει κάποιες "παγίδες" που έχω εντοπίσει, οι οποίες σας βοηθήσουν να μην κάνετε τα ίδια σφάλματα.

1. Απαίτηση απόδοσης αρχικών τιμών στην πρόσθετη λειτουργία

Η χρήση του JET εκτός του Eclipse δεν είναι εύκολη. Το JET είναι σχεδιασμένο να εκτελείται μόνο ως εφαρμογή χώρου εργασίας. Όποια εφαρμογή χρησιμοποιεί το JET πρέπει να εκτελείται, κατά ελάχιστο, ως εφαρμογή Eclipse σε κατάσταση "headless", ώστε να επιτελείται η απόδοση αρχικών τιμών. (Ο όρος headless αναφέρεται στην εκτέλεση του Eclipse χωρίς το περιβάλλον χρήστη.)

Αυτό σημαίνει ότι η χρήση του JETEmitter από μια απλή ανεξάρτητη εφαρμογή (μια βασική κλάση Java με μια μέθοδο main) δεν θα λειτουργήσει:

 // 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);

Σημειώστε ότι αυτός ο περιορισμός δεν ισχύει μόνο για την κλάση JETEmitter. Πολλές από τις κλάσεις της πρόσθετης λειτουργίας org.eclipse.emf.codegen έχουν εξαρτήσεις από άλλες πρόσθετες λειτουργίες. Η ενότητα Παράρτημα παρακάτω περιέχει περισσότερες λεπτομέρειες για τη χρήση ανεξάρτητων εφαρμογών JET.

Στη συνέχεια αυτού του άρθρου θα θεωρήσουμε ότι ο κώδικάς μας εκτελείται από μια πρόσθετη λειτουργία.

2. Ζητήματα φορτωτή κλάσεων

Ενδεχομένως να λάβετε ένα σφάλμα NoClassDefFoundError όταν μεταβιβάζετε ένα προσαρμοσμένο αντικείμενο ως όρισμα στη μέθοδο JETEmitter.generate. Αυτό μπορεί να συμβεί αν το αντικείμενο που μεταβιβάζετε ως όρισμα δεν είναι μια από τις κλάσεις java "bootstrap" (οι κλάσεις bootstrap είναι οι κλάσεις χρόνου εκτέλεσης στο rt.jar και οι κλάσεις διεθνοποίησης στο i18n.jar).

Για την αποτροπή αυτού του σφάλματος πρέπει να ορίσετε τον φορτωτή κλάσεων της πρόσθετης λειτουργίας σας όταν χρησιμοποιείτε το JETEmitter. Αν δεν έχει οριστεί φορτωτής κλάσεων, το JETEmitter χρησιμοποιεί τον φορτωτή κλάσεων της δικής του κλάσης, ο οποίος είναι συνήθως ο φορτωτής κλάσεων για την πρόσθετη λειτουργία org.eclipse.emf.codegen, ο οποίος είναι περιορισμένων δυνατοτήτων. Σε πρόσφατες εκδόσεις του EMF (από την έκδοση 1.1.0 δόμηση 20030527_0913VL), το JETEmitter έχει μια λειτουργία κατασκευής που λαμβάνει ένα όρισμα φορτωτή κλάσεων.

Σημειώστε ότι ένας άλλος τρόπος καθορισμού ενός φορτωτή κλάσεων είναι η μετατροπή του JETEmitter σε υποκλάση στο δικό σας έργο. Αν δεν έχει οριστεί φορτωτής κλάσεων, το JETEmitter θα χρησιμοποιήσει τον φορτωτή κλάσεων αυτής της υποκλάσης. (Αν χρησιμοποιείτε παλαιότερη έκδοση του EMF, δεν θα έχετε άλλη επιλογή από το να μετατρέψετε σε υποκλάση το JETEmitter στο έργο σας, εφόσον δεν υπάρχει λειτουργία κατασκευής που να λαμβάνει ορίσματα φορτωτή κλάσεων.)

Το παράδειγμα που ακολουθεί παρουσιάζει μια κλάση ενέργειας που μετατρέπει και καλεί ένα επιλεγμένο πρότυπο χρησιμοποιώντας το JETEmitter. Το παράδειγμα παρουσιάζει τον τρόπο κατασκευής ενός JETEmitter με μια παράμετρο φορτωτή κλάσεων ή μέσω της κατασκευής μιας ανώνυμης υποκλάσης.

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
}

3. Ζητήματα διαδρομής κλάσεων

Το JETEmitter μετατρέπει τα πρότυπά σας σε αρχεία πρωτογενούς κώδικα Java στο έργο .JETEmitters, ενώ καλεί το JavaBuilder για τη μεταγλώττιση αυτών των αρχείων πρωτογενούς κώδικα. Αν τα πρότυπα σας χρησιμοποιούν μη τυπικές κλάσεις Java, ή αν δεν βρίσκονται στην πρόσθετη λειτουργία EMF, θα χρειαστεί να προσθέσετε αυτές τις κλάσεις στη διαδρομή κλάσεων του έργου .JETEmitters, αλλιώς το JavaBuilder δεν θα μπορεί να μεταγλωττίσει τα αρχεία πρωτογενούς κώδικα υλοποίησης προτύπων. Ευτυχώς, το JETEmitter παρέχει έναν απλό τρόπο για να το κάνετε αυτό μέσω της μεθόδου addVariable που προσθέτει μια μεταβλητή διαδρομής κλάσεων στο έργο .JETEmitter.

Μια μεταβλητή διαδρομής κλάσεων είναι ένα όνομα καθολικό στο χώρο εργασίας που χρησιμοποιείται στο Eclipse για την αναφορά σε ένα αρχείο JAR ή έναν κατάλογο. Η λίστα όλων αυτών των μεταβλητών εμφανίζεται χρησιμοποιώντας την ενέργεια μενού Παράθυρο > Προτιμήσεις > Java > Μεταβλητές διαδρομής κλάσεων. Το πρόγραμμά σας θα πρέπει να προσθέσει μια μεταβλητή διαδρομής κλάσεων για κάθε αρχείο JAR ή κατάλογο που απαιτείται στη διαδρομή κλάσεων του έργου .JETEmitter.

Μια πρόσθετη λειτουργία που δημιουργεί πρωτογενή κώδικα

Σε αυτό το τμήμα του προγράμματος εκμάθησης JET, θα συντάξουμε μια πρόσθετη λειτουργία Eclipse που χρησιμοποιεί ένα πρότυπο JET για τη δημιουργία πρωτογενούς κώδικα Java για απαριθμήσεις με ασφάλεια είδους (typesafe), οι οποίες ήταν αρκετά δημοφιλείς πριν τη Java 5.0, όταν εισήχθησαν απαριθμήσεις στη γλώσσα.

Η πρόσθετη λειτουργία μας πρέπει να εκτελεί τις ακόλουθες εργασίες:

  1. Συλλογή τιμών δεδομένων εισόδου χρήστη για τις μεταβλητές προτύπου: όνομα κλάσης, είδος και όνομα των γνωρισμάτων της κλάσης απαρίθμησης ασφαλούς είδους και τιμές αυτών των γνωρισμάτων για κάθε χρήση. Θα συντάξουμε ένα απλό GUI για τη συλλογή αυτών των τιμών.
  2. Μετατροπή ενός αρχείου προτύπου JET σε κλάση υλοποίησης προτύπου Java
  3. Κλήση της κλάσης υλοποίησης προτύπου με ένα αντικείμενο που περιέχει τις τιμές δεδομένων εισόδου χρήστη που συνέλεξε το GUI
  4. Αποθήκευση του δημιουργημένου πρωτογενούς κώδικα σε μια θέση που λαμβάνεται από το GUI

Στην ακόλουθη ενότητα θα ακολουθήσουμε τα παραπάνω βήματα ένα προς ένα.

Απαριθμήσεις ασφαλούς είδους

Ας εξετάσουμε μια απαρίθμηση ασφαλούς είδους για να δούμε τι είδους πρωτογενή κώδικα θέλουμε να δημιουργήσουμε. Η κλάση Digit παρακάτω αποτελεί ένα παράδειγμα απαρίθμησης ασφαλούς είδους.

 // 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(); }
 }

Ας εξετάσουμε με μεγαλύτερη προσοχή αυτή την κλάση. Πρώτον, η κλάση Digit έχει διάφορες χρήσεις - οι σταθερές ZERO, ONE, TWO, κ.τλ. Κάθε χρήση ορίζεται από το όνομα μεταβλητής Java, "ZERO", "ONE", "TWO"..., και τις τιμές για κάθε γνώρισμα της κλάσης απαρίθμησης. Οι περισσότερες απαριθμήσεις ασφαλούς είδους έχουν ένα ή περισσότερα γνωρίσματα. Η κλάση Digit έχει δύο γνωρίσματα: μια τιμή ακέραιου και ένα όνομα σειράς χαρακτήρων.

Το παράδειγμα κλάσης Digit που χρησιμοποιούμε έχει επίσης μια μέθοδο lookup, η οποία επιστρέφει τη χρήση της οποίας το γνώρισμα value ισοδυναμεί με την καθορισμένη παράμετρο int. Μια μέθοδος lookup εισάγει την έννοια του κύριου γνωρίσματος. Πολλές απαριθμήσεις ασφαλούς είδους έχουν ένα ή περισσότερα γνωρίσματα που διακρίνουν με μοναδικό τρόπο τη μια χρήση από την άλλη.

Σημειώστε ότι τα κύρια γνωρίσματα δεν είναι απαραίτητα: το Java VM εγγυάται ότι κάθε νέο αντικείμενο είναι μοναδικό, συνεπώς είναι δυνατή η ύπαρξη απαριθμήσεων ασφαλούς είδους χωρίς κανένα απολύτως γνώρισμα. Σε αυτή την περίπτωση, οι χρήσεις των απαριθμήσεων διακρίνονται μέσω του τελεστή ταυτότητας χρήσης ==. Αν και αυτό λειτουργεί σωστά, είναι συχνά χρήσιμο να έχετε ένα κύριο γνώρισμα που να διακρίνει μια χρήση και μια μέθοδο lookup που να εντοπίζει μια χρήση για ένα καθορισμένο κύριο γνώρισμα.

Το πρότυπό μας έχει μια μέθοδο lookup, συνεπώς πρέπει να αποφασίσουμε τι θα κάνουμε αν δεν βρεθεί χρήση για το καθορισμένο κύριο γνώρισμα. Ουσιαστικά υπάρχουν τρεις επιλογές: η εμφάνιση μιας εξαίρεσης, η επιστροφή μιας καθορισμένης "προεπιλεγμένης" χρήσης ή η επιστροφή της τιμής null. Ποια επιλογή είναι η καλύτερη εξαρτάται κυρίως από την εφαρμογή στη οποία χρησιμοποιείται η κλάση και έτσι θα ήταν προτιμότερο να αφήσουμε την απόφαση αυτή στον χρήστη.

Τώρα που εξετάσαμε τις απαριθμήσεις ασφαλούς είδους λεπτομερώς, ας συνοψίσουμε τα στοιχεία που μπορούμε να προσαρμόσουμε σε μια απαρίθμηση ασφαλούς είδους:

Ένα απλό μοντέλο απαρίθμησης ασφαλούς είδους

Ένα απλό μοντέλο για τα προσαρμόσιμα τμήματα μιας απαρίθμησης ασφαλούς είδους μπορεί να έχει την εξής μορφή:

TypesafeEnum
getInstances() : Instance[]
getAttributes() : Attribute[]
getKeyAttributes() : Attribute[]
getDefaultInstance() : Instance
getPackageName() : String
getClassName() : String

Instance
getName() : String
getAttributeValues() : Properties
getAttributeValue(Attribute) : String
isDefault() : boolean

Attribute
getName() : String
getType() : String
isKey() : boolean

Στην επόμενη ενότητα θα χρησιμοποιήσουμε αυτές τις κλάσεις για να μετατρέψουμε την κλάση Digit σε ένα πρότυπο JET για απαριθμήσεις ασφαλούς είδους.

Πρότυπο απαρίθμησης ασφαλούς είδους

Τώρα που έχουμε ένα μοντέλο, μπορούμε να πάρουμε την κλάση Digit και να αντικαταστήσουμε όλο τον κώδικα της κλάσης Digit με μικροσενάρια JET και εκφράσεις που καλούν τα μοντέλα κλάσεών μας. Το πρότυπο που θα προκύψει μπορεί να έχει την ακόλουθη μορφή:

 <%@ 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...
 }

Όπως μπορείτε να δείτε, το πρότυπο καλεί μερικές μεθόδους που δεν υπήρχαν στο απλό μοντέλο που εισαγάγαμε νωρίτερα. Προσθέσαμε ορισμένες μεθόδους διευκόλυνσης, όπως τις μεθόδους Attribute.getCappedName() και getUncappedName(). Τέτοιες μέθοδοι βοηθούν στην διατήρηση της απλότητας του προτύπου.

Ένα άλλο παράδειγμα μεθόδων που προσθέσαμε στο μοντέλο είναι η μέθοδος TypesafeEnum.constructorParameterDescription() και η μέθοδος Instance.constructorValues(). Η υλοποίηση της μεθόδου constructorValues εμφανίζεται παρακάτω.

// 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();
}

Η μέθοδος constructorValues ανατρέχει στα γνωρίσματα της απαρίθμησης ασφαλούς είδους, εντοπίζει την τιμή για κάθε γνώρισμα στη χρήση και συνενώνει αυτές τις τιμές σε μια σειρά χαρακτήρων διαχωρίζοντας τις με κόμμα. Για παράδειγμα, στην κλάση απαρίθμησης ασφαλούς είδους Digit παραπάνω, η μέθοδος αυτή θα επέστρεφε "0, \"zero\"" για τη χρήση "ZERO".

Θα μπορούσαμε να εξετάσουμε τις τιμές γνωρισμάτων στο πρότυπο, αλλά κάτι τέτοιο θα είχε κάνει το πρότυπο δυσανάγνωστο. Η μεταβίβαση αυτής της λογικής στο μοντέλο έκανε το πρότυπο ευανάγνωστο και ευκολότερο στη συντήρηση. Από την άλλη, χάσαμε λίγη ευελιξία γιατί οι χρήστες δεν μπορούν πλέον να προσαρμόσουν αυτή τη λογική τροποποιώντας το πρότυπο. Αυτός είναι και ο συμβιβασμός που πρέπει να κάνετε. Το τι είναι καλύτερο εξαρτάται από το πρότυπο και την εφαρμογή σας.

Ένα γραφικό περιβάλλον για τη συλλογή δεδομένων εισόδου χρήστη

Τώρα που έχουμε ένα μοντέλο και ένα πρότυπο, χρειαζόμαστε ακόμα δύο στοιχεία για να ολοκληρώσουμε τη πρόσθετη λειτουργία: χρειαζόμαστε ένα γραφικό περιβάλλον που θα συλλέγει τιμές από το χρήστη με τις οποίες θα συμπληρώνει το μοντέλο, και πρέπει να καλέσουμε το πρότυπο με το συμπληρωμένο μοντέλο για τη δημιουργία του πρωτογενούς κώδικα και την αποθήκευσή του σε μια θέση στο χώρο εργασίας.

Ας ξεκινήσουμε με το γραφικό περιβάλλον. Ο χώρος εργασίας παρέχει μερικούς οδηγούς που εκτελούν λειτουργίες παρόμοιες με αυτήν που θέλουμε, όπως για παράδειγμα οδηγούς δημιουργίας κλάσης, δημιουργίας διεπαφής και δημιουργίας σεναρίου δοκιμής JUnit. Θα ήταν λογικό να δώσουμε στο γραφικό περιβάλλον μας παρόμοια εμφάνιση με αυτούς τους οδηγούς και να το προσθέσουμε στο βασικό μενού και στη γραμμή εργαλείων.

Ο οδηγός μας έχει τρεις σελίδες. Η πρώτη σελίδα, η οποία εμφανίζεται παρακάτω, μοιάζει με μια απλοποιημένη έκδοση του οδηγού δημιουργίας κλάσης. Στην πραγματικότητα, χρησιμοποιούμε το ίδιο πλαίσιο που χρησιμοποιεί ο οδηγός δημιουργίας κλάσης, δηλαδή το πακέτο org.eclipse.jdt.ui.wizards. Στην πρώτη σελίδα, συλλέγουμε το όνομα πακέτου και το όνομα κλάσης της απαρίθμησης ασφαλούς είδους και τη θέση αποθήκευσης του αποτελέσματος.

Πρώτη σελίδα του οδηγού γραφικού περιβάλλοντος: κλάση, πακέτο και θέση της απαρίθμησης ασφαλούς είδους

Η δεύτερη σελίδα συλλέγει πληροφορίες για τα γνωρίσματα της κλάσης απαρίθμησης ασφαλούς είδους. Κάθε γνώρισμα έχει ένα όνομα και ένα είδος και ενδεχομένως να είναι ένα από τα κύρια γνωρίσματα. Η δεύτερη σελίδα του οδηγού μας απεικονίζεται παρακάτω:

Δεύτερη σελίδα του οδηγού γραφικού περιβάλλοντος: γνωρίσματα της απαρίθμησης ασφαλούς είδους

Η τρίτη και τελευταία σελίδα του οδηγού μας, η οποία απεικονίζεται παρακάτω, συλλέγει πληροφορίες για τις χρήσεις της απαρίθμησης ασφαλούς είδους. Ο χρήστης εισάγει το όνομα χρήσης και παρέχει για κάθε χρήση τιμές για όλα τα γνωρίσματα.

Τέλος, μια από τις χρήσεις μπορεί να είναι η χρήση "default", η οποία είναι η χρήση που επιστρέφεται από τη μέθοδο lookup αν δεν βρεθεί καμία χρήση για τις καθορισμένες τιμές κύριου γνωρίσματος.

Τρίτη σελίδα του οδηγού γραφικού περιβάλλοντος: χρήσεις της απαρίθμησης ασφαλούς είδους

Κλήση προτύπου

Τώρα που έχουμε ένα γραφικό περιβάλλον για τη συμπλήρωση του μοντέλου μας, μπορούμε επιτέλους να χρησιμοποιήσουμε ό,τι μάθαμε στο πρώτο μέρος αυτού του άρθρου και να δημιουργήσουμε πρωτογενή κώδικα με το πρότυπο μας.

Όταν ένας χρήστης πατήσει Ολοκλήρωση στον οδηγό, καλείται στον οδηγό μας η μέθοδος performFinish. Ο κώδικας παρακάτω απεικονίζει τον τρόπο χρήσης μιας προσαρμοσμένης υποκλάσης του JETEmitter προκειμένου να προσθέσουμε το αρχείο JAR της πρόσθετης λειτουργίας μας στη διαδρομή κλάσεων του έργου .JETEmitters προτού πραγματοποιήσουμε κλήση του generate στο JETEmitter. Ο δημιουργημένος πρωτογενής κώδικας απαρίθμησης ασφαλούς είδους αποθηκεύεται στη θέση του χώρου εργασίας που όρισε ο χρήστης.

 // 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);
 }

Εγγραφή του οδηγού

Το τελευταίο τμήμα κώδικα παρακάτω απεικονίζει το τμήμα του αρχείου ρυθμίσεων plugin.xml όπου εγγράφουμε τον οδηγό μας ως μια συνεισφορά για το χώρο εργασίας.

   <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>

Τώρα ο οδηγός μας ενεργοποιείται όταν οι χρήστες πατήσουν Αρχείο > Δημιουργία > Άλλο > Java > Typesafe Enum από το χώρο εργασίας, όπως απεικονίζεται στην εικόνα παρακάτω.

Εμφάνιση οδηγού Typesafe Enum στον οδηγό δημιουργίας

Σημειώστε ότι ορίσαμε το γνώρισμα javatype ως true στον στοιχείο extension του οδηγού στο αρχείο plugin.xml. Αυτό θα έχει ως αποτέλεσμα ο οδηγός να εμφανίζεται ως ενέργεια στη γραμμή εργαλείων της προοπτικής Java, όπως απεικονίζεται στην εικόνα παρακάτω.

Εμφάνιση οδηγού απαρίθμησης ασφαλούς είδους ως ενέργειας στη γραμμή εργαλείων της προοπτικής Java

Συμπέρασμα

Το JET μπορεί να αποδειχθεί ιδιαίτερα χρήσιμο για εφαρμογές που πρέπει να δημιουργήσουν κείμενο. Όσο σημαντική εξέλιξη ήταν οι σελίδες JSP συγκριτικά με παλαιότερου στυλ μικροεφαρμογές εξυπηρετητή, άλλο τόσο είναι και τα πρότυπα αναφορικά με τη δημιουργία κώδικα.

Κατά τη χρήση του JET, πρέπει να αποφασίσετε αν θέλετε να διανείμετε τα πρότυπά σας μαζί με την εφαρμογή σας ή να διανείμετε μόνο τις κλάσεις υλοποίησης προτύπου.

Αν στόχος σας είναι η απλοποίηση των δυνατοτήτων δημιουργίας κειμένου της εφαρμογής σας, η χρήση της φύσης JET και του εργαλείου δόμησης JET για την αυτόματη μετατροπή των προτύπων σας είναι μια καλή επιλογή. Ανατρέξτε στο θέμα Πρόγραμμα εκμάθησης JET Μέρος 1 για λεπτομέρειες. Σε αυτήν την περίπτωση, το μόνο που χρειάζεται να κάνετε είναι να διανείμετε τις κλάσεις υλοποίησης προτύπου που έχουν υποστεί μετατροπή με την εφαρμογή σας, και όχι τα ίδια τα πρότυπα.

Από την άλλη, αν είναι σημαντικό για την εφαρμογή σας να έχουν οι χρήστες ολοκληρωτικό έλεγχο του δημιουργημένου κειμένου, πιθανώς να προτιμήσετε να διανείμετε τα ίδια τα αρχεία προτύπου μαζί με την εφαρμογή σας. Σε αυτήν την περίπτωση, θα χρειαστεί να μετατρέπετε αυτά τα πρότυπα κάθε φορά που δημιουργείτε κείμενο. Η πρόσθετη λειτουργία που δημιουργήσαμε σε αυτό το άρθρο αποτελεί ένα παράδειγμα αυτού του είδους εφαρμογής.

Σε αυτό το άρθρο εξηγήθηκε ποιες κλάσεις είναι διαθέσιμες στο πακέτο JET για να πετύχουν αυτόν τον σκοπό και παρουσιάστηκε ο τρόπος χρήσης αυτών των κλάσεων με μια πρόσθετη λειτουργία του Eclipse. Το ακόλουθο παράρτημα παρέχει μια επισκόπηση του JET API και παρουσιάζει τον τρόπο χρήσης του σε ανεξάρτητες ή χωρίς γραφικό περιβάλλον εφαρμογές.

Παράρτημα

Επισκόπηση του JET APΙ

Πακέτο org.eclipse.emf.codegen

Κλάση Περιγραφή
CodeGen

Η κλάση CodeGen μπορεί να μετατρέψει ένα πρότυπο JET σε πρωτογενή κώδικα Java και προαιρετικά να συγχωνεύσει τον πρωτογενή κώδικα Java υλοποίησης προτύπου με μια υπαρκτή κλάση Java. Το CodeGen μπορεί να χρησιμοποιηθεί ως μια εφαρμογή Eclipse χωρίς γραφικό περιβάλλον. Η μέθοδος run αναμένει μια παράμετρο πίνακα σειράς χαρακτήρων αποτελούμενη από δύο ή τρία στοιχεία:

  • το uri του προτύπου προς μετατροπή
  • η διαδρομή προορισμού όπου θα αποθηκευτεί το αποτέλεσμα της μετατροπής
  • ένα προαιρετικό αρχείο μοντέλου ελέγχου JMerge που καθορίζει τον τρόπο συγχώνευσης του νέου αποτελέσματος μετατροπής με τον πρωτογενή κώδικα μιας υπάρχουσας κλάσης Java
CodeGenPlugin Η κλάση πρόσθετης λειτουργίας για το πακέτο JET.

Πακέτο org.eclipse.emf.codegen.jet

Κλάση Περιγραφή
IJETNature Διεπαφή που επεκτείνει το org.eclipse.core.resources.IProjectNature. Καθορίζει μερικές από τις ιδιότητες που έχει μια φύση JET. Υλοποιείται από το JETNature. Χρησιμοποιείται ως φίλτρο για σελίδες ιδιοτήτων έργου από την πρόσθετη λειτουργία org.eclipse.emf.codegen.ui.
JETAddNatureOperation Ένα org.eclipse.core.resources.IWorkspaceRunnable για την προσθήκη φύσης JET σε ένα έργο στο χώρο εργασίας. Χρησιμοποιείται από το AddJETNatureAction στην πρόσθετη λειτουργία org.eclipse.emf.codegen.ui.
JETBuilder Αυτή η κλάση επεκτείνει το org.eclipse.core.resources.IncrementalProjectBuilder. Όταν καλείται η μέθοδος build, αναθέτει στην κλάση JETCompileTemplateOperation τη μετατροπή όλων των προτύπων του έργου χώρου εργασίας που έχουν υποστεί τροποποιηθεί μετά την προηγούμενη δόμηση. Τα πρότυπα πρέπει να βρίσκονται σε έναν από τους φακέλους που έχουν οριστεί ως θέσεις υποδοχής προτύπων στη φύση JET του έργου.
JETCharDataGenerator Υπεύθυνο για ένα μέρος της διαδικασίας μετατροπής προτύπων. Δημιουργεί σειρές χαρακτήρων για τα δεδομένα χαρακτήρων που βρίσκονται στο αρχείο προτύπου. Χρησιμοποιείται από το JETCompiler.
JETCompiler Αυτή είναι η κύρια κλάση για τη μετατροπή προτύπων. Η κλάση αυτή είναι υπεύθυνη για τη μετατροπή προτύπων σε πρωτογενή κώδικα Java μιας κλάσης υλοποίησης προτύπου. Η πραγματική μετατροπή ανατίθεται σε άλλες κλάσεις του πακέτου. Η κλάση JETParser χρησιμοποιείται για τη συντακτική ανάλυση του προτύπου σε στοιχεία προτύπου. Η κλάση JETCompiler υλοποιεί τη διεπαφή JETParseEventListener και καταχωρεί τον εαυτό της μαζί με τον συντακτικό αναλυτή για να ειδοποιηθεί όταν ο συντακτικός αναλυτής αναγνωρίζει ένα στοιχείο προτύπου. Για κάθε αναγνωρισμένο στοιχείο προτύπου, το JETCompiler χρησιμοποιεί το JETGenerator για τη μετατροπή στοιχείων προτύπου σε πρωτογενή κώδικα Java. Όταν η συντακτική ανάλυση προτύπου ολοκληρωθεί, το JETCompiler χρησιμοποιεί ένα JETSkeleton για να συγκροτήσει τα στοιχεία του πρωτογενή κώδικα Java σε μία και μόνο μονάδα μεταγλώττισης (μια κλάση Java).
JETCompileTemplateOperation Αυτή η κλάση υλοποιεί το org.eclipse.core.resources.IWorkspaceRunnable ώστε να εκτελείται ως μαζική λειτουργία στο χώρο εργασίας. Αυτή η λειτουργία δέχεται ένα έργο χώρου εργασίας, μια ή περισσότερες Θέσεις υποδοχής προτύπων και προαιρετικά μια λίστα συγκεκριμένων αρχείων προτύπου ως παραμέτρους δόμησης. Όταν καλείται η μέθοδος run, χρησιμοποιεί ένα JETCompiler για να μετατρέψει τα αρχεία προτύπου στους καθορισμένους φακέλους του χώρου εργασίας σε αρχεία πρωτογενούς κώδικα για κλάσεις υλοποίησης προτύπου. Αυτή η λειτουργία μπορεί προαιρετικά να ρυθμιστεί ώστε με την ολοκλήρωσή της να εκτελέσει μια αυτόματη ενεργοποίηση μιας ολοκληρωμένης δόμησης του έργου για τη μεταγλώττιση αρχείων πρωτογενούς κώδικα Java σε αρχεία .class.
JETConstantDataGenerator Υπεύθυνο για ένα μέρος της διαδικασίας μετατροπής προτύπων. Επεκτείνει το JETCharDataGenerator για να δημιουργήσει σταθερές δηλώσεις για τις σειρές χαρακτήρων με δεδομένα χαρακτήρα που βρίσκονται στο αρχείο προτύπου.
JETCoreElement Διεπαφή για βασικά στοιχεία σύνταξης JET (οδηγία, έκραση, μικροσενάριο και quote-escape). Χρησιμοποιείται από το JETParser.
JETEmitter Αυτή η κλάση παρέχει ένα χρήσιμο API υψηλού επιπέδου για χρήστες αυτού του πακέτου. Η μέθοδος generate αυτής της κλάσης μετατρέπει ένα πρότυπο σε πρωτογενή κώδικα Java, μεταγλωττίζει αυτόν τον πρωτογενή κώδικα σε κλάση υλοποίησης προτύπου, ζητεί την κλάση προτύπου για τη δημιουργία κειμένου και τέλος, επιστρέφει το δημιουργημένο αποτέλεσμα. Αυτή η κλάση δημιουργεί ένα έργο Java με την ονομασία .JETEmitters στον χώρο εργασίας, μετατρέπει το πρότυπο του έργου και απλά πραγματοποιεί κλήση build στο έργο .JETEmitters για τη μεταγλώττιση του πρωτογενή κώδικα. Αν αποτύχει η μετατροπή ή μεταγλώττιση, προκύπτει μια εξαίρεση JETException. Μια κλάση υλοποίησης προτύπου Java εκτελείται με την κλήση της μεθόδου generate.
JETException Επεκτείνει την κλάση org.eclipse.core.runtime.CoreException, αλλά παρέχει περισσότερο εύχρηστες λειτουργίες κατασκευής.
JETExpressionGenerator Υπεύθυνο για ένα μέρος της διαδικασίας μετατροπής προτύπων. Επεκτείνει το JETScriptletGenerator για τη μετατροπή εκφράσεων JET (<%= ... %> stuff) σε πρωτογενή κώδικα Java.
JETGenerator Διεπαφή για γεννήτριες: κλάσεις που γνωρίζουν τον τρόπο μετατροπής μέρους προτύπου JET σε στοιχείο πρωτογενή κώδικα Java.
JETMark Ένα αντικείμενο κατάστασης που χρησιμοποιείται από το JETParser για να επισημάνει σημεία στην ροή εισόδου χαρακτήρων JET και να αναθέσει τη διεργασία των μερών της ροής σε άλλα αντικείμενα.
JETNature

Αυτή η κλάση υλοποιεί το IJETNature ώστε να μπορεί να πραγματοποιήσει παραμετροποίηση του χώρου εργασίας με το JET Nature. Όταν προστίθεται αυτή η φύση σε ένα έργο, προσθέτει ένα εργαλείο δόμησης JET μπροστά από τις προδιαγραφές δόμησης του έργου. Αυτή η φύση καθορίζει δύο ιδιότητες:

  • Θέσεις υποδοχής προτύπων - μια λίστα φακέλων στο έργο που περιέχουν τα πρότυπα JET προς μετατροπή.
  • Θέση υποδοχής πρωτογενούς κώδικα - ο φάκελος προορισμού στον οποίο θα αποθηκευθούν κλάσεις υλοποίησης προτύπων που έχουν υποστεί μετατροπή.

Αυτές οι ιδιότητες χρησιμοποιούνται από το Εργαλείο δόμησης JET όταν εκτελείται μια δόμηση.

JETParseEventListener Διεπαφή για αντικείμενα που γνωρίζουν τον τρόπο διεργασίας μερών της ροής εισόδου χαρακτήρων JET.
JETParser Η κύρια κλάση συντακτικής ανάλυσης. Έχει διάφορες εσωτερικές κλάσεις για την αναγνώριση κύριων στοιχείων σύνταξης JET (οδηγία, έκφραση, μικροσενάριο και quote-escape). Όταν ένα κύριο στοιχείο σύνταξης JET αναγνωρίζεται, η πραγματική διεργασία του στοιχείου ανατίθεται σε ένα JETParseEventListener.
JETReader Μια ενδιάμεση μνήμη εισόδου για τον συντακτικό αναλυτή JET. Παρέχει μια μέθοδο stackStream που μπορούν να καλέσουν άλλοι με ροή χαρακτήρων σε ένα συμπεριλαμβανόμενο αρχείο (include file). Επίσης, παρέχει πολλές άλλες μεθόδους διευκόλυνσης για τον συντακτικό αναλυτή.
JETScriptletGenerator Υπεύθυνο για ένα μέρος της διαδικασίας μετατροπής προτύπων. Μετατρέπει μικροσενάρια JET (<% ... %> stuff) σε πρωτογενή κώδικα Java.
JETSkeleton Αυτή η κλάση παρέχει μια διεπαφή για την συγκέντρωση στοιχείων πρωτογενή κώδικα Java σε μια μονάδα μεταγλώττισης Java (μια κλάση Java). Τα στοιχεία πρωτογενή κώδικα Java συγκεντρώνονται με γνώμονα τον ορισμό σκελετού κλάσης. Ένας σκελετός μπορεί να χρησιμοποιηθεί για την πρόσθεση τυποποιημένου κώδικα (boilerplate code) σε μια κλάση υλοποίησης προτύπου που έχει υποστεί μετατροπή. Αυτή η κλάση παρέχει έναν προεπιλεγμένο και προσαρμοσμένο ορισμό σκελετού κλάσης υλοποίησης προτύπου, αλλά μπορεί επίσης να συγκεντρώσει στοιχεία Java χρησιμοποιώντας έναν προσαρμοσμένο σκελετό. Η πραγματική συντακτική ανάλυση και δημιουργία του πρωτογενή κώδικα Java ανατίθεται σε κλάσεις του πακέτου org.eclipse.jdt.core.jdom.

Πακέτο org.eclipse.emf.codegen.merge.java

Κλάση Περιγραφή
JControlModel Ένα μοντέλο ελέγχου που παρέχει λεξικά και κανόνες για την καθοδήγηση μιας διεργασίας συγχώνευσης.
JMerger Μια κλάση για συγχώνευση αρχείων πρωτογενούς κώδικα Java. Χρησιμοποιεί υλοποίηση των διεπαφών στο πακέτο org.eclipse.emf.codegen.merge.java.facade για τη συντακτική ανάλυση του πρωτογενή κώδικα. Αυτή η κλάση μπορεί να χρησιμοποιηθεί από τον κώδικα εφαρμογών.
JPatternDictionary Ένα λεξικό υπογραφών και κόμβων java.

Πακέτο org.eclipse.emf.codegen.merge.properties

Κλάση Περιγραφή
PropertyMerger Μια κλάση για συγχώνευση αρχείων ιδιοτήτων. Αυτή η κλάση μπορεί να χρησιμοποιηθεί από κώδικα εφαρμογών.

Εκτέλεση του CodeGen ως εφαρμογής Eclipse χωρίς γραφικό περιβάλλον

Η κλάση org.eclipse.emf.codegen.CodeGen μπορεί να μετατρέψει ένα πρότυπο JET σε πρωτογενή κώδικα Java και προαιρετικά να συγχωνεύσει την υλοποίηση προτύπου πρωτογενή κώδικα Java με μια υπάρχουσα κλάση Java. Το CodeGen μπορεί να χρησιμοποιηθεί ως μια εφαρμογή Eclipse χωρίς γραφικό περιβάλλον (headless application) ("headless" σημαίνει πως το GUI του Eclipse δεν έχει ξεκινήσει). Ο φάκελος plugins/org.eclipse.emf.codegen/test στην εγκατάσταση του Eclipse περιέχει μερικά σενάρια για εκκίνηση της κλάσης CodeGen ως μια εφαρμογή Eclipse χωρίς γραφικό περιβάλλον. Αυτά τα σενάρια είναι σε μορφοποίηση Unix.

Ακολουθεί ένα παράδειγμα σεναρίου για Windows. Σημειώστε πως μεταβιβάζουμε δύο ορίσματα στην κλάση CodeGen:

Αν η διαδρομή προορισμού περιέχει ήδη ένα προγενέστερο αποτέλεσμα μετατροπής και θέλετε να συγχωνεύσετε το νέο αποτέλεσμα μετατροπής με το υπάρχον, μπορείτε να ορίσετε ένα αρχείο μοντέλου ελέγχου JMerge ως το τρίτο όρισμα. Ο plugins/org.eclipse.emf.codegen/test φάκελος σην εγκατάσταση του Eclipse σας περιέχει ένα παράδειγμα αρχείου 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 Shut down Eclipse before running this script.
   java %OPTS% %MAIN% -application org.eclipse.emf.codegen.CodeGen %ARGUMENTS%

jetc: Μια εργασία ANT για μετατροπή προτύπων JET εκτός του Eclipse

Author: Knut Wannheden (knut.wannheden at paranor.ch)

Binary: jetc-task.jar.

The source: JETCTask.java.

Μερικές σημειώσεις:

Ακολουθεί ένα απλό αρχείο δόμησης Ant (η κλάση taskdef υποθέτει πως έχετε Eclipse 3.3 και 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)

http://www.eclipse.org/emf/

Ο όρος Java και όλα τα βασιζόμενα στον όρο Java εμπορικά σήματα και λογότυπα είναι εμπορικά σήματα ή σήματα κατατεθέντα της Sun Microsystems, Inc. στις Ηνωμένες Πολιτείες ή/και σε άλλες χώρες.