I used to have a miniature portable electronic address Book. I always thought it was good until one day it stopped running. The person who sold the product was unable to retrieve my contact address list, but proposed a replacement. That's when I learned the importance of the data. This shiny little invention is nothing compared to the data stored in it.
In the first installment of this series, I introduced the development environment for the Eclipse plug-in and developed a simple plugin. In the second section, I added toolbar buttons, menu items, and dialog boxes. It does not actually implement any specific functionality. It simply displays the sample text content in a font. Now we want it to be able to manage the actual data. We will modify this plugin to enable it to implement the functionality we need. This article discusses the editor documentation and shows you how to customize a wizard.
The history of Invokatron
First, let us elaborate on the invokatron itself. As we discussed in the previous article, Invokatron is a graphical tool for generating Java code. You can simply create a class using drag-and-drop operations. The method dragged in is called by the edited method (that is, the plug-in). We will have data to drive the design of the application. In the later article, we will develop this GUI. Now what we need to do is find the important data that the plug-in will enter and store. It is often referred to as the model of the application. When designing this system, we need to consider some of the following:
· Which detail data need to be saved?
· What does this data represent in memory? POJO, JavaBean or EJB?
· What is the storage format for this data? database table, XML file, property file, or serial binary file?
· What are the types of input data? Use the new File Wizard or a pop-up dialog box on the document Properties page, draw with the editor, and enter other wizards in the text editor?
These questions must be answered before we continue our work. There can be no answer to all the items; It depends entirely on your needs. In our example, I made some random, possibly problematic decisions, as follows:
· A Java class that contains class names, packages, superclass (superclass), and implementation interfaces. We base it on it and add more data to the following articles.
· I'm going to show the data as classes that extend the properties class. It establishes the "document class" of the editor.
· The format I'll use is a property file, and it's easy to use the properties class to analyze it.
· In the new File Wizard, I'll look for the data and then let the user change the data in the Properties window or text editor. This step will be completed in the next article.
Document (Documents) class
The next step is to write the document class. Create a new package (Invokatron.model) and a new Class (Invokatrondocument). The following is the beginning of our document class:
public class Invokatrondocument
Extends Properties
{
public static final String PACKAGE = "PACKAGE";
public static final String superclass = "superclass";
public static final String interfaces = "Interfaces";
}
Using the properties class makes it easier to analyze and save our data. Getter and setter are not required, but they can be added if you want to. This class is not finished yet; We will add an interface in which eclipse needs to use it.
With this class, it's very easy to get a property:
String Package =document.getproperty (invokatrondocument.package);
Custom Wizards
Take a look at the wizard that appears in the previous article. You should remember that we can access it by clicking on the toolbar button or menu item (which we added). Figure 1 is its interface:
Figure 1: The old Wizard
It has only one page and there is no picture in the upper right corner. We would like to enter more information and provide a good picture. In other words, we want to customize this wizard.
Let's analyze the guide. Please open the Invokatronwizard.java file. Notice how this class extends the wizard and implements the Inewwizard interface. You should understand a lot of ways in it. To customize the wizard, we simply call or overload some of these methods. Here are some important ways to do this:
Life cycle Approach
We should overload these methods and insert initialization and destructor (destruction) code into the wizard:
· Constructor (constructor): When the wizard instantiates, it is invoked before eclipse passes information to it. The general initialization implementation of the wizard. Usually you want to invoke the "landscaping method" (described later) and set the default value for the dialog box.
· Init (Iworkbench Workbench, Istructuredselection editorselection): Eclipse calls it to provide the information for the Workbench to the wizard. Please overload it and save the Iworkbench and object handles for later use. If it's an editor wizard instead of a new wizard, we'd better take the current editor option as the second argument.
· Dispose (): Eclipse calls it to perform cleanup work. Overload it to clear the resources used by the wizard.
· Finalize (): Clears the code, possibly using Dispose () instead.
Landscaping Methods
These methods are used to decorate the wizard form.
· Setwindowtitle (string title): Sets the header line string for the form.
· Setdefaultpageimagedescriptor (imagedescriptor image): Used to provide a picture that appears in the upper right of all pages of the wizard.
· Settitlebarcolor (RGB color): Specifies what color the title bar is in.
Button Method
These methods control the practicality and behavior of the wizard buttons.
· Boolean Canfinish (): Overloads it to specify whether the Finish button is active (depending on the state of the wizard).
· Boolean Performfinish (): Overloads it to implement the fundamental business logic of the wizard. Returns false if the wizard does not complete (the wrong condition).
· Boolean Performcancel (): Overload it to purge when the user clicks the Cancel button. Returns false if the wizard cannot terminate.
· Boolean ishelpavailable (): Overloads it to specify whether the Help button is visible.
· Boolean needspreviousandnextbuttons (): Overloads it to specify whether the previous (previous step) and Next (step) buttons are visible.
· Boolean Needsprogressmonitor (): Overloads it to specify whether the progress bar part is visible. When you click the Finish button to invoke the Performfinish () method, it appears.
Page Method
These methods control the appearance of the page.
· Addpages (): Called when the wizard displays. Overload it to insert a new page into the wizard.
· Createpagecontrols (composite pagecontainer): Eclipse calls it to instantiate all of the wizard pages (pages that have been added with the previous addpages () method). Overload it adds a continuously visible form widget to the wizard (except for parts that are not on the page).
· Iwizardpage Getstartingpage (): Overload it to detect which page is the first page of the wizard.
· Iwizardpage getnextpage (Iwizardpage nextPage): By default, clicking the Next button will enter the next page in the array provided by Addpages (). You may want to enter a different page depending on the user's choice. Overload it to compute the latter page.
· Iwizardpage getpreviouspage (Iwizardpage previouspage): Similar to Getnextpage () for calculating the previous page.
· int Getpagecount (): Retrieves the number of pages added by Addpages (). In a typical case, you don't have to overload it unless you want to display the number and form of the page.
Other useful methods
These are useful helper methods:
· Setdialogsettings (Idialogsettings settings): You can load the state of the dialog box and set the values by calling this method in Init (). In typical cases, these settings can be the default values for wizard fields. Check out the Dialogsettings class for more detailed information.
· Idialogsettings getdialogsettings (): When we need data, we call this method to retrieve it. At the end of the Performfinish () dialog box, you can save the data to a file again.
· Iwizardcontainer GetContainer (): Useful for retrieving shells, running background threads, refreshing windows, and so on.
Wizard page method
As you can see, the wizard is made up of one or more pages. These pages extend the Wizardpage class and implement the Iwizardpage interface. To customize a single page, you have to know a lot of ways. Here are some important ways to do this:
· Constructor: for instantiating pages.
Dispose (): Overloads it to implement the purge code.
CreateControl (composite parent): Overloads it to add controls to the page.
· IWizard Getwizard (): Used to get the parent Wizard object. is useful for calling Getdialogsettings ().
settitle (string title): Called to set the string that is displayed in the title Area of the wizard.
setdescription (String description): Called to provide the textual content shown below the title.
setimagedescriptor (imagedescriptor image): Called to provide a picture (used instead of the default picture) that appears in the upper-right corner of the page.
setmessage (String message): Called to display the text of the messages below the description string. The text is used to warn or prompt the user.
seterrormessage (string error): Called to display the text of the message below the description string in high brightness. It generally means that the wizard cannot continue unless the error is corrected.
Setpagecomplete (boolean complete): If the True,next button is visible.
Performhelp (): Overloads it to provide content-sensitive help information. The wizard will call it when you click the Help button.
Write Wizard code
With these methods, we are able to develop a wizard with great flexibility. We now modify the previously established Invokatron Wizard to add a page to request the user to enter the initial document data. We also added a picture to the wizard. The new code is bold:
public class Invokatronwizard extends Wizard
Implements Inewwizard {
Private Invokatronwizardpage page;
Private InvokatronWizardPage2 Page2;
Private Iselection selection;
Public Invokatronwizard () {
Super ();
Setneedsprogressmonitor (TRUE);
Imagedescriptor image =abstractuiplugin.imagedescriptorfromplugin ("Invokatron", "icons/invokatronicon32.gif");
Setdefaultpageimagedescriptor (image);
}
public void Init (Iworkbench workbench,istructuredselection selection) {
This.selection = Selection;
}
In the constructor, we open the progress bar and set up a picture of the wizard. You can download and save the following picture:
Please save this picture under the Invokatron/icons folder. To make it easier to load this image, we use the convenient Abstractuiplugin.imagedescriptorfromplugin () method.
Note: You should know that although this wizard is Inewwizard type, not all wizards are used to create new documents. You can refer to some other information to learn how to create a "standalone" wizard.
The following is the Addpages () method:
public void Addpages () {
Page=new Invokatronwizardpage (selection);
AddPage (page);
Page2 = new InvokatronWizardPage2 (selection);
AddPage (Page2);
}
In this method, we add a new page (InvokatronWizardPage2) and we edit it later. Here are some ways to execute when the user clicks the "Finish" button on the wizard:
public Boolean performfinish () {
First, save all the page data in a variable
Final String containername = Page.getcontainername ();
Final String fileName =page.getfilename ();
Final Invokatrondocument properties = new Invokatrondocument ();
Properties.setproperty (Invokatrondocument.package,page2.getpackage ());
Properties.setproperty (Invokatrondocument.superclass,page2.getsuperclass ());
Properties.setproperty (Invokatrondocument.interfaces,page2.getinterfaces ());
Now the call completes (finish) method
Irunnablewithprogress op =new irunnablewithprogress () {
public void run (Iprogressmonitor monitor)
Throws InvocationTargetException {
try {
Dofinish (ContainerName, filename,properties,monitor);
catch (Coreexception e) {
throw new InvocationTargetException (e);
finally {
Monitor.done ();
}
}
};
try {
GetContainer (). Run (True, false, OP);
catch (Interruptedexception e) {
return false;
catch (InvocationTargetException e) {
Throwable realexception =e.gettargetexception ();
Messagedialog.openerror (Getshell (), "Error", Realexception.getmessage ());
return false;
}
return true;
}
In order to save the data, we have to do a background transaction. The transaction is performed by the wizard's container (Eclipse Workbench) and must implement the Irunnablewithprogress interface, containing (unique) a run () method. The iprogressmonitor passed in allows us to report the progress of the transaction. The actual data save work is performed in an auxiliary method (Dofinish ()):
private void Dofinish (String containername,string fileName, properties Properties,
Iprogressmonitor monitor)
Throws Coreexception {
Create a sample file
Monitor.begintask ("Creating" + FileName, 2);
Iworkspaceroot root = Resourcesplugin.getworkspace (). Getroot ();
Iresource resource = Root.findmember (new Path (containername));
if (!resource.exists () | |! ( Resource instanceof IContainer)) {
Throwcoreexception ("Container \" "+ containername +" \ "does not exist.");
}
IContainer container = (icontainer) resource;
Final IFile ifile = container.getfile (new Path (FileName));
Final file File =ifile.getlocation (). ToFile ();
try {
OutputStream OS = new FileOutputStream (file, false);
Properties.store (OS, NULL);
Os.close ();
catch (IOException e) {
E.printstacktrace ();
Throwcoreexception ("Error writing to File" + file.tostring ());
}
Make sure that the project is refreshed and that the file is built outside of the eclipse API
Container.refreshlocal (iresource.depth_infinite, monitor);
monitor.worked (1);
Monitor.settaskname ("Opening file for editing ...");
Getshell (). Getdisplay (). Asyncexec (New Runnable () {
public void Run () {
Iworkbenchpage page =platformui.getworkbench (). Getactiveworkbenchwindow (). GetActivePage ();
try {
Ide.openeditor (page,ifile,true);
catch (Partinitexception e) {
}
}
});
monitor.worked (1);
}
We also do a lot of work:
· We searched the location where we wanted to save the file (with Eclipse's IFile class).
· We also obtained the file.
· We've saved the property to this location.
· We then have the Eclipse Workbench refresh the project so that the file can be displayed.
· We finally scheduled a transaction that will be executed at a later time. This transaction includes opening the new file in the editor.
· Throughout the process, we prompt the user about the current progress by calling the Iprogressmonitor object, which is passed in as a parameter.
The last method is an auxiliary method that displays an error message in the wizard when the file fails to save:
private void Throwcoreexception (String message) throws Coreexception {
Istatus Status =new status (Istatus.error, "Invokatron", istatus.ok,message,null);
throw new Coreexception (status);
}
}
The wizard can catch the coreexception exception and then display the status object it contains to the user. The wizard will not be closed.
Code to write a new wizard page
Next, we write InvokatronWizardPage2. Its entire class is brand NEW:
public class InvokatronWizardPage2 extends Wizardpage {
Private Text Packagetext;
Private Text Superclasstext;
Private Text Interfacestext;
Private Iselection selection;
Public InvokatronWizardPage2 (iselection selection) {
Super ("WizardPage2");
Settitle ("Invokatron Wizard");
SetDescription ("This wizard creates a new" + "file with *.invokatron extension.");
This.selection = Selection;
}
private void UpdateStatus (String message) {
Seterrormessage (message);
Setpagecomplete (message = = NULL);
}
Public String Getpackage () {
return Packagetext.gettext ();
}
Public String Getsuperclass () {
return Superclasstext.gettext ();
}
Public String getinterfaces () {
return Interfacestext.gettext ();
}
The above constructor sets the title of the page (high-brightness display below the title bar) and description (shown below the page title). We also have some auxiliary methods. UpdateStatus handles the display of page-specific error messages. If there is no error message, it means that the page is finished, so the Next button is available. There is also the getter (get) method for the content of the data field. The following is the CreateControl () method, which establishes all the visual components of a page:
public void CreateControl (composite parent) {
Composite Controls =new Composite (parent, SWT. NULL);
GridLayout layout = new GridLayout ();
Controls.setlayout (layout);
Layout.numcolumns = 3;
layout.verticalspacing = 9;
Label label =new label (controls, SWT. NULL);
Label.settext ("&package:");
Packagetext = new Text (CONTROLS,SWT. BORDER | Swt. Single);
Griddata gd = new Griddata (griddata.fill_horizontal);
Packagetext.setlayoutdata (GD);
Packagetext.addmodifylistener (
New Modifylistener () {
public void Modifytext (Modifyevent e) {
Dialogchanged ();
}
});
Label = new label (controls, SWT. NULL);
Label.settext ("Blank = Default package");
Label = new label (controls, SWT. NULL);
Label.settext ("&superclass:");
Superclasstext = new Text (CONTROLS,SWT. BORDER | Swt. Single);
GD = new Griddata (griddata.fill_horizontal);
Superclasstext.setlayoutdata (GD);
Superclasstext.addmodifylistener (New Modifylistener () {
public void Modifytext (Modifyevent e) {
Dialogchanged ();
}
});
Label = new label (controls, SWT. NULL);
Label.settext ("Blank = Object");
Label = new label (controls, SWT. NULL);
Label.settext ("&interfaces:");
Interfacestext = new Text (CONTROLS,SWT. BORDER | Swt. Single);
GD = new Griddata (griddata.fill_horizontal);
Interfacestext.setlayoutdata (GD);
Interfacestext.addmodifylistener (
New Modifylistener () {
public void Modifytext (Modifyevent e) {
Dialogchanged ();
}
});
Label = new label (controls, SWT. NULL);
Label.settext ("Separated by ', '");
Dialogchanged ();
Setcontrol (controls);
}
In order to write this code, you have to understand SWT (please check some of this information yourself). Basically, this method creates labels and fields and places them on the grid layout. When the field changes, the dialogchanged () is invoked to validate its data:
private void dialogchanged () {
String apackage = Getpackage ();
String Asuperclass = Getsuperclass ();
String interfaces = Getinterfaces ();
String status = New Packagevalidator (). IsValid (Apackage);
if (status!= null) {updatestatus (status);
Return
}
Status = New Superclassvalidator (). IsValid (Asuperclass);
if (status!= null) {updatestatus (status);
Return
}
Status = New Interfacesvalidator (). IsValid (interfaces);
if (status!= null) {updatestatus (status);
Return
}
UpdateStatus (NULL);
}
}
This work was done with the help of three tool classes--packagevalidator, Superclassvalidator and Interfacesvalidator. Next we write these classes.
Validation class
Validation can be done in any part of the plug-in's user input data. Therefore, it makes sense to put the validation code into reusable classes so that you don't have to copy it to multiple locations. The following is an example of a validation class.
public class Interfacesvalidator implements Icelleditorvalidator
{
Public String isValid (Object value)
{
if (! (value instanceof String))
return null;
string interfaces = ((String) value). Trim ();
if (Interfaces.equals (""))
return null;
string[] Interfacearray = Interfaces.split (",");
for (int i = 0; i < interfacearray.length; i++)
{
Istatus status = Javaconventions.validatejavatypename (Interfacearray[i]);
if (Status.getcode ()!= Istatus.ok)
Return "Validation of Interface" + Interfacearray[i] + ":" + status.getmessage ();
}
return null;
}
}
Other validation classes are very similar to it.
Another excellent class in the Eclipse class library is javaconventions, which validates the data for us! It contains a number of validation methods, such as:
· Validatejavatypename () checks the names of classes and interfaces.
· Validatepackagename () checks the name of the package.
· Validatefieldname () checks the name of the data member.
· Validatemethodname () checks the name of the method.
· Validateidentifiername () checks the name of the variable.
Now we don't need the Icelleditorvalidator interface, but in future articles, we need it.
Results
So far, we have a working wizard that has a picture and two pages, and the second page builds the original Invokatron document. Figure 2 shows the results:
Figure 2: Custom wizards
shiny Inventions.
As we can see, it's usually the data-driven application. Appearance (presentation) is also very important. Ugly inventions are hard to sell, but shiny inventions can be easily sold. But data is a very essential thing for us programmers to implement.
In this article, we first determine the data that we will be working on. Then we get the data in the form of a custom wizard. The next article will continue to explain the issues that are displayed, including custom editors and property pages.