de.ovgu.featureide.ui.views.collaboration.outline.CollaborationOutline.java Source code

Java tutorial

Introduction

Here is the source code for de.ovgu.featureide.ui.views.collaboration.outline.CollaborationOutline.java

Source

/* FeatureIDE - An IDE to support feature-oriented software development
 * Copyright (C) 2005-2012  FeatureIDE team, University of Magdeburg
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/.
 *
 * See http://www.fosd.de/featureide/ for further information.
 */
package de.ovgu.featureide.ui.views.collaboration.outline;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.content.IContentDescription;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.ui.texteditor.ITextEditor;

import de.ovgu.featureide.core.CorePlugin;
import de.ovgu.featureide.core.IFeatureProject;
import de.ovgu.featureide.core.fstmodel.FSTField;
import de.ovgu.featureide.core.fstmodel.FSTMethod;
import de.ovgu.featureide.core.fstmodel.preprocessor.FSTDirective;
import de.ovgu.featureide.core.listeners.ICurrentBuildListener;
import de.ovgu.featureide.fm.ui.editors.FeatureModelEditor;
import de.ovgu.featureide.fm.ui.views.outline.FmLabelProvider;
import de.ovgu.featureide.fm.ui.views.outline.FmOutlinePageContextMenu;
import de.ovgu.featureide.fm.ui.views.outline.FmTreeContentProvider;
import de.ovgu.featureide.ui.UIPlugin;
import de.ovgu.featureide.ui.views.collaboration.model.Class;
import de.ovgu.featureide.ui.views.collaboration.model.Role;

/**
 * Another outline view displaying the same information as the collaboration
 * diagram
 * 
 * @author Jan Wedding
 * @author Melanie Pflaume
 */
/*
 * TODO #404 fix bug: do not close the tree if a corresponding file 
 * was opened with an other way e.g. via collaboration diagram
 * 
 * TODO Sometimes the outline has no content -> display a warning / message 
 * 
 */
public class CollaborationOutline extends ViewPart implements ICurrentBuildListener {

    private TreeViewer viewer;
    private IFile iFile;
    private IEditorPart active_editor;
    private UIJob uiJob;
    private CollaborationOutlineTreeContentProvider contentProvider = new CollaborationOutlineTreeContentProvider();
    private CollaborationOutlineLabelProvider clabel = new CollaborationOutlineLabelProvider();
    private FmTreeContentProvider modelContentProvider = new FmTreeContentProvider();
    private FmLabelProvider modelLabelProvider = new FmLabelProvider();
    private FmOutlinePageContextMenu contextMenu;

    private static final ImageDescriptor IMG_COLLAPSE = UIPlugin.getDefault()
            .getImageDescriptor("icons/collapse.gif");
    private static final ImageDescriptor IMG_EXPAND = UIPlugin.getDefault().getImageDescriptor("icons/expand.gif");

    public static final String ID = UIPlugin.PLUGIN_ID + ".views.collaboration.outline.CollaborationOutline";

    private IPartListener editorListener = new IPartListener() {

        public void partOpened(IWorkbenchPart part) {
            if (part instanceof IEditorPart)
                setEditorActions(part);
        }

        public void partDeactivated(IWorkbenchPart part) {

        }

        public void partClosed(IWorkbenchPart part) {

        }

        public void partBroughtToTop(IWorkbenchPart part) {
            if (part instanceof IEditorPart)
                setEditorActions(part);
        }

        public void partActivated(IWorkbenchPart part) {
            if (part instanceof IEditorPart || part instanceof ViewPart)
                setEditorActions(part);
        }

    };

    private IPropertyListener plistener = new IPropertyListener() {
        @Override
        public void propertyChanged(Object source, int propId) {
            update(iFile);
        }

    };

    /**
     * colors the tree in case a treeItem has been expanded (because the
     * children are lazily loaded)
     */
    private ITreeViewerListener treeListener = new ITreeViewerListener() {

        @Override
        public void treeCollapsed(TreeExpansionEvent event) {
        }

        @Override
        public void treeExpanded(TreeExpansionEvent event) {
            colorizeItems(viewer.getTree().getItems());
        }

    };

