info.limpet.ui.editors.DataManagerEditor.java Source code

Java tutorial

Introduction

Here is the source code for info.limpet.ui.editors.DataManagerEditor.java

Source

/*****************************************************************************
 *  Limpet - the Lightweight InforMation ProcEssing Toolkit
 *  http://limpet.info
 *
 *  (C) 2015-2016, Deep Blue C Technologies Ltd
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the Eclipse Public License v1.0
 *  (http://www.eclipse.org/legal/epl-v10.html)
 *
 *  This library 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.
 *****************************************************************************/
package info.limpet.ui.editors;

import info.limpet.IChangeListener;
import info.limpet.ICommand;
import info.limpet.IContext;
import info.limpet.IOperation;
import info.limpet.IStore;
import info.limpet.IStoreGroup;
import info.limpet.IStoreItem;
import info.limpet.data.operations.AddLayerOperation;
import info.limpet.data.operations.GenerateDummyDataOperation;
import info.limpet.data.operations.admin.OperationsLibrary;
import info.limpet.data.persistence.xml.XStreamHandler;
import info.limpet.data.store.StoreGroup;
import info.limpet.data.store.StoreGroup.StoreChangeListener;
import info.limpet.ui.RCPContext;
import info.limpet.ui.data_provider.data.DataModel;
import info.limpet.ui.editors.dnd.DataManagerDropAdapter;

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

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.DecoratingLabelProvider;
import org.eclipse.jface.viewers.ILabelDecorator;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.SaveAsDialog;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.FileEditorInput;
import org.osgi.framework.Bundle;

public class DataManagerEditor extends EditorPart {

    private IStore _store;
    private TreeViewer viewer;
    private IMenuListener _menuListener;
    private Action refreshView;
    private Action generateData;
    private Action addFolder;
    private boolean _dirty = false;
    private DataModel _model;
    private StoreChangeListener _changeListener = new StoreChangeListener() {

        @Override
        public void changed() {
            // indicate the file is dirty
            _dirty = true;
            firePropertyChange(PROP_DIRTY);

            // and refresh the UI
            viewer.refresh();
        }
    };
    private IContext _context = new RCPContext();

    private IResourceChangeListener resourceChangeListener = new IResourceChangeListener() {

        @Override
        public void resourceChanged(IResourceChangeEvent event) {
            IResourceDelta delta = event.getDelta();
            final int eventType = event.getType();
            if (delta != null) {
                try {
                    delta.accept(new IResourceDeltaVisitor() {

                        @Override
                        public boolean visit(final IResourceDelta delta) throws CoreException {
                            IResource resource = delta.getResource();
                            if (resource instanceof IWorkspaceRoot) {
                                return true;
                            }
                            if (resource instanceof IProject) {
                                IEditorInput input = getEditorInput();
                                if (input instanceof IFileEditorInput) {
                                    IProject project = ((IFileEditorInput) input).getFile().getProject();
                                    final boolean isPreDelete = eventType == IResourceChangeEvent.PRE_DELETE;
                                    final boolean isPreClose = eventType == IResourceChangeEvent.PRE_CLOSE;
                                    if (resource.equals(project) && (isPreDelete || isPreClose)) {
                                        closeEditor();
                                        return false;
                                    }
                                }
                                return true;
                            }
                            if (resource instanceof IFolder) {
                                return true;
                            }
                            if (resource instanceof IFile) {
                                IEditorInput input = getEditorInput();
                                if (input instanceof IFileEditorInput) {
                                    IFile file = ((IFileEditorInput) input).getFile();
                                    if (resource.equals(file) && delta.getKind() == IResourceDelta.REMOVED) {
                                        IPath movedToPath = delta.getMovedToPath();
                                        if (movedToPath != null) {
                                            IResource path = ResourcesPlugin.getWorkspace().getRoot()
                                                    .findMember(movedToPath);
                                            if (path instanceof IFile) {
                                                final FileEditorInput newInput = new FileEditorInput((IFile) path);
                                                Display.getDefault().asyncExec(new Runnable() {

                                                    @Override
                                                    public void run() {
                                                        setInputWithNotify(newInput);
                                                        setPartName(newInput.getName());
                                                    }
                                                });
                                            }
                                        } else {
                                            closeEditor();
                                        }
                                    }
                                    boolean resChanged = delta.getKind() == IResourceDelta.CHANGED;
                                    boolean contentChanged = (delta.getFlags() & IResourceDelta.CONTENT) != 0;
                                    if (resource.equals(file) && (resChanged && contentChanged)) {
                                        reload();
                                    }
                                }
                            }
                            return false;
                        }

                    });
                } catch (CoreException e) {
                    log(e);
                }
            }
        }
    };

