edu.isistan.carcha.plugin.editors.TraceabilityEditor.java Source code

Java tutorial

Introduction

Here is the source code for edu.isistan.carcha.plugin.editors.TraceabilityEditor.java

Source

/**
 * Copyright 2015 UNICEN. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package edu.isistan.carcha.plugin.editors;

import isistan.edu.carcha.model.carcha.CarchaProject;
import isistan.edu.carcha.model.carcha.DesignDecision;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.FillLayout;
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.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.MultiPageEditorPart;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.PieSectionLabelGenerator;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.plot.PiePlot;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.experimental.chart.swt.ChartComposite;
import org.jfree.util.Rotation;

import edu.isistan.carcha.util.PluginUtil;

/**
 * An example showing how to create a multi-page editor. This example has 3
 * pages:
 * <ul>
 * <li>page 0 contains a nested text editor.
 * <li>page 1 allows you to change the font used in page 2
 * <li>page 2 shows the words in page 0 in sorted order
 * </ul>
 */
public class TraceabilityEditor extends MultiPageEditorPart implements IResourceChangeListener {

    /** The Constant logger. */
    private static final Log logger = LogFactory.getLog(TraceabilityEditor.class);

    /** The cp. */
    CarchaProject cp;

    /** The impact dataset. */
    DefaultPieDataset ddDataset, impactDataset;

    /** The dirty. */
    private boolean dirty = false;

    /** The text editor used in page 0. */
    private TextEditor editor;

    /** The markers. */
    List<IMarker> markers;

    /** The bottom new link. */
    TableViewer topViewLink, bottomViewLink, topNewLink, bottomNewLink;

    /**
     * Creates a multi-page editor example.
     */
    public TraceabilityEditor() {
        super();
        ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
    }

    /**
     * Loads the current file into a CarchaProject.
     */
    void createCarchaProject() {
        IFile file = (IFile) editor.getEditorInput().getAdapter(IFile.class);
        cp = PluginUtil.loadTraceabilityModel(file);
    }

