Monday, December 10, 2012

Convert a Java Project to a Java Plugin programatically


Normally, when we want to create a Java project or plugin, we use wizards of IDE as Eclipse or Netbeans to do it easily. However, in some cases we want to do it programmatically. On this article, I will show you a way to convert a Java Project to a Java Plugin by code. After that, I will contribute a menu item to a popup menu, for example the popup menu of the genmodel’s editor.
I also will add comments on lines of code so you could easy to understand it.

Environment

I use Eclipse 4.2 (Juno) and Jdk 6 on Window platform to illustrate. You at least need to know about Eclipse plugins and Eclipse commands. You could learn these topics on the Vogella blog (see more on references).

Create a plugin

First of all, we create a plugin by choosing File à New à Other à Plugin Project


Set the project name to de.rwth.swc.demo.generation, and hit Next and Finish (keep everything as default).
Open the Manifest file, choose the Dependencies tab then add two more plugins:
  • org.eclipse.jdt.core
  • org.eclipse.core.resources

The next step is to create a command and a menu contribution:
  • Open the Extension tab, add org.eclipse.ui.commands and org.eclipse.ui.menu.
  • Right click on org.eclipse.ui.commands, choose New -> Command.
  • Set the id is de.rwth.swc.demo.generation.test and the name is test.
  • Set de.rwth.swc.demo.generation.MenuHandler for defaultHandler, then click on the hyperlink of defaulHandler. It will create a class MenuHandler on package de.rwth.swc.demo.generation. We will comeback with this class on the next part.
  • Right click on org.eclipse.ui.menu, choose New -> menuContribution.
  • Set popup:org.eclipse.emf.codegen.ecore.genmodel.presentation.GenModelEditorID to locationURI. To understand why we could know this URI, please look on the appendix part.
  • Right click on the menuContribution created on the previous step, New à Command. Set de.rwth.swc.demo.generation.test to the commandId field and set Create a Plugin for the label.

After these steps, we will have the Extensions tab as below:


When you run this plugin, assume that you have a genmodel file, the popup menu should include a menu item, named Create a Plugin as below:


Work with the Handler class

Now is time to work with the handler class which will perform the back end of the menu item. Open the MenuHandler class, we could see that it implements the IHandler interface. However, on this article we just work with the execute() method, so we change a bit, let the MenuHandler class extends AbstractHandler class and we will have the original code:
package de.rwth.swc.demo.generation;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;

public class MenuHandler extends AbstractHandler {
    
     @Override
     public Object execute(ExecutionEvent event) throws                                                                      ExecutionException {
          return null;
     }
}
To achieve our purpose in the beginning, I will do some steps as below:
  • Create a Java project.
  • Set JavaNature feature for the project
  • Set JRE path
  • Create the bin folder and set as the output folder
  • Create a source folder (if you want to create Java classes)
  • Convert the Java project to become a Java plugin.

Create a Java project.

We add three attributes for the handler class:
     // Snipped…
private IProject project;
     private IJavaProject javaProject;
     private final String PROJECT_NAME =           "de.rwth.mocca.demo.test";
// Snipped…
 Add a new method, createProject() as below:
     private void createProject() throws CoreException {
          // Create a progress monitor.
          IProgressMonitor progressMonitor = new     NullProgressMonitor();
          // Get the workspace of Eclipse.
          IWorkspaceRoot root =                    ResourcesPlugin.getWorkspace().getRoot();
          // Get a project by its name but hasn't created a real project.
          project = root.getProject(PROJECT_NAME);
          // Create a new project.
          project.create(progressMonitor);
          // Open the project.
          project.open(progressMonitor);
          // Convert the project to a java project.
          javaProject = JavaCore.create(project);
     }

Set JavaNature feature for the project

Even we convert the project to become a java project, but we need to set the Java nature or feature for this project. We create a new method, setJavaNature() as below:
     private void setJavaNature() throws CoreException {
          // Get the description of the project
          IProjectDescription description = project.getDescription();
          // Change the description with NATURE_ID of JavaCore
          description.setNatureIds(new String[] {JavaCore.NATURE_ID});
          // Set the description back to the project
          project.setDescription(
              description, null);
     }
JavaCore is a class on the org.eclipse.jdt.core plugin. It includes many constants as CORE*, FORMATTER*, COMPILER*, etc. Two important contants are NATURE_ID and PLUGIN_ID.

Set JRE path

A Java project needs to have a JRE path. We add a method, addDefaultJRE to do this task:
     private void addDefaultJRE(IProgressMonitor progressMonitor) throws JavaModelException {
          // Create an empty class path entry array for the project
          javaProject.setRawClasspath(
              new IClasspathEntry[0], progressMonitor);
          // Get it - the old entries
          IClasspathEntry[] oldEntries = javaProject.getRawClasspath();
          // Increase 1 for the size of the new entry array.
          IClasspathEntry[] newEntries = new IClasspathEntry[oldEntries.length + 1];
          // Copy the old entries to new entry array.
          System.arraycopy(
              oldEntries, 0, newEntries, 0, oldEntries.length);
          // Set the new element to the default JRE of the system.
          newEntries[oldEntries.length] = JavaRuntime.getDefaultJREContainerEntry();
          // Set back entry paths to the project
          javaProject.setRawClasspath(
              newEntries, progressMonitor);
     }
