com.drgarbage.sourcecodevisualizer.editors.JavaCodeEditor.java Source code

Java tutorial

Introduction

Here is the source code for com.drgarbage.sourcecodevisualizer.editors.JavaCodeEditor.java

Source

/**
 * Copyright (c) 2008-2012, Dr. Garbage Community
 *
 * 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 com.drgarbage.sourcecodevisualizer.editors;

import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.draw2d.FigureCanvas;
import org.eclipse.draw2d.MouseEvent;
import org.eclipse.draw2d.MouseListener;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor;
import org.eclipse.jdt.internal.ui.javaeditor.ICompilationUnitDocumentProvider;
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditorBreadcrumb;
import org.eclipse.jdt.ui.IContextMenuConstants;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextListener;
import org.eclipse.jface.text.TextEvent;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.projection.IProjectionListener;
import org.eclipse.jface.text.source.projection.ProjectionViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ST;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Sash;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

import com.drgarbage.controlflowgraph.intf.IDirectedGraphExt;
import com.drgarbage.core.CorePlugin;
import com.drgarbage.core.preferences.CorePreferenceConstants;
import com.drgarbage.core.views.ControlFlowGraphViewPage;
import com.drgarbage.core.views.IControlFlowGraphView;
import com.drgarbage.core.views.IControlFlowGraphViewPage;
import com.drgarbage.sourcecodevisualizer.SourcecodeVisualizerPlugin;
import com.drgarbage.sourcecodevisualizer.actions.ConstructorFilterAction;
import com.drgarbage.sourcecodevisualizer.actions.MethodGraphFilterAction;
import com.drgarbage.sourcecodevisualizer.actions.OpenGraphInControlflowgraphFactory;
import com.drgarbage.sourcecodevisualizer.actions.RefreshAction;

/**
 * Java Editor with an embeded sourcecode visualizer.
 *
 * @author Sergej Alekseev
 * @version $Revision$
 * $Id: JavaCodeEditor.java 1523 2012-04-13 14:34:24Z Sergej Alekseev $
 */
public class JavaCodeEditor extends CompilationUnitEditor implements IControlFlowGraphView {

    /**
     * Parent composite. 
     */
    private Composite parent;

    /**
     * Preference property change listener.
     */
    private IPropertyChangeListener preferenceListener;

    /**
     * Control flow graph viewer.
     */
    private SourceCodeGraphCanvas canvasControlFlowGraph;

    /**
     * Text widget.
     */
    private StyledText textWidget;

    /**
     * Document line number counter.
     */
    private int lineCount = 0;

    /**
     * Flag if the graph has to be shown in the outline.
     */
    private boolean showGraphInSeparateView = false;

    /**
     * Graph Factory Action.
     */
    private OpenGraphInControlflowgraphFactory graphFactoryAction = null;

    /**
     * Reference to the selcted Method.
     */
    private IMethod selectedMethod = null;

    /**
     * Default Contructor.
     */
    public JavaCodeEditor() {
        super();
    }

    /**
     * The canonical empty selection. This selection should be used 
     * for bug correction bug#91 NullPointerException if license has been expired.
     */
    public final StructuredSelection EMPTY = new StructuredSelection2();

    /* bug#91 NullPointerException if license has been expired. */
    class StructuredSelection2 extends StructuredSelection {
        public Object getFirstElement() {
            return new JavaEditorBreadcrumb(getEditor());
        }
    }

    /* bug#91 NullPointerException if license has been expired. */
    private JavaEditor getEditor() {
        return this;
    }