    /**
     * Creates the columns.
     *
     * @param parent the parent
     * @param viewer the viewer
     */
    void createColumns(final Composite parent, final TableViewer viewer) {
        String[] titles = { "Type", "Label" };
        int[] bounds = { 100, 700 };

        // first column is for the kind
        TableViewerColumn col = createTableViewerColumn(titles[0], bounds[0], 0, viewer);
        col.setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                String[] temp = (String[]) element;
                return temp[0];
            }
        });

        // second column is for the  name
        col = createTableViewerColumn(titles[1], bounds[1], 1, viewer);
        col.setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                String[] temp = (String[]) element;
                return temp[1];
            }
        });
    }

    /**
     * Creates the pages of the multi-page editor.
     */
    protected void createPages() {
        markers = new ArrayList<IMarker>();
        impactListPage();
        impactPieChartPage();
        createTraceabilityLinkPage();
        sourcePage();

        createCarchaProject();
        generateWarnings();
        generateLinkViewData();
    }

    /**
     * Creates the table viewer column.
     *
     * @param title the title
     * @param bound the bound
     * @param colNumber the col number
     * @param viewer the viewer
     * @return the table viewer column
     */
    TableViewerColumn createTableViewerColumn(String title, int bound, final int colNumber, TableViewer viewer) {
        final TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.NONE);
        final TableColumn column = viewerColumn.getColumn();
        column.setText(title);
        column.setWidth(bound);
        column.setResizable(true);
        column.setMoveable(true);
        return viewerColumn;
    }

    /**
     * Creates a page to allow users to create a traceability link.
     */
    void createTraceabilityLinkPage() {
        final Composite composite = new Composite(getContainer(), SWT.NONE);
        composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 1, 5));
        composite.setLayout(new GridLayout());

        Label concernLabel = new Label(composite, SWT.BORDER);
        concernLabel.setText("Crosscuttings Concerns(CCC)");
        concernLabel.setToolTipText("This are the Crosscuttings Concerns detected on the requierement document");
        GridData gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
        concernLabel.setLayoutData(gridData);

        topNewLink = new TableViewer(composite, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
        createColumns(composite, topNewLink);

        final Table table = topNewLink.getTable();
        table.setHeaderVisible(true);
        table.setLinesVisible(true);

        topNewLink.setContentProvider(new ArrayContentProvider());
        getSite().setSelectionProvider(topNewLink);
        // define layout for the viewer
        gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        topNewLink.getControl().setLayoutData(gridData);

        Button button = new Button(composite, SWT.PUSH);
        button.setText("Link");
        button.addSelectionListener(new SelectionListener() {
            public void widgetDefaultSelected(SelectionEvent event) {
            }

            public void widgetSelected(SelectionEvent event) {
                IStructuredSelection topSelection = (IStructuredSelection) topNewLink.getSelection();
                IStructuredSelection bottomSelection = (IStructuredSelection) bottomNewLink.getSelection();

                String[] crosscuttingConcern = (String[]) topSelection.getFirstElement();
                String[] designDecision = (String[]) bottomSelection.getFirstElement();

                if ((crosscuttingConcern != null) && (designDecision != null)) {
                    // create dialog with ok and cancel button and info icon
                    MessageBox dialog = new MessageBox(composite.getShell(),
                            SWT.ICON_QUESTION | SWT.OK | SWT.CANCEL);
                    dialog.setText("Link confirmation");
                    dialog.setMessage("Do you want to link the selected items?");

                    // open dialog and await user selection
                    int response = dialog.open();
                    if (response == SWT.OK) {
                        PluginUtil.createNewLink(crosscuttingConcern, designDecision, cp);
                        dirty = true;
                        firePropertyChange(IEditorPart.PROP_DIRTY);
                    }
                } else {
                    MessageDialog.openError(composite.getShell(), "Error", "Please select item(s) to link");
                }
            }
        });

        gridData = new GridData(SWT.CENTER, SWT.TOP, false, false, 2, 1);
        button.setLayoutData(gridData);

        Label ddsLabel = new Label(composite, SWT.BORDER);
        ddsLabel.setText("Architectural design decisions");
        ddsLabel.setToolTipText("This are the design decisions detected in the architectural document");
        gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
        ddsLabel.setLayoutData(gridData);
        bottomNewLink = new TableViewer(composite, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
        createColumns(composite, bottomNewLink);

        Table table2 = bottomNewLink.getTable();
        table2.setHeaderVisible(true);
        table2.setLinesVisible(true);

        bottomNewLink.setContentProvider(new ArrayContentProvider());

        getSite().setSelectionProvider(bottomNewLink);
        gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        bottomNewLink.getControl().setLayoutData(gridData);

        int index = addPage(composite);
        setPageText(index, "New Link");
    }

    /**
     * The <code>MultiPageEditorPart</code> implementation of this
     * <code>IWorkbenchPart</code> method disposes all nested editors.
     * Subclasses may extend.
     */
    public void dispose() {
        for (IMarker marker : markers) {
            try {
                marker.delete();
            } catch (CoreException e) {
                logger.error("Error disposing the editors", e);
            }
        }
        ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
        super.dispose();
    }

    /**
     * Saves the multi-page editor's document.
     *
     * @param monitor the monitor
     */
    public void doSave(IProgressMonitor monitor) {
        getEditor(3).doSave(monitor);
        IFile file = (IFile) editor.getEditorInput().getAdapter(IFile.class);
        try {
            PluginUtil.saveTraceabilityModel(file, cp);
        } catch (IOException e) {
            logger.error("Error saving the traceability model", e);

        }
        dirty = false;
        firePropertyChange(IEditorPart.PROP_DIRTY);

    }

    /**
     * Saves the multi-page editor's document as another file. Also updates the
     * text for page 0's tab, and updates this multi-page editor's input to
     * correspond to the nested editor's.
     */
    public void doSaveAs() {
        IEditorPart editor = getEditor(0);
        editor.doSaveAs();
        setPageText(0, editor.getTitle());
        setInput(editor.getEditorInput());
        dirty = false;
        firePropertyChange(IEditorPart.PROP_DIRTY);

    }

    /**
     * Generate link view data.
     */
    void generateLinkViewData() {
        List<String[]> ccs = new ArrayList<String[]>();
        for (isistan.edu.carcha.model.carcha.CrosscuttingConcern cccs : PluginUtil
                .getLinkedCrossCuttingConcerns(cp)) {
            String[] cc = { cccs.getKind(), cccs.getName() };
            ccs.add(cc);
        }

        topViewLink.setInput(ccs);
    }

    /**
     * Get the info for the pie chart.
     */
    void generatePieData() {
        HashMap<String, Integer> values = PluginUtil.getCrossCuttingConcernsDistribution(cp);
        for (String key : values.keySet()) {
            ddDataset.setValue(key, values.get(key));
        }
    }

    /**
     * Generate table data.
     */
    void generateTableData() {
        List<String[]> ccs = new ArrayList<String[]>();
        for (isistan.edu.carcha.model.carcha.CrosscuttingConcern cccs : cp.getConcerns()) {
            String[] cc = { cccs.getKind(), cccs.getName() };
            ccs.add(cc);
        }
        topNewLink.setInput(ccs);

        List<String[]> dds = new ArrayList<String[]>();
        for (DesignDecision ddds : cp.getDesignDecisions()) {
            String[] dd = { ddds.getKind(), ddds.getName() };
            dds.add(dd);
        }
        bottomNewLink.setInput(dds);

    }

    /**
     * Generates the warning for unlinked crosscutting concerns.
     */
    void generateWarnings() {
        try {
            IFile file = (IFile) editor.getEditorInput().getAdapter(IFile.class);
            for (isistan.edu.carcha.model.carcha.CrosscuttingConcern cccs : PluginUtil.getUnusedConcerns(cp)) {
                IMarker marker = file.createMarker(IMarker.PROBLEM);
                marker.setAttribute(IMarker.MESSAGE, "Link not found on: " + cccs.getName());
                marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
                marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING);
                marker.setAttribute(IMarker.LINE_NUMBER, getLineFor(cccs.getName(), cccs.getKind()));
                markers.add(marker);
            }
            //         for (DesignDecision dds : PluginUtil.getUnusedDesignDecisions(cp)){
            //            IMarker marker = file.createMarker(IMarker.PROBLEM);
            //               marker.setAttribute(IMarker.MESSAGE, "Link not found on: "+ dds.getName());
            //               marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
            //               marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING);
            //               marker.setAttribute(IMarker.LINE_NUMBER, getLineFor(dds.getName(), dds.getKind()));
            //               markers.add(marker);
            //         }
        } catch (Exception e) {
            logger.error("Attribute value was rejected", e);
        }
    }

    /**
     * Gets the line number for a given cross cutting concern or design decision.
     *
     * @param name the name
     * @param kind the kind
     * @return line number
     */
    int getLineFor(String name, String kind) {
        int i = 0;
        String editorText = editor.getDocumentProvider().getDocument(editor.getEditorInput()).get();
        String[] lines = editorText.split(System.getProperty("line.separator"));

        int temp = 1;
        String lineEscaped;
        boolean find = false;
        for (int j = 0; j < lines.length && !find; j++) {
            lineEscaped = StringEscapeUtils.unescapeXml(lines[j]);
            if (lineEscaped.contains(name) && lineEscaped.contains(kind)) {
                i = temp;
                find = true;
            }
            temp++;
        }
        return i;
    }

    /**
     * Goto marker.
     *
     * @param marker the marker
     */
    /*
     * (non-Javadoc) Method declared on IEditorPart
     */
    public void gotoMarker(IMarker marker) {
        setActivePage(0);
        IDE.gotoMarker(getEditor(0), marker);
    }

    /**
     * Creates the impact list page.
     */
    void impactListPage() {
        final Composite composite = new Composite(getContainer(), SWT.NONE);
        composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1));
        composite.setLayout(new GridLayout());

        Label concernLabel = new Label(composite, SWT.BORDER);
        concernLabel.setText("Crosccuting Concerns(CCC)");
        concernLabel.setToolTipText("This are the concern detected on the requierement document.");
        GridData gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
        concernLabel.setLayoutData(gridData);

        /////////////////////
        ScrolledComposite sc = new ScrolledComposite(composite, SWT.H_SCROLL | SWT.V_SCROLL);
        Composite parent = new Composite(sc, SWT.NONE);
        parent.setLayout(new GridLayout());

        topViewLink = new TableViewer(parent, SWT.BORDER);
        createColumns(parent, topViewLink);

        final Table table = topViewLink.getTable();
        table.setHeaderVisible(true);
        table.setLinesVisible(true);

        topViewLink.setContentProvider(new ArrayContentProvider());

        getSite().setSelectionProvider(topViewLink);
        GridData data = new GridData(SWT.FILL, SWT.FILL, true, false);
        data.heightHint = 10 * table.getItemHeight();
        table.setLayoutData(data);

        sc.setContent(parent);
        sc.setExpandHorizontal(true);
        sc.setExpandVertical(true);
        sc.setMinSize(parent.computeSize(SWT.DEFAULT, SWT.DEFAULT));

        /////////////////////

        Button button = new Button(composite, SWT.PUSH);
        button.setText("Remove");
        button.addSelectionListener(new SelectionListener() {
            public void widgetDefaultSelected(SelectionEvent event) {
            }

            public void widgetSelected(SelectionEvent event) {
                IStructuredSelection topSelection = (IStructuredSelection) topViewLink.getSelection();
                IStructuredSelection bottomSelection = (IStructuredSelection) bottomViewLink.getSelection();

                String[] crosscuttingConcern = (String[]) topSelection.getFirstElement();
                String[] designDecision = (String[]) bottomSelection.getFirstElement();
                if (topSelection.size() > 1) {
                    MessageDialog.openError(composite.getShell(), "Error",
                            "Please select one crosscutting concern");
                } else {
                    if ((crosscuttingConcern != null) && (designDecision != null)) {
                        // create dialog with ok and cancel button and info icon
                        MessageBox dialog = new MessageBox(composite.getShell(),
                                SWT.ICON_QUESTION | SWT.OK | SWT.CANCEL);
                        dialog.setText("Link confirmation");
                        dialog.setMessage("Do you want to remove the link between the selected items?");

                        // open dialog and await user selection
                        int response = dialog.open();
                        if (response == SWT.OK) {
                            PluginUtil.removeLink(crosscuttingConcern, designDecision, cp);
                            dirty = true;
                            firePropertyChange(IEditorPart.PROP_DIRTY);
                            // update the list after the remove
                            generateLinkViewData();
                            bottomViewLink.getTable().clearAll();
                        }
                    } else {
                        MessageDialog.openError(composite.getShell(), "Error",
                                "Please select a crosscutting concern and a design decision to remove a traceability link");
                    }
                }
            }
        });
        gridData = new GridData(SWT.CENTER, SWT.TOP, false, false, 2, 1);
        button.setLayoutData(gridData);

        Label ddsLabel = new Label(composite, SWT.BORDER);
        ddsLabel.setText("Architectural design decisions");
        ddsLabel.setToolTipText("This are the design decisions detected on the architectural document");

        gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
        ddsLabel.setLayoutData(gridData);

        bottomViewLink = new TableViewer(composite,
                SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
        createColumns(composite, bottomViewLink);
        Table table2 = bottomViewLink.getTable();
        table2.setHeaderVisible(true);
        table2.setLinesVisible(true);
        bottomViewLink.setContentProvider(new ArrayContentProvider());
        topViewLink.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                IStructuredSelection selection = (IStructuredSelection) event.getSelection();
                if (!selection.isEmpty()) {
                    String[] cccs = (String[]) selection.getFirstElement();
                    List<DesignDecision> dds = PluginUtil.getDesignDecisionsForCrossCuttingConcern(cp, cccs[1],
                            cccs[0]);
                    logger.info("Impact List for CCC (" + dds.size() + " DDD): " + cccs[0] + " - " + cccs[1]);
                    List<String[]> designDecisions = new ArrayList<String[]>();

                    for (DesignDecision dd : dds) {
                        String[] designDecision = { dd.getKind(), dd.getName() };
                        designDecisions.add(designDecision);
                    }
                    bottomViewLink.setInput(designDecisions);
                }
            }
        });
        getSite().setSelectionProvider(bottomViewLink);
        gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        bottomViewLink.getControl().setLayoutData(gridData);

        int index = addPage(composite);
        setPageText(index, "Links");
    }

    /**
     * Creates the impact Pie Chart page. It allows users to select a Crosscutting Concern and see Design Decision type distribution
     */
    void impactPieChartPage() {

        ddDataset = new DefaultPieDataset();
        impactDataset = new DefaultPieDataset();
        JFreeChart ddChart = ChartFactory.createPieChart("Crosscutting Concerns", ddDataset, true, true, false);
        JFreeChart impactChart = ChartFactory.createPieChart("Design Decisions", impactDataset, true, true, false);

        PiePlot ddPlot = (PiePlot) ddChart.getPlot();
        ddPlot.setStartAngle(290);
        ddPlot.setDirection(Rotation.CLOCKWISE);
        ddPlot.setForegroundAlpha(0.5f);

        PiePlot impactPlot = (PiePlot) impactChart.getPlot();
        impactPlot.setStartAngle(290);
        impactPlot.setDirection(Rotation.CLOCKWISE);
        impactPlot.setForegroundAlpha(0.5f);

        PieSectionLabelGenerator gen = new StandardPieSectionLabelGenerator("{0}  ({2})");
        ddPlot.setLabelGenerator(gen);
        impactPlot.setLabelGenerator(gen);

        Composite composite = new Composite(getContainer(), SWT.NONE);
        FillLayout layout = new FillLayout();
        composite.setLayout(layout);
        final ChartComposite cComposite = new ChartComposite(composite, SWT.NONE, ddChart, true);
        cComposite.addChartMouseListener(new ChartMouseListener() {

            @Override
            public void chartMouseClicked(ChartMouseEvent event) {
                String[] parts = event.getEntity().getToolTipText().split(":");
                HashMap<String, Integer> values = PluginUtil.getDDDistributionForCrossCuttingConcern(cp, parts[0]);
                logger.info("Impact Pie Chart for: " + parts[0]);
                impactDataset.clear();
                for (String key : values.keySet()) {
                    impactDataset.setValue(key, values.get(key));
                }
            }

            @Override
            public void chartMouseMoved(ChartMouseEvent event) {
            }
        });
        new ChartComposite(composite, SWT.NONE, impactChart, true);
        int index = addPage(composite);
        setPageText(index, "Graph");
    }

    /**
     * The <code>MultiPageEditorExample</code> implementation of this method
     * checks that the input is an instance of <code>IFileEditorInput</code>.
     *
     * @param site the site
     * @param editorInput the editor input
     * @throws PartInitException the part init exception
     */
    public void init(IEditorSite site, IEditorInput editorInput) throws PartInitException {
        if (!(editorInput instanceof IFileEditorInput))
            throw new PartInitException("Invalid Input: Must be IFileEditorInput");
        super.init(site, editorInput);
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.part.MultiPageEditorPart#isDirty()
     */
    @Override
    public boolean isDirty() {
        return dirty;
    }

    /*
     * (non-Javadoc) Method declared on IEditorPart.
     */
    public boolean isSaveAsAllowed() {
        return false;
    }

    /**
     * Calculates the contents of page 2 when the it is activated.
     *
     * @param newPageIndex the new page index
     */
    protected void pageChange(int newPageIndex) {
        super.pageChange(newPageIndex);
        if (newPageIndex == 0) {
            generateLinkViewData();
        }
        if (newPageIndex == 1) {
            generatePieData();
        }
        if (newPageIndex == 2) {
            generateTableData();
        }

    }

    /**
     * Closes all project files on project close.
     *
     * @param event the event
     */
    public void resourceChanged(final IResourceChangeEvent event) {
        if (event.getType() == IResourceChangeEvent.PRE_CLOSE) {
            Display.getDefault().asyncExec(new Runnable() {
                public void run() {
                    IWorkbenchPage[] pages = getSite().getWorkbenchWindow().getPages();
                    for (int i = 0; i < pages.length; i++) {
                        if (((FileEditorInput) editor.getEditorInput()).getFile().getProject()
                                .equals(event.getResource())) {
                            IEditorPart editorPart = pages[i].findEditor(editor.getEditorInput());
                            pages[i].closeEditor(editorPart, true);
                        }
                    }
                }
            });
        }
    }

    /**
     * Creates a View Source page
     */
    void sourcePage() {
        try {
            editor = new TextEditor();
            int index = addPage(editor, getEditorInput());
            setPageText(index, "Source");
        } catch (PartInitException e) {
            ErrorDialog.openError(getSite().getShell(), "Error creating nested text editor", null, e.getStatus());
        }
    }

}