In this article we will create a simple eclipse plugin for Java source code.
Plugin example in this article
- Add a menu item & toolbar button to eclipse.
- On click of button/menu, plugin will scan Java source code & check if improper empty String checks are being done like string.equals(“”).
- If improper checks found then show message dialog to use string.isEmpty() instead.
- Mark the lines of code with improper checks using markers.
Quick facts about eclipse architecture & plugin development
- Basic core eclipse mainly provides common user interface, Operating system compatibility & an architecture for dynamic discovery, loading, and running of plug-ins.
- Eclipse architecture is completely based on plug-in model. Apart from core abilities mentioned above, everything else is added in the form on plug-ins.
- Even the java file editor, java related menus like source, reference etc. belong to JDT Java development tooling plugins.
- Plug-ins can add more functionalities to eclipse by using ‘Extension Points’ as shown in below diagram. By extending you can create your own views, Menu items, Editors & lot more.
Common Extension Points
These are basic extension points that can be used in plugins to add new functionality.
- UI Based extension points
- org.eclipse.ui.views – Used for adding view similar to the one shown in above image.
- org.eclipse.ui.editors – Used for adding editor similar to the one shown in above image.
- org.eclipse.ui.menus – Used for adding menu similar to the one shown in above image.
- Execution based extension points
- org.eclipse.ui.commands – This is the trigger or message on trigger that occurs when specific user action happens.
- org.eclipse.ui.handlers – This is the actual class that implements the behavior for given user action.
- org.eclipse.ui.bindings – This binds keyboard shortcuts to menus, toolbar etc. (Like Ctrl+6 in our example)
This diagram explains basic plugin execution i.e. how user actions & the plugin code is tied together to perform expected operation.
- On user operation like click of menu, command is triggered & sent to specific handler object.
- Handler object will perform the behavior as needed for plugin.
- Keyboard shortcut bound using ‘Binding’ extension point can also be used to trigger command.
Lets create a basic plugin project
Follow steps in this presentation to create a basic eclipse plugin using eclipse. This slide uses ‘Spring tool suite’ which is just another specialized variation of eclipse to be used for ‘Spring’ projects. But steps are same in any eclipse that you might be having.
Watch it on YouTube – Hello World Eclipse Plugin Project (Java) | Create, run & customize
In the project created, in plugin.xml you can see that there is an extension added to “org.eclipse.ui.menus” to introduce new menu item in eclipse UI for our plugin.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<extension point="org.eclipse.ui.menus"> <menuContribution locationURI="menu:org.eclipse.ui.main.menu?after=additions"> <menu id="StringCheckPlugin.menus.sampleMenu" label="Sample Menu" mnemonic="M"> <command commandId="StringCheckPlugin.commands.sampleCommand" id="StringCheckPlugin.menus.sampleCommand" mnemonic="S"> </command> </menu> </menuContribution> <menuContribution locationURI="toolbar:org.eclipse.ui.main.toolbar?after=additions"> <toolbar id="StringCheckPlugin.toolbars.sampleToolbar"> <command id="StringCheckPlugin.toolbars.sampleCommand" commandId="StringCheckPlugin.commands.sampleCommand" icon="icons/sample.png" tooltip="Say hello world"> </command> </toolbar> </menuContribution> </extension> |
It also has command & handler binding in the form of extension.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<extension point="org.eclipse.ui.commands"> <category id="StringCheckPlugin.commands.category" name="Sample Category"> </category> <command categoryId="StringCheckPlugin.commands.category" name="Sample Command" id="StringCheckPlugin.commands.sampleCommand"> </command> </extension> <extension point="org.eclipse.ui.handlers"> <handler class="com.itsallbinary.stringcheckplugin.handlers.StringCheckHandler" commandId="StringCheckPlugin.commands.sampleCommand"> </handler> </extension> |
This is the default handler class that generates message dialog box.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package com.itsallbinary.stringcheckplugin.handlers; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.handlers.HandlerUtil; import org.eclipse.jface.dialogs.MessageDialog; public class StringCheckHandler extends AbstractHandler { @Override public Object execute(ExecutionEvent event) throws ExecutionException { IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindowChecked(event); MessageDialog.openInformation( window.getShell(), "StringCheckPlugin", "Always do proper String checks"); return null; } } |
Improve basic plugin to scan java file using JDT
Now lets get to our requirementgs for plugin. In order to deal with java source code, we first need to import Java development tooling (JDT) related package & few other bundles to our plugin.
Java development tooling (JDT) allows users to write, compile, test, debug, and edit programs written in the Java programming language.
Eclipse provides easy way to add such packages & bundles as shown in below screenshot. Open plugin.xml in eclipse. Go to ‘Dependencies’ tab > Imported Packages/Required Plug-ins > Add. This will add packages & bundles to MANIFEST.MF. You can also add this directly to MANIFEST.MF
Here is the modified MANIFEST.MF file with new packages & bundles added.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: StringCheckPlugin Bundle-SymbolicName: StringCheckPlugin;singleton:=true Bundle-Version: 1.0.0.qualifier Require-Bundle: org.eclipse.core.resources, org.eclipse.core.runtime, org.eclipse.ui, org.eclipse.jface.text, org.eclipse.ui.editors Automatic-Module-Name: StringCheckPlugin Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: org.eclipse.jdt.internal.ui.javaeditor |
Modify labels & tooltip in plugin.xml to make it more meaningful to our requirements.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<extension point="org.eclipse.ui.commands"> . . . <command categoryId="StringCheckPlugin.commands.category" name="Verify String checks" id="StringCheckPlugin.commands.sampleCommand"> </command> </extension> . . . <extension point="org.eclipse.ui.menus"> . . . <menu id="StringCheckPlugin.menus.sampleMenu" label="String check verification" mnemonic="M"> . . . icon="icons/sample.png" tooltip="Verify string check"> </command> . . .<br> |
Now modify handler class to –
- Get java source code of selected file.
- Scan it & look for code like <string>.equals(“”)
- If found, then add message “Improper String check. Please use String#isEmpty()”
- Mark line numbers with improper empty string check with message to correct check. We will use ‘Problem’ marker type which is existing eclipse marker type.
This is the handler code with explanation in code comments.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
package com.itsallbinary.stringcheckplugin.handlers; import java.util.Scanner; import java.util.regex.Pattern; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.text.IDocument; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.handlers.HandlerUtil; import org.eclipse.ui.part.FileEditorInput; public class StringCheckHandler extends AbstractHandler { @Override public Object execute(ExecutionEvent event) throws ExecutionException { IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindowChecked(event); String message = ""; // Make sure current active editor is java editor. if (window.getActivePage().getActiveEditor() instanceof JavaEditor) { // Get file name from title of java editor & verify its java file. JavaEditor javaEditor = (JavaEditor) window.getActivePage().getActiveEditor(); String fileName = javaEditor.getTitle(); if (fileName.endsWith(".java")) { // Get java source code. IDocument doc = javaEditor.getDocumentProvider().getDocument(javaEditor.getEditorInput()); String sourceCode = doc.get(); IFile file = ((FileEditorInput) javaEditor.getEditorInput()).getFile(); // Clear previous markers. try { for (IMarker m : ((IResource) file).findMarkers(IMarker.PROBLEM, true, 1)) { if (((String) m.getAttribute(IMarker.MESSAGE)).startsWith("StringCheck:")) { m.delete(); } } } catch (CoreException e1) { e1.printStackTrace(); } // Iterate line by line & look for wrong String check. boolean isWrongStringCheck = false; Scanner sc = new Scanner(sourceCode); int lineNr = 1; while (sc.hasNextLine()) { String line = sc.nextLine(); // Look for empty string check usage using .equals("") Pattern emptyStringRegex = Pattern.compile(".*\\Q.equals(\"\")\\E", Pattern.MULTILINE); boolean isMatch = emptyStringRegex.matcher(line).find(); if (isMatch) { isWrongStringCheck = true; // Set marker is wrong String check found try { IMarker marker = ((IResource) file).createMarker(IMarker.PROBLEM); marker.setAttribute(IMarker.LINE_NUMBER, lineNr); marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING); marker.setAttribute(IMarker.MESSAGE, "StringCheck: Improper String check. Please use String#isEmpty()"); marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); } catch (CoreException e) { message = "Oops ! Something went wrong !"; e.printStackTrace(); } } lineNr++; } // If improper check is present, show message in message box. message = isWrongStringCheck ? ("Improper String check. Please use String#isEmpty() ") : "Your code is good !"; } else { message = "Not a java file"; } } else { message = "Not a java file"; } MessageDialog.openInformation(window.getShell(), "String check result", message); return null; } } |
Execute plugin
Now execute plugin same like we did earlier. You should see updated menu label. Sometimes you might still see old label. In such case add “-clean” to “Run Configurations > Arguments > Program Arguments”.
Once you run plugin as eclipse application, in newly opened eclipse, create a simple java project in that eclipse application. Create a simple test java program which has empty string check as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.itsallbinary; public class Test { public static void main(String[] args) { String str = ""; if (str.equals("")) { System.out.println("String is empty !"); } } } |
Now keep cursor on this java file (So that active editor becomes JavaEditor) & then click menu “String check verification > Verify String checks”. A command will be sent to our handler & then it will scan java source code & show message box like shown below. It will also mark line with improper String check. In ‘Problems’ tab below, you can see an entry added with our message.
You can also try using Ctrl+6 keyboard shortcut which will also trigger our plugin.
You can experiment with changing code, running from different codes, editors etc. to assess how handler works.
Source Code
You can find complete source code for plugin at below github repository.
GitHub Source Code – StringCheckEclipsePlugin
Further Read
Custom eclipse plugin | Build, create & publish to update site & marketplace | Good for beginners
References
Eclipse guide, reference & API documentation
Plugin spy short cuts –
- Alt + Shift + F1 – Plugin Selection Spy
- Alt + Shift + F2 – Plugin Menu Spy