    /**
     * Creates the SWT controls for this editor.
     * @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
     */
    public void createPartControl(final Composite parent) {
        this.parent = parent;

        /* init preferences */
        String s = CorePlugin.getDefault().getPreferenceStore()
                .getString(CorePreferenceConstants.GRAPH_PANEL_LOCATION);
        if (s.equals(CorePreferenceConstants.GRAPH_PANEL_LOCATION_SEPARATE_VIEW)) {
            showGraphInSeparateView = true;
        } else if (s.equals(CorePreferenceConstants.GRAPH_PANEL_LOCATION_EDITOR)) {
            showGraphInSeparateView = false;
        } else {
            showGraphInSeparateView = false;
            //FIXME Warn here
        }

        final IEditorInput editorInput = getEditorInput();
        final IWorkbenchPage page = getEditorSite().getPage();
        final IEditorPart part = this;
        final String editorID = getEditorSite().getId();

        createPartControlsImpl(parent);

        /* disable folding */
        final ProjectionViewer viewer = (ProjectionViewer) getSourceViewer();
        if (viewer != null) {
            if (viewer.isProjectionMode())
                viewer.doOperation(ProjectionViewer.TOGGLE);

            viewer.addProjectionListener(new IProjectionListener() {
                public void projectionDisabled() {
                    /* nothing to do */
                }

                public void projectionEnabled() {
                    viewer.doOperation(ProjectionViewer.TOGGLE);
                }
            });
        }
    }

    private void createPartControlsImpl(Composite parent) {
        /* create controls */
        if (showGraphInSeparateView) {
            createPartControlWithGraphOutline(parent);
        } else {
            createPartControlWithSach(parent);
        }
    }

    /**
     * Creates the SWT controls for this editor.
     */
    public void createPartControlWithGraphOutline(final Composite parent) {

        final StackLayout layout = new StackLayout();
        parent.setLayout(layout);

        /* create the control for the Byte Code Editor */
        Composite editorParent = new Composite(parent, SWT.NONE);
        editorParent.setLayout(new FillLayout());
        super.createPartControl(editorParent);
        layout.topControl = editorParent;

        /* deactivate graph actions */
        IAction a = getAction(OpenGraphInControlflowgraphFactory.ID);
        a.setEnabled(false);
    }