    private void reload() {
        if (_store != null) {
            _store.removeChangeListener(_changeListener);
        }
        load(getEditorInput());
        Display.getDefault().asyncExec(new Runnable() {

            @Override
            public void run() {
                viewer.setInput(_store);
                viewer.refresh();
            }
        });

    }

    private void closeEditor() {
        Display.getDefault().asyncExec(new Runnable() {

            @Override
            public void run() {
                getSite().getPage().closeEditor(DataManagerEditor.this, false);
            }
        });
    }

    @Override
    public void init(IEditorSite site, IEditorInput input) throws PartInitException {
        // FIXME we will support FileEditorInput, FileStoreEditorInput and
        // FileRevisionEditorInput
        load(input);
        setSite(site);
        setInput(input);
        setPartName(input.getName());
    }

    private void load(IEditorInput input) {
        if (input instanceof IFileEditorInput) {
            // just check if the document is empty
            IFileEditorInput iF = (IFileEditorInput) input;
            try {
                // ok, the file may be empty, do a quick check
                if (iF.getFile().exists() && iF.getFile().getContents().available() > 1) {
                    _store = new XStreamHandler().load(((IFileEditorInput) input).getFile());

                    // we need to loop down through the data, setting all of the listeners
                    StoreGroup ms = (StoreGroup) _store;

                    // and get hooked up
                    Iterator<IStoreItem> iter = ms.iterator();
                    while (iter.hasNext()) {
                        connectUp(iter.next(), null, ms);
                    }
                } else {
                    // ok, it was empty. generate an empty store
                    _store = new StoreGroup();
                }
            } catch (IOException | CoreException e) {
                log(e);
            }

        }
        _store.addChangeListener(_changeListener);
    }

    /**
     * walk down through the object tree, connecting listeners as appropriate
     * 
     * @param next
     * @param parent
     * @param listener
     */
    private void connectUp(IStoreItem next, IStoreGroup parent, IChangeListener listener) {
        if (next instanceof IStoreGroup) {
            IStoreGroup group = (IStoreGroup) next;
            Iterator<IStoreItem> iter = group.iterator();
            while (iter.hasNext()) {
                connectUp(iter.next(), group, group);
            }
        }

        next.addChangeListener(listener);
    }

    @Override
    public boolean isDirty() {
        return _dirty;
    }

    @Override
    public boolean isSaveAsAllowed() {
        return true;
    }

    @Override
    public void createPartControl(Composite parent) {
        viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
        _model = new DataModel();
        viewer.setContentProvider(_model);
        LabelProvider labelProvider = new LimpetLabelProvider();
        ILabelDecorator decorator = PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator();
        viewer.setLabelProvider(new DecoratingLabelProvider(labelProvider, decorator));
        viewer.setInput(_store);

        getSite().setSelectionProvider(viewer);
        makeActions();
        hookContextMenu();

        IActionBars bars = getEditorSite().getActionBars();
        fillLocalToolBar(bars.getToolBarManager());

        configureDropSupport();
        configureDragSupport();
        ResourcesPlugin.getWorkspace().addResourceChangeListener(resourceChangeListener,
                IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE
                        | IResourceChangeEvent.POST_CHANGE);
    }

