Home > Eclipse development, Software Development > Writing an Eclipse Plug-in (Part 10): Custom Project: Creating a Custom File Type

Writing an Eclipse Plug-in (Part 10): Custom Project: Creating a Custom File Type


Happy Halloween! Trick or treat!

Ah! I love the smell of plug-in progress in the morning. Who knows? Somewhere around Part 42 there might be enough done to actually accomplish something.

Speaking of which: our custom project doesn’t do very much at this point. While there is plenty to add to the custom navigator I fear that the customplugin project has been feeling neglected.

I have a few things to add to the customplugin before returning to the navigator; the navigator will continue to burn a significant amount of my attention…unless I decide to go back to my genetic programming examples. Maybe I’ll toss a coin to see which I decide to do next. Or maybe I’ll just check on the cat.

Anyway, for this post we have a few simple things to do:

  • Add the schema file type to the Custom Wizards category
  • Add the deployment file type to the Custom Wizards category
  • Implement the schema file creation code
  • Implement the deployment file creation code

Adding A New Schema File Wizard

You know the drill. Follow the yellow brick road:

  1. customplugin –> plugin.xml –> Extensions –> org.eclipse.ui.newWizards –> new –> wizard
    • id: customplugin.wizard.file.schema
    • name: Schema File
    • class: customplugin.wizards.CustomProjectNewSchemaFile
    • icon: icons/schema-file_16x16.png [copy another image to your icons folder and point to it here]
    • category: customplugin.category.wizards
    • descriptionImage: icons/schema-file_32x32.png [copy another image to your icons folder and point to it here]
  2. Click the class link for CustomProjectNewSchemaFile
  3. Everything looks good
  4. Click Finish to close the New Java Class Wizard
  5. [If you must: start the runtime workbench, press Ctrl+N and look in the Custom Wizards folder; the Schema File and icon should be visible. Exit the runtime workbench.]

Now that we have a schema file creation wizard defined let’s implement some code.

First the constructor so the wizard window has a title:

CustomProjectNewSchemaFile.java

public class CustomProjectNewSchemaFile extends Wizard implements INewWizard {

    public CustomProjectNewSchemaFile() {
        setWindowTitle("New Schema File");
    }

    ...
}

[Update: In doing the above I remembered that I never got around to doing this for the New Custom Project Wizard. The New Custom Project Wizard post, and the New Custom Project Wizard refactoring post have been updated.]

Next, in init() we save the incoming selection values as we will need them later for the constructor of the file creation page:

    private IWorkbench _workbench;
    private IStructuredSelection _selection;

    ...

    /* (non-Javadoc)
     * @see org.eclipse.ui.IWorkbenchWizard#init(org.eclipse.ui.IWorkbench, org.eclipse.jface.viewers.IStructuredSelection)
     */
    @Override
    public void init(IWorkbench workbench, IStructuredSelection selection) {
        _workbench = workbench;
        _selection = selection;
    }

    ...

In addPages() we call a class we haven’t created yet: WizardSchemaNewFileCreationPage. It subclasses WizardNewFileCreationPage which does exactly what we want: create a file for us. This is just another reason why it is so important to know what pieces are available within Eclipse for your use.

    private WizardNewFileCreationPage _pageOne;

    ...

    @Override
    public void addPages() {
        super.addPages();

        _pageOne = new WizardSchemaNewFileCreationPage(_selection);

        addPage(_pageOne);
    }

    ...

The variable declaration for _pageOne is purposely declared as WizardNewFileCreationPage even though the call in addPages() is to WizardSchemaNewFileCreationPage. We are only overriding method calls to facilitate the initialization of the object. Create the new class: press Ctrl+1 on the line in addPages() and select Create Class WizardSchemaNewFileCreationPage. It should automatically select WizardNewFileCreationPage as the superclass. Click Finish to create the new class.

Implement WizardSchemaNewFileCreationPage with:

public class WizardSchemaNewFileCreationPage extends WizardNewFileCreationPage {

    public WizardSchemaNewFileCreationPage(IStructuredSelection selection) {
        super("Custom Plug-in Schema File Wizard", selection);

        setTitle("Schema File Wizard");
        setDescription("Create a Schema File");
        setFileExtension("xml");
    }

    @Override
    protected InputStream getInitialContents() {
        String xmlTemplate = "<hc-schema>\n"
            + "  <tables></tables>\n"
            + "  <filters></filters>\n"
            + "  <views></views>\n"
            + "</hc-schema>\n";
        return new ByteArrayInputStream(xmlTemplate.getBytes());
    }
}