The JavaRuntime class is not available on our plugin at this moment. It belongs to org.eclipse.jdt.launching so we need to add it as a dependency of the de.rwth.swc.demo.generation plugin. JavaRuntime offers some methods to work not only with JRE but also with Virtual Machine.

Create the bin folder

We need to add a bin folder and set it to become the output location (the folder keeps class files) for the project: create a new method, createBinFolder(), as below:
     private IFolder createBinFolder() throws CoreException {
          // Get the folder with the name bin
          IFolder binFolder = project.getFolder("bin");
          // Create this folder. if the first parameter is false, it does not
          // forced to override the folder
          binFolder.create(
              false, true, null);
          // Get the path of the bin folder
          IPath outputLocation = binFolder.getFullPath();
          // Set that path as the output location of the project
          javaProject.setOutputLocation(
              outputLocation, null);
          return binFolder;
     }

Create a source folder (if you want to create Java classes)

Now we will create a source folder to keep the java files. We will do the same way to create the bin folder: create a folder, then add to class path of new project. The code is enclosed on the createSourceFolder  method:
     private IPackageFragmentRoot createSourceFolder(String srcName) throws CoreException {
          // Get the folder with srcName name. It may be not exist
          IFolder folder = project.getFolder(srcName);
          // Create a real folder. In the case you want to force override the
          // folder,
          // set the first parameter as true
          folder.create(
              false, true, null);
          // Get the source folder as root package
          IPackageFragmentRoot root = javaProject.getPackageFragmentRoot(folder);
          // Do the samething on the addDefaultJRE method
          IClasspathEntry[] oldEntries = javaProject.getRawClasspath();
          IClasspathEntry[] newEntries = new IClasspathEntry[oldEntries.length + 1];
          System.arraycopy(
              oldEntries, 0, newEntries, 0, oldEntries.length);
          // Add a new source folder as a new entry for class paths
          newEntries[oldEntries.length] = JavaCore.newSourceEntry(root.getPath());
          // Set back entry paths to the project
          javaProject.setRawClasspath(
              newEntries, null);
          return root;
     }

Convert the Java project to become a Java plugin.

