Browse DevX
Sign up for e-mail newsletters from DevX


Getting Tidy with Eclipse : Page 3

Eclipse's extensibility makes it the perfect collaborator. Learn how to build custom views in Eclipse that integrate with other useful public libraries. For example, in this article, you'll learn how to create a split pane view in Eclipse that cleans malformed HTML files using JTidy.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Splitting It All Together
Now that I have shown how to make use of JTidy and the SWT browser, I can integrate the two into a split-pane view. I start by creating a class named BrowserView, which extends ViewPart. All Eclipse views must extend ViewPart and all parts must implement the createPartControl and setFocus methods. The createPartControl method is where the initial UI of the view is created.

My view is going to consist of several controls. I want a button that when clicked will show the user a dialog box for browsing the file system. This will be used to select the input HTML file. Next I want a button that will execute JTidy against the input and then render the results. I will make use of a SashForm to create the split-pane. Finally, I will use a GridLayout to arrange the various controls. To make it easier I am also going to have BrowserView implement SelectionListener, which will provide callbacks when either of the two buttons is clicked. That means I will also need to implement the widgetSelected and widgetDefaultSelected methods. All of the imports and the shell class are shown below:

import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.ui.part.ViewPart; import org.w3c.tidy.Tidy; public class BrowserView extends ViewPart implements SelectionListener { public void createPartControl(Composite parent) { } public void setFocus() { } public void widgetSelected(SelectionEvent e) { } public void widgetDefaultSelected(SelectionEvent e) { } }

Next, I will create several fields to hold references to the various objects with which I need to interact. The declarations are as follows:

Tidy tidy = null; Browser left = null; Browser right = null; Button browse = null; Button render = null; String selectedFile = null;

The first field, tidy, will hold a reference to my Tidy instance. The left and right fields will hold references to the Browser widgets for each pane. The browse and render fields provide references to my buttons. And last, the selectedFile is a string that will hold the path to the file selected by the user as input.

Next I create a constructor to instantiate Tidy:

public BrowserView() { tidy = new Tidy(); }

With all of that complete I am ready to implement the various methods. There is no need to implement setFocus or widgetDefaultSelected in this case, so those will remain empty.

public void createPartControl(Composite parent) { GridLayout gridLayout = new GridLayout(); gridLayout.numColumns = 2; parent.setLayout(gridLayout); browse = new Button(parent, SWT.PUSH); browse.setText("File Browse"); browse.addSelectionListener(this); render = new Button(parent, SWT.PUSH); render.setText("Render"); render.addSelectionListener(this); GridData gridData = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL); gridData.horizontalSpan = 2; SashForm sashForm = new SashForm(parent, SWT.HORIZONTAL); sashForm.setLayoutData(gridData); left = new Browser(sashForm, SWT.NONE); right = new Browser(sashForm, SWT.NONE); parent.pack(); }

The createPartControl method is implemented above. The first step is to assign a GridLayout with two columns to the parent composite. Next, I create my buttons and add a SelectionListener to each one ensuring that my class will be notified when the buttons are pressed.

Next, I create a SashForm to implement my split-pane. Because I want the SashForm to span the entire width of my view I use a GridData object to indicate that. Finally, I create instances of each of my browsers and then pack the parent Composite to have everything lay out correctly.

That leaves me with the widgetSelected method. This method will be called each time one of the buttons is clicked. If the browse button is clicked then I want to present a dialog box to browse the file system for a HTML file. If the render button is clicked then I want to execute Tidy and render the results. The method's code is shown below:

public void widgetSelected(SelectionEvent e) { if(e.getSource().equals(browse)) { FileDialog dialog = new FileDialog(getSite().getShell()); selectedFile = dialog.open(); } else if(e.getSource().equals(render)) { if(selectedFile != null || selectedFile.length() > 0) { try { BufferedInputStream in = new BufferedInputStream(new FileInputStream(selectedFile)); BufferedOutputStream out = new BufferedOutputStream(new
FileOutputStream(selectedFile + "-tidy.html")); tidy.parse(in, out); in.close(); out.close(); left.setUrl(new File(selectedFile).toURL().toString()); right.setUrl(new File(selectedFile + "-tidy.html").toURL().toString()); } catch (Exception ex) { ex.printStackTrace(); } } } }

By calling the getSource method on the SelectionEvent I can determine which button was pressed. In the case of the browse button, I create an instance of FileDialog and then call its open method. The open method will take care of actually presenting the dialog to the user and will return a string indicating his or her selection, which I assign to the selectedFile field for later use.

In this case of the render button, I need to do several things. First, I need to make sure that a file has actually been selected. After that, I can create a FileInputStream against the selected file as well as a FileOutputStream to write the results from Tidy. I then call Tidy's parse method with my streams and then close them. With the streams closed I just need to render them, which I do by turning the respective files into URLs using the File object.

Possible Problems
When I first attempted to use JTidy with Eclipse I ran into some problems. It seems that with certain platforms, the binary version of JTidy will not work. The solution is to recompile JTidy itself. Because of this, I created a JTidy plugin for Eclipse that can be referenced as a dependent plugin instead of adding the library directly to your classpath. I would recommend using this plugin instead of the JTidy binary, so that any deployment issues on other platforms are avoided.

The downloadable code provided as part of this article contains both the Tidy plugin and the example split-pane browser plugin. These plugins should work together out of the box. Also included is the HTML from my previous Eclipse article that can be used as input to Tidy.

Matt Liotta started his development career at the age of 12 by building C applications for faculty at Emory University. Since then he worked with an early ASP, Cignify, to build their transaction processing system for payroll time data.
Thanks for your registration, follow us on our social networks to keep up-to-date