    /**
     * triggers a scrolling action to the selected field/method in the current
     * editor
     */
    private ISelectionChangedListener selectionChangedListener = new ISelectionChangedListener() {
        @Override
        public void selectionChanged(SelectionChangedEvent event) {
            if (iFile != null) {
                Object selection = ((IStructuredSelection) viewer.getSelection()).getFirstElement();
                if (selection instanceof FSTMethod) {
                    FSTMethod meth = (FSTMethod) selection;
                    if (meth.isOwn(iFile)) {
                        scrollToLine(active_editor, meth.getLineNumber(iFile));
                    }
                    return;
                } else if (selection instanceof FSTField) {
                    FSTField field = (FSTField) selection;
                    if (field.isOwn(iFile)) {
                        scrollToLine(active_editor, field.getLineNumber(iFile));
                    }
                    return;
                } else if (selection instanceof FSTDirective) {
                    FSTDirective directive = (FSTDirective) selection;
                    scrollToLine(active_editor, directive.getStartLine(), directive.getEndLine(),
                            directive.getStartOffset(), directive.getEndLength());
                    return;
                } else if (selection instanceof Role) {

                    Role r = (Role) selection;

                    if (r.file.isAccessible()) {
                        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
                        IWorkbenchPage page = window.getActivePage();
                        IContentType contentType = null;
                        try {
                            IContentDescription description = r.file.getContentDescription();
                            if (description != null) {
                                contentType = description.getContentType();
                            }
                            IEditorDescriptor desc = null;
                            if (contentType != null) {
                                desc = PlatformUI.getWorkbench().getEditorRegistry()
                                        .getDefaultEditor(r.file.getName(), contentType);
                            } else {
                                desc = PlatformUI.getWorkbench().getEditorRegistry()
                                        .getDefaultEditor(r.file.getName());
                            }
                            iFile = r.file;
                            if (desc != null) {
                                page.openEditor(new FileEditorInput(r.file), desc.getId());
                            } else {
                                // case: there is no default editor for the file
                                page.openEditor(new FileEditorInput(r.file), "org.eclipse.ui.DefaultTextEditor");
                            }
                        } catch (CoreException e) {
                            UIPlugin.getDefault().logError(e);
                        }
                    }
                }
            }

        }

    };

    public CollaborationOutline() {
        super();
        CorePlugin.getDefault().addCurrentBuildListener(this);
    }

    /**
     * handles all the editorActions
     * 
     * @param part
     */
    private void setEditorActions(IWorkbenchPart activeEditor) {
        IEditorPart part = null;

        if (activeEditor != null) {
            IWorkbenchPage page = activeEditor.getSite().getPage();
            if (page != null) {
                part = page.getActiveEditor();
                if (part != null) {
                    IEditorInput editorInput = part.getEditorInput();
                    if (editorInput instanceof FileEditorInput) {
                        active_editor = part;
                        active_editor.addPropertyListener(plistener);
                        // case: open editor
                        FileEditorInput inputFile = (FileEditorInput) part.getEditorInput();
                        IFeatureProject featureProject = CorePlugin.getFeatureProject(inputFile.getFile());

                        if (featureProject != null) {
                            Control control = viewer.getControl();
                            if (control != null && !control.isDisposed()) {
                                update(inputFile.getFile());
                            }
                            return;
                        }
                    }
                }
            }
        }
        // remove content from TreeViewer
        update(null);
    }

    @Override
    public void createPartControl(Composite parent) {
        viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
        viewer.getControl().setEnabled(false);
        getSite().getPage().addPartListener(editorListener);

        viewer.setAutoExpandLevel(2);
        addToolbar(getViewSite().getActionBars().getToolBarManager());

        if (getSite().getPage().getActiveEditor() != null) {

            FileEditorInput inputFile = (FileEditorInput) getSite().getPage().getActiveEditor().getEditorInput();

            IFeatureProject featureProject = CorePlugin.getFeatureProject(inputFile.getFile());

            if (featureProject != null)
                update(inputFile.getFile());
        }

        viewer.addTreeListener(treeListener);
        viewer.addSelectionChangedListener(selectionChangedListener);
    }

    @Override
    public void setFocus() {
        viewer.getControl().setFocus();
    }