We use a class of Eclipse to do this task. Firstly, we import the source code of the plugin named org.eclipse.pde.ui. We copy class org.eclipse.pde.internal.ui.wizards.tools.ConvertProjectToPluginOperation to the de.rwth.demo.generation package. (The reason why we know the plugin and the class, you could see on the Appendix part. After copying, there are some errors on this class. To fix those errors, we need some other plugins and import some packages:
  • Add dependencies: org.eclipse.pde.core and org.eclipse.pde.
  • Import packages: org.eclipse.pde.internal.ui, org.eclipse.pde.internal.ui.util, org.eclipse.ui.actions, org.eclipse.pde.internal.ui.wizards.tools.

On this class, we will call the execute(…) method to start converting projects.

The last step

Now we change the createProject() a little bit. We call the methods created above:
          setJavaNature();
          // Add JRE
          addDefaultJRE(progressMonitor);
          // Create the bin folder
          createBinFolder();
          // Create a source folder
          IPackageFragmentRoot src = createSourceFolder("src");
          // Convert the project to a plugin
          ConvertProjectToPluginOperation convert = new ConvertProjectToPluginOperation(new IProject[] {project}, true);
          try {
              convert.execute(progressMonitor);
          }catch (InvocationTargetException e) {
              e.printStackTrace();
          }catch (InterruptedException e) {
              e.printStackTrace();
          }
On the execute() method of MenuHandler, we call the method createProject() and everything is done.

Running

You could export the de.rwth.swc.demo.generation plugin and import to your Eclipse if you want. However, here I will run directly this plugin and create a new instance of Eclipse.
Right click on the manifest file and run as Eclipse application. Assume that you have a genmodel file or something like ecore file, right click and choose Create a Plugin, you will get a new plugin with the name de.rwth.mocca.demo.test.

Appendix

Question: How could I know to set popup:org.eclipse.emf.codegen.ecore.genmodel.presentation.GenModelEditorID to the locationURI field?.
Answer: Open one genmodel file, and press Shift + Alt + F2 to spy the popup menu


We will see that the class take care this operation is GenModelActionBarContributor$GenerateAction on the plugin org.eclipse.emf.codegen.ecore.ui. Import the source code of this plugin, open the manifest file, then the Extensions tab, you could see the ID of EMF Generator is the value which is set for the locationURI field.

Question: How could I know the ConvertProjectToPluginOperation class?
Answer: By using spy key code (Shift + Alt + F1), we could check what class has responsible to convert projects. Right click on a Java project, choose Configure -- > Convert to Plug-in Projects, we have:


By importing the plugin org.eclipse.ui.workench (we need to switch to the plug-in perspective) and the open ConvertProjectAction class, you will find on performFinish() methods leading to the ConvertProjectToPluginOperation class.

References

There are some links that you could read related to this article:
Eclipse Plugin: http://www.vogella.com/articles/EclipsePlugIn/article.html
JDT:








Remove API baseline warnings or errors


When you work with Eclipse, you might see warnings or errors with the text: An API baseline has not been set for the current workspace. We could remove them easily:
Choose Window menu - > Preferences, then open Plug-in Development - > API Baselines. On the option part, we set Ignore Missing API Baseline:



And it’s solved.

Monday, November 19, 2012

Compare performace of String and StringBuilder


As we know java.lang.String is an immutable class. When we want to combine Strings, a lot of new Strings will be created before we get the last result. In some case as reading long text files or retrieving and concatenating big text fields from DB, we should consider to use StringBuilder instead of String. We will see how we could improve performance.

String – an immutable class

Immutable classes offer safe threads when we use them. However, there is a disadvantage of this type of classes is the number of existing objects. Every time we invoke methods to change states of immutable objects, we truly create new objects. Let we see an example:
          String message = "Java";
          message += " is a programming";
          message += "language.";
And we will have message = “Java is a programming language.”. The question is: How many String objects do we have after running this code? Three? Or four? The answer is five Strings: 3 obviously Strings “Java”, “ is a programming”, and “ language”; the result String “Java is a programming language.”; and another one is “Java is a programming”. The “+” operation or the concat() method of String does copy contents of original Strings to make a new String.
When the frequency to combine Strings is large, we will get abysmal performance.
StringBuilder is a mutable sequence of characters. StringBuilder is not synchronized as StringBuffer. The main operations of StringBuilder are convert() and append(). We should use StringBuilder when we need to concatenate Strings.

Compare performance by using String and StringBuilder

My computer uses Window 7 with Core i3 2.27GHz CPU and 4Gb Ram. I downloaded from the Internet one text file having 10800 lines. We will see the simple code to read this text file:

public class Test {
     public static void main(String[] args) {
          long start = System.currentTimeMillis();
          readFile("sample.txt");
          long stop = System.currentTimeMillis();
          System.out.println("Running time: " + (stop - start) + " ms");
     }

     public static String readFile(String filename) {
          String content = "";
          try {
               File file = new File(filename);
              FileReader fr;
              fr = new FileReader(file);
              BufferedReader br = new BufferedReader(fr);
              String line = "";
              while ((line = br.readLine()) != null) {
                   content += line;
              }
              br.close();
          } catch (FileNotFoundException e) {
              e.printStackTrace();
          } catch (IOException e) {
              e.printStackTrace();
          }
          return content;
     }
}
When I run this code on my laptop, the output was “Running time: 16967 ms”, near by 17 seconds. Now let we change a bit to use StringBuilder:

public class Test {
     public static void main(String[] args) {
          long start = System.currentTimeMillis();
          readFile("sample.txt");
          long stop = System.currentTimeMillis();
          System.out.println("Running time: " + (stop - start) + " ms");
     }

     public static String readFile(String filename) {
          StringBuilder content = new StringBuilder();
          try {
              File file = new File(filename);
              FileReader fr;
              fr = new FileReader(file);
              BufferedReader br = new BufferedReader(fr);
              String line = "";
              while ((line = br.readLine()) != null) {
                   content.append(line);
              }
              br.close();
          } catch (FileNotFoundException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          } catch (IOException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }

          return content.toString();
     }
}
Running this code, I got the output: “Running time: 24 ms”, faster than 700 times! Therefore, we easy decide what we should use on cases like this.

Saturday, November 17, 2012

Eclipse Working Set


Eclipse is a popular IDE for programming. We have known conveniences and utilities of this IDE. This article will show how we work with Eclipse Working Set.

What is Working Sets?

Working sets group elements for display in views or for operations on a set of elements. (Eclipse Documentation)
Eclipse allows programmers create or import plug-ins to their projects. Big projects have tens, or hundreds different plug-ins. However, we do not always work all with them at one time. Plug-ins could be grouped by purposes, tasks on projects, for examples, database plug-ins, GUI plug-ins, etc. Working Sets will help us to select and display whatever plug-ins that we want without affect to the whole system.

Create a working set

Create a working set on Eclipse is pretty easy. Assume that we have some plug-in displaying on the Package Explorer.
Step 1: Click on the small downward triangle (View Menu) and click Select Working Set.


Figure 1: List of project

Step 2: Click the New button to create a new Working Set if you do not have. In case that you already have working set, you could choose them on this step and hit OK.


Figure 2: Create a new working set

Step 3: There are several types of working sets like Resources, Breakpoint, Java …Here we choose Java and hit Next.


Figure 3: Choose a type for new working set

Step 4: Firstly, we type a name for new working set, and then we select plug-ins that we want to move from Workspace Contents to Working set content. Finally, hit Finish.


Figure 4: Select projects/plug-ins

Step 5: Select our new working set and hit OK. You could choose more than one working set.


Figure 5: Select working sets

When we finish, we just see what projects or plug-ins on chosen working sets. Using working sets do not restrict resources of the whole system. It is just the way to limit views on the Package Explorer. We still could use Ctrl + Shift + R or whatever to open other classes or resources.