    private void configureDragSupport() {
        int ops = DND.DROP_COPY | DND.DROP_MOVE;
        Transfer[] transfers = new Transfer[] { TextTransfer.getInstance(), LocalSelectionTransfer.getTransfer() };
        viewer.addDragSupport(ops, transfers, new LimpetDragListener(viewer));
    }

    /**
     * sort out the drop target
     */
    private void configureDropSupport() {
        final int dropOperation = DND.DROP_COPY | DND.DROP_MOVE;
        final Transfer[] dropTypes = { FileTransfer.getInstance(), TextTransfer.getInstance() };
        viewer.addDropSupport(dropOperation, dropTypes, new DataManagerDropAdapter(viewer, _store));
    }

    protected void fillLocalToolBar(IToolBarManager manager) {
        manager.add(refreshView);
        manager.add(generateData);
        manager.add(addFolder);
    }

    private void makeActions() {

        // our operation wrapper needs to be able to get the selection, help it out
        final ISelectionProvider provider = new ISelectionProvider() {
            public List<IStoreItem> getSelection() {
                return getSuitableObjects();
            }
        };

        addFolder = new OperationWrapper(new AddLayerOperation(), "Add folder",
                PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_PASTE),
                _context, _store, provider);

        generateData = new OperationWrapper(new GenerateDummyDataOperation("Small", 20), "Create dummy data",
                PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_NEW_WIZARD),
                _context, _store, provider);

        // refresh view is purely UI. So, we can implement it here
        refreshView = new Action("Refresh View",
                PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_REDO)) {

            @Override
            public void run() {
                refresh();
            }
        };

    }

    public void showMessage(String message) {
        MessageDialog.openInformation(viewer.getControl().getShell(), "Data Manager Editor", message);
    }

    protected IMenuListener createContextMenuListener() {
        return new IMenuListener() {
            public void menuAboutToShow(IMenuManager menu) {
                setFocus();
                editorContextMenuAboutToShow(menu);
            }
        };
    }

    protected void editorContextMenuAboutToShow(IMenuManager menu) {
        // get any suitable objects from selection
        List<IStoreItem> selection = getSuitableObjects();

        // include some top level items
        showThisList(selection, menu, OperationsLibrary.getTopLevel(), _store, _context, null);

        // now the tree of operations
        menu.add(new Separator());

        // get the list of operations
        HashMap<String, List<IOperation<?>>> ops = OperationsLibrary.getOperations();

        // and the RCP-specific operations
        HashMap<String, List<IOperation<?>>> rcpOps = RCPOperationsLibrary.getOperations();
        ops.putAll(rcpOps);

        // did we find anything?
        Iterator<String> hIter = ops.keySet().iterator();

        while (hIter.hasNext()) {
            // ok, we're in a menu grouping
            String name = (String) hIter.next();

            // create a new menu tier
            MenuManager newM = new MenuManager(name);
            menu.add(newM);

            // now loop through this set of operations
            List<IOperation<?>> values = ops.get(name);

            showThisList(selection, newM, values, _store, _context, null);
        }

        menu.add(new Separator());
        menu.add(refreshView);

    }

    public static void showThisList(List<IStoreItem> selection, IMenuManager newM, List<IOperation<?>> values,
            final IStore theStore, final IContext context, final Runnable listener) {
        Iterator<IOperation<?>> oIter = values.iterator();
        while (oIter.hasNext()) {
            @SuppressWarnings("unchecked")
            final IOperation<IStoreItem> op = (IOperation<IStoreItem>) oIter.next();
            Collection<ICommand<IStoreItem>> matches = op.actionsFor(selection, theStore, context);

            Iterator<ICommand<IStoreItem>> mIter = matches.iterator();
            while (mIter.hasNext()) {
                final ICommand<IStoreItem> thisC = (ICommand<IStoreItem>) mIter.next();
                newM.add(new Action(thisC.getName()) {
                    @Override
                    public void run() {
                        thisC.execute();

                        // do we have a listener?
                        if (listener != null) {
                            listener.run();
                        }
                    }
                });
            }
        }
    }

    private List<IStoreItem> getSuitableObjects() {
        ArrayList<IStoreItem> matches = new ArrayList<IStoreItem>();

        // ok, find the applicable operations
        ISelection sel = viewer.getSelection();
        IStructuredSelection str = (IStructuredSelection) sel;
        Iterator<?> iter = str.iterator();
        while (iter.hasNext()) {
            Object object = (Object) iter.next();
            if (object instanceof IStoreItem) {
                matches.add((IStoreItem) object);
            } else if (object instanceof IAdaptable) {
                IAdaptable ada = (IAdaptable) object;
                Object match = ada.getAdapter(IStoreItem.class);
                if (match != null) {
                    matches.add((IStoreItem) match);
                }
            }
        }

        return matches;
    }

    protected final IMenuListener getContextMenuListener() {
        if (_menuListener == null) {
            _menuListener = createContextMenuListener();
        }
        return _menuListener;
    }

    private void hookContextMenu() {
        String id = "#DataManagerEditor";
        MenuManager menuMgr = new MenuManager(id, id);
        menuMgr.setRemoveAllWhenShown(true);
        menuMgr.addMenuListener(getContextMenuListener());
        Menu menu = menuMgr.createContextMenu(viewer.getControl());
        viewer.getControl().setMenu(menu);
        // We shouldn't register menu because we will contribute menus
        // using separate extension point
        // getSite().registerContextMenu(menuMgr, viewer);
    }

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

    @Override
    public void doSave(IProgressMonitor monitor) {
        final IEditorInput input = getEditorInput();
        // FIXME we will support FileEditorInput, FileStoreEditorInput and
        // FileRevisionEditorInput
        if (input instanceof IFileEditorInput) {
            IFile file = ((IFileEditorInput) input).getFile();
            doSaveAs(file, monitor);
        }
    }

    private void doSaveAs(IFile file, IProgressMonitor monitor) {
        try {
            ResourcesPlugin.getWorkspace().removeResourceChangeListener(resourceChangeListener);
            new XStreamHandler().save(_store, file);
            _dirty = false;
            file.refreshLocal(IResource.DEPTH_INFINITE, monitor);
            firePropertyChange(PROP_DIRTY);
        } catch (CoreException | IOException e) {
            log(e);
        } finally {
            ResourcesPlugin.getWorkspace().addResourceChangeListener(resourceChangeListener,
                    IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE
                            | IResourceChangeEvent.POST_CHANGE);
        }
    }

    private void log(Throwable t) {
        Bundle bundle = Platform.getBundle("info.limpet");
        if (bundle != null) {
            ILog log = Platform.getLog(bundle);
            if (log != null) {
                log.log(new Status(IStatus.WARNING, bundle.getSymbolicName(), t.getMessage(), t));
                return;
            }
        }
        t.printStackTrace();
    }

    @Override
    public void doSaveAs() {
        final SaveAsDialog dialog = new SaveAsDialog(getEditorSite().getShell());
        dialog.setTitle("Save As");
        if (getEditorInput() instanceof IFileEditorInput) {
            IFileEditorInput input = (IFileEditorInput) getEditorInput();
            IFile file = input.getFile();
            dialog.setOriginalFile(file);
        }
        dialog.create();
        dialog.setMessage("Save file to another location.");
        if (dialog.open() == Window.OK) {
            final IPath path = dialog.getResult();
            final IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
            doSaveAs(file, new NullProgressMonitor());
            IFileEditorInput input = new FileEditorInput(file);
            setInput(input);
            setPartName(input.getName());
            viewer.refresh();
        }
    }

    @Override
    public void dispose() {
        super.dispose();
        ResourcesPlugin.getWorkspace().removeResourceChangeListener(resourceChangeListener);
        if (_store != null) {
            _store.removeChangeListener(_changeListener);
        }
    }

    public IStore getStore() {
        return _store;
    }

    public IContext getContext() {
        return _context;
    }

    public void refresh() {
        if (!viewer.getControl().isDisposed()) {
            viewer.refresh();
        }
    }
}