    /**
     * Creates the SWT controls for this editor.
     * @param parent
     */
    public void createPartControlWithSach(final Composite parent) {

        /* set parent Layout */
        final FormLayout form = new FormLayout();
        parent.setLayout(form);

        /* Create sash and set its layout */
        final Sash sash = new Sash(parent, SWT.VERTICAL);

        /* Wrap the first view: Byte Code Editor */
        Composite editorParent = new Composite(parent, SWT.NONE);
        FormData editorParentFormData = new FormData();
        editorParentFormData.left = new FormAttachment(0, 0);
        editorParentFormData.right = new FormAttachment(sash, 0);
        editorParentFormData.top = new FormAttachment(0, 0);
        editorParentFormData.bottom = new FormAttachment(100, 0);
        editorParent.setLayoutData(editorParentFormData);
        editorParent.setLayout(new FillLayout());

        /* create the control for the Byte Code Editor */
        super.createPartControl(editorParent);

        /* create the control flow graph viewer */
        textWidget = getSourceViewer().getTextWidget();

        /* FIX: unfold document, because of the line number count */
        final ProjectionViewer viewer = (ProjectionViewer) getSourceViewer();
        if (viewer != null) {
            if (viewer.isProjectionMode())
                viewer.doOperation(ProjectionViewer.TOGGLE);
        }

        /* canvas for the control flow graph viewer */
        canvasControlFlowGraph = new SourceCodeGraphCanvas(parent);
        List<IDirectedGraphExt> graphs = createGraphs();
        lineCount = textWidget.getLineCount();
        canvasControlFlowGraph.init(lineCount, textWidget.getLineHeight(), graphs);

        /* Wrap the second view: Control Flow Graph Viewer */
        FormData editorParentFormData2 = new FormData();
        editorParentFormData2.left = new FormAttachment(sash, 0);
        editorParentFormData2.right = new FormAttachment(100, 0);
        editorParentFormData2.top = new FormAttachment(0, 0);
        editorParentFormData2.bottom = new FormAttachment(100, 0);
        canvasControlFlowGraph.setLayoutData(editorParentFormData2);

        /* init control flow graph view */
        setControlFlowViewBackGround();

        /* Synchronize scroll bars in the left and right views */
        synchronizeScrollBars(textWidget, canvasControlFlowGraph);

        /* Synchronize line selection in the left and right views */
        synchronizeLineSelection(textWidget, canvasControlFlowGraph);

        /* synchronize current line color */
        synchronizeCurrentLineColor();

        /* default form data for the sash */
        final FormData sashFormData = new FormData();
        sashFormData.left = new FormAttachment(0, 0);
        sashFormData.top = new FormAttachment(0, 0);
        sashFormData.bottom = new FormAttachment(100, 0);
        sash.setLayoutData(sashFormData);

        final int graphAreaWidht = canvasControlFlowGraph.getFigureWidth();
        final int sashWidthoffset = 24; //sash + balks

        /* resize of the editor shell */
        parent.addControlListener(new ControlListener() {
            public void controlMoved(ControlEvent e) {
                /* nothing to do */
            }

            /* set the left are = size of the graph view, but maximal 50% of the whole view */
            public void controlResized(ControlEvent e) {
                Rectangle shellRect = parent.getClientArea();
                int right = shellRect.width - graphAreaWidht - sashWidthoffset;
                right = Math.max(right, shellRect.width / 2);
                sashFormData.left.offset = right;
                parent.layout();
            }
        });

        /* dynamic resizing of the sash area */
        final int limit = 40;
        sash.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event e) {
                Rectangle sashRect = sash.getBounds();
                Rectangle shellRect = parent.getClientArea();
                int right = shellRect.width - sashRect.width - limit;
                e.x = Math.max(Math.min(e.x, right), limit);

                if ((shellRect.width - e.x - sashWidthoffset) < 0) {/*check the minimum size*/
                    e.x = shellRect.width - graphAreaWidht - sashWidthoffset;
                }

                if (e.x != sashRect.x) {
                    sashFormData.left.offset = e.x;
                    parent.layout();
                }
            }
        });

        getSourceViewer().addTextListener(new ITextListener() {

            public void textChanged(TextEvent event) {
                if (lineCount != textWidget.getLineCount()) {
                    lineCount = textWidget.getLineCount();
                    canvasControlFlowGraph.setHeightByLineCount(lineCount);
                }
            }
        });

    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#setAction(java.lang.String, org.eclipse.jface.action.IAction)
     */
    public void setAction(String actionID, IAction action) {
        super.setAction(actionID, action);
    }

    /*
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#editorContextMenuAboutToShow(org.eclipse.jface.action.IMenuManager)
     * @since 3.1
     */
    public void editorContextMenuAboutToShow(IMenuManager menu) {
        super.editorContextMenuAboutToShow(menu);
        IAction graphFactoryAction = getAction(OpenGraphInControlflowgraphFactory.ID);
        if (graphFactoryAction != null)
            //menu.add(graphFactoryAction);
            menu.appendToGroup(IContextMenuConstants.GROUP_OPEN, graphFactoryAction);

        /* set context menu in the control flow view */
        Menu m = ((MenuManager) menu).getMenu();
        if (m != null && !m.isDisposed())//TODO: menu is disposed if the control flow graph view is closed.
            canvasControlFlowGraph.setMenu(m);
    }

    /**
     * Gets the backroud color from preferencies and
     * sets it in the control flow graph view.
     */
    private void setControlFlowViewBackGround() {
        RGB rgb = PreferenceConverter.getColor(getPreferenceStore(),
                AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND);
        Color c = getSharedColors().getColor(rgb);

        canvasControlFlowGraph.setBackground(c);
    }

    /**
     * Gets current line color from preferences and sets the it in the
     * control flow graph viewer.
     * @param the control flow graph viewer.
     */
    private void synchronizeCurrentLineColor() {
        RGB rgb = PreferenceConverter.getColor(getPreferenceStore(),
                AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE_COLOR);
        Color c = getSharedColors().getColor(rgb);
        canvasControlFlowGraph.setLineSelectorColor(c);
    }

    /**
     * Create a list of graphs.
     * @return list of graphs
     */
    private List<IDirectedGraphExt> createGraphs() {

        IDocumentProvider p = getDocumentProvider();
        if (p instanceof ICompilationUnitDocumentProvider) {
            ICompilationUnitDocumentProvider cp = (ICompilationUnitDocumentProvider) p;
            ICompilationUnit unit = cp.getWorkingCopy(getEditorInput());
            IJavaProject jp = unit.getJavaProject();

            return SourcecodeGraphUtils.createGraphs(unit, jp);
        }

        return null;
    }

    class JavaCodeEditorPropertyChangeListener implements IPropertyChangeListener {

        /* (non-Javadoc)
         * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
         */
        public void propertyChange(PropertyChangeEvent event) {
            //System.out.println("event=" + event.getProperty()); //TODO: log for debuging

            /* bytecode visualizer property */
            if (event.getProperty().startsWith(AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND)) {
                setControlFlowViewBackGround();
            } else if (event.getProperty()
                    .equals(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE_COLOR)) {
                RGB rgb = PreferenceConverter.getColor(getPreferenceStore(),
                        AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE_COLOR);
                Color c = getSharedColors().getColor(rgb);
                if (!canvasControlFlowGraph.getLineSelectorColor().equals(c)) {
                    canvasControlFlowGraph.setLineSelectorColor(c);
                }
            } else if (event.getProperty().equals(JFaceResources.TEXT_FONT)) { /* text fond has been changed */
                /*
                 * Hack: The default handler has a bug.
                 * The JavaPlugin.getDefault().getPreferenceStore() is not updated synchronously.
                 * This code copy the new font value to the default preference store.  
                 */
                Font f = JFaceResources.getTextFont();
                FontData data = f.getFontData()[0];
                //System.out.println(data); //TODO: Log for debuging
                JavaPlugin.getDefault().getPreferenceStore().setValue(event.getProperty(), data.toString());

                /* 
                 * Set new font in the text widget. The font will be overwriten later with
                 * the same value. But we have to set font to calculate the line heght.
                 */
                textWidget.setFont(f);

                //TODO: define better update method without new generation of graphs
                List<IDirectedGraphExt> graphs = createGraphs();
                canvasControlFlowGraph.init(textWidget.getLineCount(), textWidget.getLineHeight(), graphs);
            } else if (event.getProperty().startsWith(CorePreferenceConstants.GRAPH_COLOR_PREFIX)) {
                /* bytecode visualizer property */
                //TODO: define better update method without new generation of graphs
                List<IDirectedGraphExt> graphs = createGraphs();
                canvasControlFlowGraph.init(textWidget.getLineCount(), textWidget.getLineHeight(), graphs);
            } else {
                /* other references which are not matched before */
                //System.out.println("event=" + event.getProperty());
            }
        }
    };

    /*
     * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#initializeEditor()
     */
    protected void initializeEditor() {
        super.initializeEditor();

        /* create preference property listener */
        preferenceListener = new JavaCodeEditorPropertyChangeListener();

        /* fond and color preferences */
        JFaceResources.getFontRegistry().addListener(preferenceListener);

        /* bytecode visualizer property */
        EditorsUI.getPreferenceStore().addPropertyChangeListener(preferenceListener);

        /* bytecode visualizer property */
        SourcecodeVisualizerPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(preferenceListener);
    }

    /* (non-Javadoc)
     * @see org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor#dispose()
     */
    public void dispose() {
        /* remove property listeners */

        /* fond and color preferences */
        JFaceResources.getFontRegistry().removeListener(preferenceListener);

        /* bytecode visualizer property */
        EditorsUI.getPreferenceStore().removePropertyChangeListener(preferenceListener);

        /* bytecode visualizer property */
        SourcecodeVisualizerPlugin.getDefault().getPreferenceStore()
                .removePropertyChangeListener(preferenceListener);

        /* call default dispose method */
        super.dispose();
    }

    /**
     * Synchronisation of scrol bars in the left and right views.
     * @param the control object of the first view. (text editor)
     * @param the control object of the second view. (control flow graph view)
     */
    private void synchronizeScrollBars(final StyledText textWidget, final FigureCanvas controlFlowGraphCanvas) {
        /* Synchronize scroll bars */
        //final ScrollBar vBar1 = textWidget.getVerticalBar();
        final ScrollBar vBar2 = controlFlowGraphCanvas.getVerticalBar();

        /* 
         * The listener for left scroll bar. 
         * The right scroll bar is synchronized by the paint listener. 
         */
        SelectionListener listener2 = new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                //int y =  vBar2.getSelection() * (vBar1.getMaximum() - vBar1.getThumb()) / Math.max(1, vBar2.getMaximum() - vBar2.getThumb());
                int y = vBar2.getSelection();
                textWidget.setTopPixel(y);
            }
        };

        vBar2.addSelectionListener(listener2);
    }

    /**
     * Synchronization of the line selection in the text editor and
     * the control flow graph view.
     * The following table represents the implementation of listeners.
     * 
     * <p><code>  Text Editor             |    Control Flow Graph Viewer </code></p>
     * <p><code>  ------------------------+----------------------------- </code></p>                      
     * <p><code>  update line selection  <---  Key Listener              </code></p>
     * <p><code>  update line selection  <---  Mouse Listener          </code></p>
     * <p><code>  SWT.Paint Listener     ---->  update line selection     </code></p>
     *  
     * @param the control object of the first view. (text editor)
     * @param the control object of the second view. (control flow graph view)
     * @param the control flow graph viewer.
     * @param the canvas of the control flow graph viewer.
     */
    private void synchronizeLineSelection(final StyledText textWidget,
            final SourceCodeGraphCanvas canvasControlFlowGraph) {

        final ScrollBar vBar1 = textWidget.getVerticalBar();
        //      final ScrollBar vBar2 = canvasControlFlowGraph.getVerticalBar();

        /* paint listener for the text editor view */
        textWidget.addPaintListener(new PaintListener() {

            public void paintControl(PaintEvent e) {
                if (canvasControlFlowGraph.isDisposed())
                    return;

                canvasControlFlowGraph.selectLine(getSelectedLine());

                int y = vBar1.getSelection();
                canvasControlFlowGraph.scrollSmoothTo(canvasControlFlowGraph.getHorizontalBar().getSelection(), y);

                /* by scrolling in the left window the canvas is not updated. 
                 * MAC OS specific correction. Not need for SWT 3.5 */
                canvasControlFlowGraph.getViewport().repaint();
            }

        });

        /* key listener for the control flow graph view */
        canvasControlFlowGraph.addKeyListener(new KeyListener() {

            public void keyPressed(KeyEvent e) {
                switch (e.keyCode) {
                case ARROW_UP: /* Up */
                {
                    StyledText widget = getSourceViewer().getTextWidget();
                    widget.invokeAction(ST.LINE_UP);
                    widget.redraw();
                    selectLine(getSelectedLine());
                    break;
                }
                case ARROW_DOWN: /* Down */
                {
                    StyledText widget = getSourceViewer().getTextWidget();
                    widget.invokeAction(ST.LINE_DOWN);
                    widget.redraw();
                    selectLine(getSelectedLine());
                    break;
                }
                }
            }

            public void keyReleased(KeyEvent e) {
                /* nothing to do */
            }

        });

        /* mouse listener for the control flow graph view */
        canvasControlFlowGraph.getRootFigure().addMouseListener(new MouseListener() {
            public void mouseDoubleClicked(MouseEvent me) {
                /* nothing to do */
            }

            public void mousePressed(MouseEvent me) {
                Point p = me.getLocation();
                int line = p.y / textWidget.getLineHeight();
                selectLine(line);
            }

            public void mouseReleased(MouseEvent me) {
                /* nothing to do */
            }
        });
    }

    private static final int ARROW_DOWN = SWT.KEYCODE_BIT + 2; //keyCode=16777218 (KeyEvent KeyDown)
    private static final int ARROW_UP = SWT.KEYCODE_BIT + 1; //keyCode=16777217 (KeyEvent KeyUp)

    /**
     * Returns the number of the line currently selected
     * in the text editor.
     * @return the selected line
     */
    public int getSelectedLine() {
        ISourceViewer sourceViewer = this.getSourceViewer();

        if (sourceViewer == null)
            return -1;

        /* point.x is offset */
        org.eclipse.swt.graphics.Point selectedRange = sourceViewer.getSelectedRange();
        IDocument document = getDocumentProvider().getDocument(getEditorInput());

        int line = -1;
        try {
            line = document.getLineOfOffset(selectedRange.x);
        } catch (BadLocationException e) {
            System.err.println("ERROR: " + e);
            e.printStackTrace(System.err);
        }

        return line;
    }

    /**
     * Selects the given line.
     * @param the number of the line to be selected. 
     */
    public void selectLine(int line) {
        IDocument document = getDocumentProvider().getDocument(getEditorInput());
        try {
            int offset = document.getLineOffset(line);
            ISourceViewer sourceViewer = this.getSourceViewer();

            if (sourceViewer == null)
                return;

            sourceViewer.setSelectedRange(offset, 0);

        } catch (BadLocationException e) {
            /*nothing to do */}
    }

    /* (non-Javadoc)
     * @see org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor#doSave(org.eclipse.core.runtime.IProgressMonitor)
     */
    public void doSave(IProgressMonitor progressMonitor) {
        super.doSave(progressMonitor);

        /* simple implementation. The compilation is done by separate thread.
         * Just wait a second. It should be enough to compile a source. */
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace(System.err);
        }

        //TODO: imlement synchronisation of the threads by using of JavaCore listener.
        //      JavaCore.addPreProcessingResourceChangedListener(new IResourceChangeListener(){
        //         public void resourceChanged(IResourceChangeEvent event) {
        //         }
        //      }, IResourceChangeEvent.POST_CHANGE);

        refreshGraphPanel();

    }

    public void refreshGraphPanel() {
        if (canvasControlFlowGraph != null) {
            List<IDirectedGraphExt> graphs = createGraphs();
            canvasControlFlowGraph.init(textWidget.getLineCount(), textWidget.getLineHeight(), graphs);
        }
    }

    /**
     * Creates this editor's standard actions and connects them with the global
     * workbench actions for context, toplevel menu and toolbar.
     * <p>
     * Subclasses may extend.</p>
     */
    protected void createActions() {
        super.createActions();

        /* open graph in the control flow factory action */
        graphFactoryAction = new OpenGraphInControlflowgraphFactory(this);
        setAction(OpenGraphInControlflowgraphFactory.ID, graphFactoryAction);

        /* own actions*/
        IAction a = new ConstructorFilterAction(this);
        setAction(ConstructorFilterAction.ID, a);

        a = new MethodGraphFilterAction(this);
        setAction(MethodGraphFilterAction.ID, a);

        a = new RefreshAction(this);
        setAction(RefreshAction.ID, a);
    }

    protected void setSelection(ISourceReference reference, boolean moveCursor) {
        super.setSelection(reference, moveCursor);

        /*
         * FIX: if license expired
         * java.lang.NullPointerException
         * at com.drgarbage.sourcecodevisualizer.editors.JavaCodeEditor.setSelection(JavaCodeEditor.java:827)
         * at org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.setSelection(JavaEditor.java:2380)
         * at org.eclipse.jdt.internal.ui.javaeditor.EditorUtility.revealInEditor(EditorUtility.java:251)
         * at org.eclipse.jdt.ui.JavaUI.revealInEditor(JavaUI.java:729)
         */
        if (graphFactoryAction == null) {
            return;
        }

        if (reference instanceof IMethod) {
            graphFactoryAction.setEnabled(true);
            selectedMethod = (IMethod) reference;
        } else {
            if (graphFactoryAction != null) {
                graphFactoryAction.setEnabled(false);
            }
        }

    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#saveState(org.eclipse.ui.IMemento)
     */
    public void saveState(org.eclipse.ui.IMemento selection) {
        /*
         * FIX: NullPointerException
         * java.lang.NullPointerException
         * at org.eclipse.ui.texteditor.AbstractTextEditor.saveState(AbstractTextEditor.java:6939)
         * at com.drgarbage.sourcecodevisualizer.editors.JavaCodeEditor.saveState(JavaCodeEditor.java:854)
         * at org.eclipse.ui.internal.EditorManager$10.run(EditorManager.java:1602) 
         **/
        if (selection.getTextData() != null)
            super.saveState(selection);
    }

    /**
     * @return the selectedMethod
     */
    public IMethod getSelectedMethod() {
        return selectedMethod;
    }

    /* (non-Javadoc)
     * @see org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor#getAdapter(java.lang.Class)
     */
    public Object getAdapter(Class required) {

        if (IControlFlowGraphViewPage.class.equals(required)) {
            return createGraphControlFlowViewPage();
        }

        if (IContentOutlinePage.class.equals(required)) {
            return super.getAdapter(required);
        }

        return super.getAdapter(required);
    }

    /**
     * Creates and initialize control flow graph canvas
     */
    private void createAndInitializeControlFlowGraphCanvas() {
        if (getSourceViewer() == null) {
            canvasControlFlowGraph = new SourceCodeGraphCanvas(parent);
            return;
        }

        /* create and initialize control flow graph canvas */
        /* create the control flow graph viewer */
        textWidget = getSourceViewer().getTextWidget();

        /* canvas for the control flow graph viewer */
        canvasControlFlowGraph = new SourceCodeGraphCanvas(parent);
        List<IDirectedGraphExt> graphs = createGraphs();
        lineCount = textWidget.getLineCount();
        canvasControlFlowGraph.init(lineCount, textWidget.getLineHeight(), graphs);

        /* init control flow graph view */
        setControlFlowViewBackGround();

        /* init control flow graph view */
        setControlFlowViewBackGround();

        /* Synchronize scroll bars in the left and right views */
        synchronizeScrollBars(textWidget, canvasControlFlowGraph);

        /* Synchronize line selection in the left and right views */
        synchronizeLineSelection(textWidget, canvasControlFlowGraph);

        /* synchronize current line color */
        synchronizeCurrentLineColor();
    }

    protected ControlFlowGraphViewPage createGraphControlFlowViewPage() {
        if (canvasControlFlowGraph == null || canvasControlFlowGraph.isDisposed()) {
            /* recreate control flow graph viewer */
            createAndInitializeControlFlowGraphCanvas();
        }

        ControlFlowGraphViewPage page = new ControlFlowGraphViewPage(canvasControlFlowGraph);
        return page;
    }

    /**
     * Returns canvas of the control flow garph.
     * @return the canvasControlFlowGraph
     */
    public SourceCodeGraphCanvas getCanvasControlFlowGraph() {
        return canvasControlFlowGraph;
    }

    /**
     * returns flag if the graph has to be shown in the outline view.
     * @return the showGraphInOutline
     */
    public boolean isShowGraphInSeparateView() {
        return showGraphInSeparateView;
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#doRestoreState(org.eclipse.ui.IMemento)
     */
    protected void doRestoreState(IMemento memento) {
        super.doRestoreState(memento);
    }
}