    /**
     * Sets the new input or disables the viewer in case no editor is open
     * 
     * @param iFile2
     */
    private void update(IFile iFile2) {
        if (viewer != null) {
            Control control = viewer.getControl();
            if (control != null && !control.isDisposed()) {
                if (refreshContent(iFile2)) {
                    return;
                }
                iFile = iFile2;

                if (uiJob == null || uiJob.getState() == Job.NONE) {
                    uiJob = new UIJob("Update Outline View") {
                        public IStatus runInUIThread(IProgressMonitor monitor) {

                            if (viewer != null) {
                                if (viewer.getControl() != null && !viewer.getControl().isDisposed()) {
                                    viewer.getControl().setRedraw(false);

                                    if (iFile != null) {
                                        if ("model.xml".equals(iFile.getName())
                                                && active_editor instanceof FeatureModelEditor) {

                                            viewer.setContentProvider(modelContentProvider);
                                            viewer.setLabelProvider(modelLabelProvider);
                                            viewer.setInput(((FeatureModelEditor) active_editor).getFeatureModel());

                                            //recreate the context menu in case we switched to another model
                                            if (contextMenu == null || contextMenu
                                                    .getFeatureModel() != ((FeatureModelEditor) active_editor)
                                                            .getFeatureModel()) {
                                                if (contextMenu != null) {
                                                    // the listener isn't recreated, if it still exists
                                                    // but we need a new listener for the new model
                                                    viewer.removeDoubleClickListener(contextMenu.dblClickListener);
                                                }
                                                contextMenu = new FmOutlinePageContextMenu(getSite(),
                                                        (FeatureModelEditor) active_editor, viewer,
                                                        ((FeatureModelEditor) active_editor).getFeatureModel());
                                            }

                                        } else {
                                            clabel.setFile(iFile);
                                            viewer.setContentProvider(contentProvider);
                                            viewer.setLabelProvider(clabel);
                                            viewer.setInput(iFile);
                                        }
                                    } else {
                                        // simply remove the content from the outline
                                        //case: no providers set
                                        if (viewer.getContentProvider() == null) {
                                            viewer.setContentProvider(contentProvider);
                                        }
                                        if (!(viewer
                                                .getLabelProvider() instanceof CollaborationOutlineLabelProvider)) {
                                            viewer.setLabelProvider(clabel);
                                        }
                                        viewer.setInput(null);
                                    }
                                    colorizeItems(viewer.getTree().getItems());
                                    viewer.getControl().setEnabled(CollaborationOutline.this.iFile != null);
                                    viewer.refresh();
                                    viewer.getControl().setRedraw(true);
                                }
                            }
                            return Status.OK_STATUS;
                        }
                    };
                    uiJob.setPriority(Job.SHORT);
                    uiJob.schedule();
                }
            }
        }
    }