The constructor above speaks for itself. The getInitialContents() not so much. This method is used by the wizard to find default contents for the new file if one is created. Since this is my schema file I now have a test version of the file to display. Since this will be refactored into another file we are safe hard-coding it for now.

Returning to CustomProjectNewSchemaFile: Add the following to performFinish():

    @Override
    public boolean performFinish() {
        boolean result = false;

        IFile file = _pageOne.createNewFile();
        result = file != null;

        if (result) {
            try {
                IDE.openEditor(_workbench.getActiveWorkbenchWindow().getActivePage(), file);
            } catch (PartInitException e) {
                e.printStackTrace();
            }
        } // else no file created...result == false

        return result;
    }

The code is simple and self explanatory except for the call to openEditor();it automatically opens the editor for our XML file using whatever editor has been configured for XML files or the default editor if an XML editor is not available. The first argument to openEditor() needs a reference to IWorkbenchPage and we get it by asking the workbench for it. A small price to pay to have the editor opened automatically for us.

Go ahead and try the runtime workbench to check out your handiwork. You should be able to create a plain vanilla resource project and create a schema file to go with it. Life is good.

With that test out of the way we are now emboldened to use a template file instead of a hard coded XML string.

Create a folder named templates under your customplugin folder. In the templates folder create a file named schema-template.xml. Insert the following XML into the file.

<hc-schema>
    <tables>
    </tables>
    <views>
    </views>
    <filters>
    </filters>
</hc-schema>

With the above in place update getInitialContents():

WizardSchemaNewFileCreationPage.java
    protected InputStream getInitialContents() {
        String templateFilePath = "/templates/schema-template.xml";
        InputStream inputStream = null;
        try {
            inputStream = Activator.getDefault().getBundle().getEntry(templateFilePath).openStream();
        } catch (IOException e) {
            // send back null
        }

        return inputStream;
    }

Confirm nothing is broken by starting the runtime workbench.

Adding A New Deployment File Wizard