    /**
     * This method only updates the root and colors  
     * @param iFile2 
     * @return <code>false</code> if the content needs to be replaced
     */
    private boolean refreshContent(IFile iFile2) {
        if (iFile2 != null && iFile != null) {
            /** only set the colors of the tree if the content is the same **/
            TreeItem[] items = viewer.getTree().getItems();
            if (iFile2.getName().equals(iFile.getName()) && items.length > 0) {
                TreeItem item = items[0];
                if (item != null) {
                    if (item.getData() instanceof Class) {
                        if (!hasSameClass((Class) item.getData(), iFile2)) {
                            return false;
                        }
                        iFile = iFile2;
                        String toAppend = " - Composed class";
                        for (Role r : ((Class) item.getData()).getRoles()) {
                            if (r.directives.size() > 0) {
                                toAppend = "";
                                break;
                            }
                            if (r.getRoleFile().equals(iFile)) {
                                toAppend = " - " + r.featureName;
                                break;
                            }
                        }
                        item.setText(((Class) item.getData()).getName() + toAppend);
                        colorizeItems(viewer.getTree().getItems());
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * @return <code>true</code> if the new input does not change the old content.
     */
    private boolean hasSameClass(Class Class, IFile iFile2) {
        if (!iFile2.getProject().equals(iFile.getProject())) {
            return false;
        }
        if (isBuildFile(iFile2.getParent(), CorePlugin.getFeatureProject(iFile2).getBuildFolder())) {
            return true;
        }
        if (isBuildFile(iFile.getParent(), CorePlugin.getFeatureProject(iFile).getBuildFolder())) {
            return true;
        }

        if (iFile2.equals(iFile)) {
            return true;
        }
        boolean i = false;
        boolean j = false;
        for (Role role : Class.getRoles()) {
            if (role.getRoleFile().equals(iFile)) {
                i = true;
            } else if (role.getRoleFile().equals(iFile2)) {
                j = true;
            }
        }
        return j && i;
    }

    /**
     * @param parent
     * @param buildFolder
     * @return <code>true</code> if the build folder contains the given folder
     */
    private boolean isBuildFile(IContainer parent, IFolder buildFolder) {
        if (parent == null) {
            return false;
        }
        if (parent instanceof IFolder) {
            if (parent.equals(buildFolder)) {
                return true;
            }
            return isBuildFile(parent.getParent(), buildFolder);
        }
        return false;
    }

    /**
     * colors the TreeItems gray in case the method/field is not in the current
     * file<br>
     * makes the TreeItems bold in case the Feature inside the TreeItem is in
     * the current file
     * 
     * @param treeItems
     *            the items that should be colored
     */
    private void colorizeItems(TreeItem[] treeItems) {
        for (int i = 0; i < treeItems.length; i++) {
            if (treeItems[i].getItems().length > 0) {
                colorizeItems(treeItems[i].getItems());
            }

            if (treeItems[i].getData() instanceof FSTMethod) {
                if (!((FSTMethod) treeItems[i].getData()).isOwn(iFile)) {
                    treeItems[i].setForeground(viewer.getControl().getDisplay().getSystemColor(SWT.COLOR_GRAY));
                } else {
                    treeItems[i].setForeground(viewer.getControl().getDisplay().getSystemColor(SWT.DEFAULT));
                }
            } else if (treeItems[i].getData() instanceof FSTField) {
                if (!((FSTField) treeItems[i].getData()).isOwn(iFile)) {
                    treeItems[i].setForeground(viewer.getControl().getDisplay().getSystemColor(SWT.COLOR_GRAY));
                } else {
                    treeItems[i].setForeground(viewer.getControl().getDisplay().getSystemColor(SWT.DEFAULT));
                }
            } else if (treeItems[i].getData() instanceof Role) {
                if (((Role) treeItems[i].getData()).file.equals(iFile)) {
                    // get old Font and simply make it bold
                    treeItems[i].setFont(
                            new Font(treeItems[i].getDisplay(), treeItems[i].getFont().getFontData()[0].getName(),
                                    treeItems[i].getFont().getFontData()[0].getHeight(), SWT.BOLD));
                } else {
                    treeItems[i].setFont(
                            new Font(treeItems[i].getDisplay(), treeItems[i].getFont().getFontData()[0].getName(),
                                    treeItems[i].getFont().getFontData()[0].getHeight(), 0));
                }
            }
        }
    }

    /**
     * provides functionality to expand and collapse all items in viewer
     * 
     * @param iToolBarManager
     */
    public void addToolbar(IToolBarManager iToolBarManager) {
        Action collapseAllAction = new Action() {
            public void run() {
                viewer.collapseAll();
                viewer.expandToLevel(2);
                viewer.refresh();
            }
        };
        collapseAllAction.setToolTipText("Collapse All");
        collapseAllAction.setImageDescriptor(IMG_COLLAPSE);

        Action expandAllAction = new Action() {
            public void run() {
                viewer.expandAll();
                // treeExpanded event is not triggered, so we manually have to
                // call this function
                colorizeItems(viewer.getTree().getItems());
            }
        };
        expandAllAction.setToolTipText("Expand All");
        expandAllAction.setImageDescriptor(IMG_EXPAND);

        iToolBarManager.add(collapseAllAction);
        iToolBarManager.add(expandAllAction);
    }

    /**
     * Jumps to a line in the given editor
     * 
     * @param editorPart
     * @param lineNumber
     */
    public static void scrollToLine(IEditorPart editorPart, int lineNumber) {
        if (!(editorPart instanceof ITextEditor) || lineNumber <= 0) {
            return;
        }
        ITextEditor editor = (ITextEditor) editorPart;
        IDocument document = editor.getDocumentProvider().getDocument(editor.getEditorInput());
        if (document != null) {
            IRegion lineInfo = null;
            try {
                lineInfo = document.getLineInformation(lineNumber - 1);
            } catch (BadLocationException e) {
            }
            if (lineInfo != null) {
                editor.selectAndReveal(lineInfo.getOffset(), lineInfo.getLength());
            }
        }
    }

    /**
     * Highlights the whole if-Block for a FSTDirective
     * 
     * @param editorPart
     * @param startLine the first line of a directive
     * @param endLine the last line of a directive
     * @param startOffset characters before the statement starts
     * @param endOffset length of the last line
     */
    public static void scrollToLine(IEditorPart editorPart, int startLine, int endLine, int startOffset,
            int endOffset) {
        if (!(editorPart instanceof ITextEditor) || startLine < 0 || endLine < 0) {
            return;
        }
        ITextEditor editor = (ITextEditor) editorPart;
        IDocument document = editor.getDocumentProvider().getDocument(editor.getEditorInput());
        if (document != null) {
            try {
                int offset = document.getLineOffset(startLine) + startOffset;
                editor.selectAndReveal(offset, document.getLineOffset(endLine) - (offset) + endOffset);
            } catch (BadLocationException e) {
            }
        }
    }

    /* (non-Javadoc)
     * @see de.ovgu.featureide.core.listeners.ICurrentBuildListener#updateGuiAfterBuild(de.ovgu.featureide.core.IFeatureProject)
     */
    @Override
    public void updateGuiAfterBuild(IFeatureProject project, IFile configurationFile) {
        if (iFile != null && project.equals(CorePlugin.getFeatureProject(iFile))) {
            IFile iFile2 = iFile;
            iFile = null;
            update(iFile2);
        }
    }
}