Now let’s do the same for the deployment file:

  1. customplugin –> plugin.xml –> Extensions –> org.eclipse.ui.newWizards –> new –> wizard
    • id: customplugin.wizard.file.deployment
    • name: Deployment File
    • class: customplugin.wizards.CustomProjectNewDeploymentFile
    • icon: icons/deployment-file_16x16.png [copy another image to your icons folder and point to it here]
    • category: customplugin.category.wizards
    • descriptionImage: icons/deployment-file_32x32.png [copy another image to your icons folder and point to it here]
      • Click the class link for CustomProjectNewDeploymentFile
      • Everything looks good
      • Click Finish to close the New Java Class Wizard
      • [Again, quick test: start the runtime workbench, press Ctrl+N and look in the Custom Wizards folder; the Deployment File and icon should be visible. Exit the runtime workbench.]

          Since we already know that we are using the WizardNewFileCreationPage as a subclass for the page that will create our file let’s create it upfront.

          public class WizardDeploymentNewFileCreationPage extends WizardNewFileCreationPage {
          
              public WizardDeploymentNewFileCreationPage(IStructuredSelection selection) {
                  super("Custom Plug-in Deployment File Wizard", selection);
                  
                  setTitle("Deployment File Wizard");
                  setDescription("Create a Deployment File");
                  setFileExtension("xml");
              }
          
              @Override
              protected InputStream getInitialContents() {
                  String templateFilePath = "/templates/deployment-template.xml";
                  InputStream inputStream = null;
                  try {
                      inputStream = Activator.getDefault().getBundle().getEntry(templateFilePath).openStream();
                  } catch (IOException e) {
                      // send back null
                  }
          
                  return inputStream;
              }
          }
          

          Create another template file in the templates folder named deployment-schema.xml. Contents follow:

          <hc-deployment>
          </hc-deployment>
          

          The actual wizard code for the CustomProjectNewDeploymentFile also looks suspiciously like the code for the schema file:

          public abstract class CustomProjectNewDeploymentFile extends Wizard implements INewWizard {
          
              private WizardNewFileCreationPage _pageOne;
              private IWorkbench                _workbench;
              private IStructuredSelection      _selection;
          
              public CustomProjectNewDeploymentFile() {
                  setWindowTitle("New Deployment File");
              }
          
              @Override
              public void addPages() {
                  super.addPages();
          
                  _pageOne = new WizardDeploymentNewFileCreationPage(_selection);
          
                  addPage(_pageOne);
              }
              @Override
              public boolean performFinish() {
                  boolean result = false;
          
                  IFile file = _pageOne.createNewFile();
                  result = file != null;
          
                  if (result) {
                      try {
                          IDE.openEditor(_workbench.getActiveWorkbenchWindow().getActivePage(), file);
                      } catch (PartInitException e) {
                          e.printStackTrace();
                      }
                  } // else no file created...result == false
          
                  return result;
              }
          
              @Override
              public void init(IWorkbench workbench, IStructuredSelection selection) {
                  _workbench = workbench;
                  _selection = selection;
              }
          }
          

          After all of the above in the runtime workbench you should be able to create a schema file and a deployment file and the default text editor should open once each file is defined in the New File Wizard.

          Run a quick test. From the runtime workbench create a project, create a schema file and deployment file.

          Woo hoo! All done!

          Oh…wait…time to refactor a few things.

          Refactor Strings

          Time to wash up!

          Once again, go to the plugin.xml Overview tab of the customplugin and run the Externalize String Wizard to externalize the two new strings we added. Change:

          • wizard.name.0 to wizard.name.schema
          • wizard.name.1 to wizard.name.deployment

          In the schema file related files refactor:

          • the wizard window title to WIZARD_NAME and add $NON-NLS$ using the Quick Fix functionality.
          • the WizardSchemaNewFileCreationPage title to PAGE_NAME.

          In WizardSchemaNewFileCreationPage:

          • Open the Externalize String wizard (one way: hover over one of the strings to open the wizard)
          • Change WizardSchemaNewFileCreationPage_0 to WizardSchemaNewFileCreationPage_Schema_File_Wizard by clicking in the desired field and typing Schema_File_Wizard
          • Change WizardSchemaNewFileCreationPage_1 to WizardSchemaNewFileCreationPage_Create_a_Schema_File by clicking in the desired field and typing Create_a_Schema_File
          • Change WizardSchemaNewFileCreationPage_2 to WizardSchemaNewFileCreationPage_Schema_File_Extension by clicking in the desired field and typing Schema_File_Extension
          • Change WizardSchemaNewFileCreationPage_3 to WizardSchemaNewFileCreationPage_Schema_Template_Location by clicking in the desired field and typing Schema_Template_Location
          • Click Configure in the Accessor class section at the bottom of the window.
          • Click Browse for Class Name and select NewWizardMessages; might as well put these strings with the others.
          • Click OK
          • Click Next and take a look at the various changes that are about to take place
          • Click Finish

          Do the same for the WizardDeploymentNewFileCreationPage only use the word Deployment instead of Schema.

          A question I have asked myself is: Should the WIZARD_NAME and PAGE_NAME also be externalized? While these posts have been examples of how to do things, with a mix of useful and useless things, the answer is…it depends. If i18n is important to the ultimate user of the plug-in then yes, externalize everything, otherwise don’t sweat it.

          [If I were being paid to do this I would fight tooth-and-nail to externalize every last string. It makes absolutely no sense to have to recompile one or more files just because a string has changed.]

          Refactor Code

          Both CustomProjectNewSchemaFile and CustomProjectNewDeploymentFile have the same constructor, performFinish() and init() methods. Let’s create a new parent class and move everything, but addPages() into it (it is the only method with custom code).

          The following will do the trick:

          1. Right click in the editor for CustomProjectNewSchemaFile and select Refactor –> Extract Superclass
            • Superclass Name: CustomProjectNewFile
          2. In Types to Extract a Superclass From click Add and add CustomProjectNewDeploymentFile
          3. In Specify Actions for Members check:
            • _pageOne
            • _workbench
            • _selection
            • performFinish()
            • init()
          4. Click Next
          5. In Subtypes of Type select init() and performFinish() to be removed from both subclasses. Notice that the constructors have not been recognized as having common behavior.
          6. Click Finish.

          Oddly enough, when the refactoring is over the superclass has a compile error! How could that be? Well, CustomProjectNewFile is missing the INewWizard interface that is needed to properly find init(). The refactoring missed it, but the compiler didn’t. Manually (yuck) add it to CustomProjectNewFile and remove it from CustomProjectNewSchemaFile and CustomProjectNewDeploymentFile (if you don’t remove it nothing will happen, but your code might develop cooties).

          Let’s change the constructors for CustomProjectNewSchemaFile and CustomProjectNewDeploymentFile. Change:

          CustomProjectNewSchemaFile.java
          
              public CustomProjectNewSchemaFile() {
                  setWindowTitle(WIZARD_NAME);
              }
          

          to:

          CustomProjectNewSchemaFile.java
          
              public CustomProjectNewSchemaFile() {
                  super(WIZARD_NAME);
              }
          

          Change CustomProjectNewDeploymentFile from:

          CustomProjectNewDeploymentFile.java
          
              public CustomProjectNewDeploymentFile() {
                  setWindowTitle(WIZARD_NAME);
              }
          

          to:

          CustomProjectNewDeploymentFile.java
          
              public CustomProjectNewDeploymentFile() {
                  super(WIZARD_NAME);
              }
          

          Use quick fix to create the new constructor in the CustomProjectNewFile parent class:

              public CustomProjectNewFile(String wizardName) {
                  setWindowTitle(wizardName);
              }
          

          Not too shabby.

          Run a quick test if you are so inclined.

          customplugin-part-10-custom-file-types-in-editor

          What just happened?

          Well, this was an interesting visit. We certainly coded more than usual for the customplugin, but it was all pretty trivial.

          We created two new wizards based on the built-in file creation page and create two templates so our custom files have a good starting point.

          The refactoring will make changing labels and such easier.

          The body count so far:
          customplugin: 11 classes, one properties file
          customnavigator: 13 classes

          Next post: back to the Custom Navigator, probably to display the Stored Procedure category with a Java file as an entry. The deployment file is not displayed, but will be found in the deployment-files folder.

          Unless I decide to go back to GP for a while.

          In celebration of this religious holiday go have some candy.

          (Anybody seen a cat?)

          References and Thanks

          How to create a new File Wizard? for reminding me about bundle functionality.

          Code

          CustomProjectNewFile.java

          /**
           * Coder beware: this code is not warranted to do anything.
           *
           * Copyright Oct 31, 2009 Carlos Valcarcel
           */
          package customplugin.wizards;
          
          import org.eclipse.core.resources.IFile;
          import org.eclipse.jface.viewers.IStructuredSelection;
          import org.eclipse.jface.wizard.Wizard;
          import org.eclipse.ui.INewWizard;
          import org.eclipse.ui.IWorkbench;
          import org.eclipse.ui.PartInitException;
          import org.eclipse.ui.dialogs.WizardNewFileCreationPage;
          import org.eclipse.ui.ide.IDE;
          
          public abstract class CustomProjectNewFile extends Wizard implements INewWizard {
          
              protected WizardNewFileCreationPage _pageOne;
              private IWorkbench _workbench;
              protected IStructuredSelection _selection;
          
              public CustomProjectNewFile() {
                  super();
              }
          
              @Override
              public boolean performFinish() {
                  boolean result = false;
                  
                  IFile file = _pageOne.createNewFile();
                  result = file != null;
              
                  if (result) {
                      try {
                          IDE.openEditor(_workbench.getActiveWorkbenchWindow().getActivePage(), file);
                      } catch (PartInitException e) {
                          e.printStackTrace();
                      }
                  } // else no file created...result == false
                  
                  return result;
              }
          
              @Override
              public void init(IWorkbench workbench, IStructuredSelection selection) {
                  _workbench = workbench;
                  _selection = selection;
              }
          
          }
          
          

          CustomProjectNewSchemaFile.java

          /**
           * Coder beware: this code is not warranted to do anything.
           *
           * Copyright Oct 31, 2009 Carlos Valcarcel
           */
          package customplugin.wizards;
          
          
          /**
           * @author carlos
           *
           */
          public class CustomProjectNewSchemaFile extends CustomProjectNewFile {
          
              private static final String WIZARD_NAME = "New Schema File"; //$NON-NLS-1$
              /**
               * 
               */
              public CustomProjectNewSchemaFile() {
                  setWindowTitle(WIZARD_NAME);
              }
          
              @Override
              public void addPages() {
                  super.addPages();
          
                  _pageOne = new WizardSchemaNewFileCreationPage(_selection);
          
                  addPage(_pageOne);
              }
          }
          
          

          CustomProjectNewDeploymentFile.java

          /**
           * Coder beware: this code is not warranted to do anything.
           * Copyright Oct 31, 2009 Carlos Valcarcel
           */
          package customplugin.wizards;
          
          
          /**
           * @author carlos
           */
          public class CustomProjectNewDeploymentFile extends CustomProjectNewFile {
              private static final String WIZARD_NAME = "New Deployment File"; //$NON-NLS-1$
          
              public CustomProjectNewDeploymentFile() {
                  setWindowTitle(WIZARD_NAME);
              }
          
              @Override
              public void addPages() {
                  super.addPages();
          
                  _pageOne = new WizardDeploymentNewFileCreationPage(_selection);
          
                  addPage(_pageOne);
              }
          
          }
          

          WizardSchemaNewFileCreationPage.java

          /**
           * Coder beware: this code is not warranted to do anything.
           *
           * Copyright Oct 31, 2009 Carlos Valcarcel
           */
          package customplugin.wizards;
          
          import java.io.IOException;
          import java.io.InputStream;
          
          import org.eclipse.jface.viewers.IStructuredSelection;
          import org.eclipse.ui.dialogs.WizardNewFileCreationPage;
          
          import customplugin.Activator;
          
          /**
           * @author carlos
           *
           */
          public class WizardSchemaNewFileCreationPage extends WizardNewFileCreationPage {
          
              private static final String PAGE_NAME = "Custom Plug-in Schema File Wizard"; //$NON-NLS-1$
          
              public WizardSchemaNewFileCreationPage(IStructuredSelection selection) {
                  super(PAGE_NAME, selection);
                  
                  setTitle(NewWizardMessages.WizardSchemaNewFileCreationPage_Schema_File_Wizard);
                  setDescription(NewWizardMessages.WizardSchemaNewFileCreationPage_Create_a_Schema_File);
                  setFileExtension(NewWizardMessages.WizardSchemaNewFileCreationPage_Schema_File_Extension);
              }
          
              @Override
              protected InputStream getInitialContents() {
                  String templateFilePath = NewWizardMessages.WizardSchemaNewFileCreationPage_Schema_Template_Location;
                  InputStream inputStream = null;
                  try {
                      inputStream = Activator.getDefault().getBundle().getEntry(templateFilePath).openStream();
                  } catch (IOException e) {
                      // send back null
                  }
          
                  return inputStream;
              }
          
          }
          

          WizardDeploymentNewFileCreationPage.java

          /**
           * Coder beware: this code is not warranted to do anything.
           *
           * Copyright Oct 31, 2009 Carlos Valcarcel
           */
          package customplugin.wizards;
          
          import java.io.IOException;
          import java.io.InputStream;
          
          import org.eclipse.jface.viewers.IStructuredSelection;
          import org.eclipse.ui.dialogs.WizardNewFileCreationPage;
          
          import customplugin.Activator;
          
          /**
           * @author carlos
           *
           */
          public class WizardDeploymentNewFileCreationPage extends WizardNewFileCreationPage {
          
              private static final String PAGE_NAME = "Custom Plug-in Deployment File Wizard"; //$NON-NLS-1$
          
              public WizardDeploymentNewFileCreationPage(IStructuredSelection selection) {
                  super(PAGE_NAME, selection);
                  
                  setTitle(NewWizardMessages.WizardDeploymentNewFileCreationPage_Deployment_File_Wizard);
                  setDescription(NewWizardMessages.WizardDeploymentNewFileCreationPage_Create_a_Deployment_File);
                  setFileExtension(NewWizardMessages.WizardDeploymentNewFileCreationPage_Deployment_File_Extension);
              }
          
              @Override
              protected InputStream getInitialContents() {
                  String templateFilePath = NewWizardMessages.WizardDeploymentNewFileCreationPage_Deployment_Template_Location;
                  InputStream inputStream = null;
                  try {
                      inputStream = Activator.getDefault().getBundle().getEntry(templateFilePath).openStream();
                  } catch (IOException e) {
                      // send back null
                  }
          
                  return inputStream;
              }
          
          }
          
          Advertisements
  1. John
    July 9, 2010 at 12:01 pm

    Tiny cop-and-paste typo..
    CustomProjectNewSchemaFile–>CustomProjectNewDeploymentFile

    really enjoying this tutorial. I’m learning alot!!

    • cvalcarcel
      July 11, 2010 at 3:14 pm

      Excellent catch! It is now corrected.

      Glad you are enjoying the tutorial. Who knows? I might even finish it.

  2. Otto
    August 7, 2011 at 12:04 pm

    Hi, I am struggling at the moment with CNF little bit. I have a custom file type, say “*.kwk” and I want by plugin which uses CommonViewer to display my custom icon for “*.kwk” files. I would appreciate if you could point me to a right direction.
    Thanks,
    Otto

  3. sdf
    August 18, 2011 at 11:54 am

    terrible

  4. Alan Sambol
    May 23, 2012 at 3:28 am

    Your CustomProjectNewDeploymentFile shouldn’t be abstract.

  5. pjain
    June 26, 2012 at 5:57 am

    Hi

    When I create a new Project (Custom Project), I do not see the two files : sample-deploy.xml and sample-schema.xml.

    I can see the custom file creation wizard and I can create the schema and deployment files through wizard independently. But when I create Custom Project I do not see these two files automatically.

    Did I miss a step to include this file creation while creating the Custom Project ?

  6. suma
    August 25, 2015 at 6:42 am

    HI,I have to execute a piece of code while installing a custom plugin in eclipse..which extension I can use???

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: