org.larz.dom4.dm.ui.editor.DmXtextEditor.java Source code

Java tutorial

Introduction

Here is the source code for org.larz.dom4.dm.ui.editor.DmXtextEditor.java

Source

/* This file is part of dom4Editor.
 *
 * dom4Editor 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 3 of the License, or
 * (at your option) any later version.
 *
 * dom4Editor 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 dom4Editor.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.larz.dom4.dm.ui.editor;

import java.io.File;

import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.URIUtil;
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.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
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.Path;
import org.eclipse.emf.common.ui.URIEditorInput;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.source.projection.ProjectionViewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IURIEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.FileStoreEditorInput;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.xtext.ui.editor.XtextEditor;

public class DmXtextEditor extends XtextEditor {
    protected static final String AUTOLINK_PROJECT_NAME = "_LinkedModFiles_"; //$NON-NLS-1$
    protected static final String ENCODING_UTF8 = "utf-8"; //$NON-NLS-1$

    private String path;

    public DmXtextEditor() {
        super();
    }

    @Override
    protected void installFoldingSupport(ProjectionViewer projectionViewer) {
        super.installFoldingSupport(projectionViewer);
        projectionViewer.doOperation(ProjectionViewer.TOGGLE);
    }

    @Override
    public boolean equals(Object obj) {
        if ((obj instanceof XtextEditor) == false)
            return false;
        if (getEditorInput() == null || ((XtextEditor) obj).getEditorInput() == null)
            return false;
        java.net.URI u1 = ((IURIEditorInput) getEditorInput()).getURI();
        java.net.URI u2 = ((IURIEditorInput) ((XtextEditor) obj).getEditorInput()).getURI();
        if (u1 == null || u2 == null)
            return false;
        return u1.toString().equals(u2.toString());
    }

    /**
     * Here it comes the part that handles external files
     * (from outside the workspace)
     *
     */
    private static void createLink(IProject project, IFile linkFile, java.net.URI uri) throws CoreException {
        IPath path = linkFile.getFullPath();

        IPath folders = path.removeLastSegments(1).removeFirstSegments(1);
        IPath checkPath = Path.ROOT;
        int segmentCount = folders.segmentCount();
        for (int i = 0; i < segmentCount; i++) {
            checkPath = checkPath.addTrailingSeparator().append(folders.segment(i));
            IFolder folder = project.getFolder(checkPath);
            if (!folder.exists())
                folder.create(true, true, null);
        }
        linkFile.createLink(uri, IResource.ALLOW_MISSING_LOCAL, null);
    }

    @Override
    public void createPartControl(Composite parent) {
        super.createPartControl(parent);

        MenuManager manager = new MenuManager();
        manager.setRemoveAllWhenShown(true);
        manager.addMenuListener(new IMenuListener() {
            @Override
            public void menuAboutToShow(IMenuManager manager) {
                manager.removeAll();
                manager.add(new Separator(ITextEditorActionConstants.GROUP_UNDO));
                manager.add(new GroupMarker(ITextEditorActionConstants.GROUP_SAVE));
                manager.add(new Separator(ITextEditorActionConstants.GROUP_COPY));
                manager.add(new Separator(ITextEditorActionConstants.GROUP_SETTINGS));

                if (isEditable()) {
                    addAction(manager, ITextEditorActionConstants.GROUP_UNDO, ITextEditorActionConstants.UNDO);
                    addAction(manager, ITextEditorActionConstants.GROUP_UNDO,
                            ITextEditorActionConstants.REVERT_TO_SAVED);
                    addAction(manager, ITextEditorActionConstants.GROUP_SAVE, ITextEditorActionConstants.SAVE);
                    addAction(manager, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.CUT);
                    addAction(manager, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.COPY);
                    addAction(manager, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.PASTE);
                } else {
                    addAction(manager, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.COPY);
                }
                IAction preferencesAction = getAction(ITextEditorActionConstants.CONTEXT_PREFERENCES);
                manager.appendToGroup(ITextEditorActionConstants.GROUP_SETTINGS, preferencesAction);
            }
        });

        StyledText styledText = getSourceViewer().getTextWidget();
        styledText.setMenu(manager.createContextMenu(styledText));
    }

    private IFile getWorkspaceFile(IFileStore fileStore) {
        IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
        IFile[] files = workspaceRoot.findFilesForLocationURI(fileStore.toURI());
        if (files != null && files.length == 1)
            return files[0];
        return null;
    }

    /**
     * Translates an incoming IEditorInput being an FilestoreEditorInput, or IURIEditorInput
     * that is not also a IFileEditorInput.
     * FilestoreEditorInput is used when opening external files in an IDE environment.
     * The result is that the regular XtextEditor gets an IEFSEditorInput which is also an
     * IStorageEditorInput.
     */
    @Override
    public void init(IEditorSite site, IEditorInput input) throws PartInitException {
        // THE ISSUE HERE:
        // In the IDE, the File Open Dialog (and elsewhere) uses a FilestoreEditorInput class
        // which is an IDE specific implementation.
        // The state at this point:
        // 1. When creating a file, the IEditorInput is an IURIEditorInput
        // 2. The only (non IDE specific) interface implemented by FilestoreEditorInput is IURIEditorInput
        // 3. The creation of a file is however also an IFileEditorInput
        //
        // Remedy:
        if (input instanceof IURIEditorInput && !(input instanceof IFileEditorInput)) {
            java.net.URI uri = ((IURIEditorInput) input).getURI();
            path = uri.getPath();
            String name = ((IURIEditorInput) input).getName();
            // Check if this is linkable input
            if (uri.getScheme().equals("file")) { //$NON-NLS-1$
                IFile linkedFile = obtainLink(uri);
                IFileEditorInput linkedInput = new LinkedFileEditorInput(linkedFile);
                super.init(site, linkedInput);
            } else {
                // use EMF URI (readonly) input - will open without validation though...
                // (Could be improved, i.e. perform a download, make readonly, and keep in project,
                // or stored in tmp, and processed as the other linked resources..
                URIEditorInput uriInput = new URIEditorInput(URI.createURI(uri.toString()), name);
                super.init(site, uriInput);
            }
            return;
        } else if (input instanceof IFileEditorInput) {
            path = ((IFileEditorInput) input).getFile().getLocation().toString();
        }
        super.init(site, input);
    }

    public String getPath() {
        return path;
    }

    /**
     * Throws WrappedException - the URI must refer to an existing file!
     *
     * @param uri
     * @return
     */
    public static IFile obtainLink(java.net.URI uri) {
        try {
            IWorkspace ws = ResourcesPlugin.getWorkspace();
            // get, or create project if non existing
            IProject project = ws.getRoot().getProject(AUTOLINK_PROJECT_NAME);
            boolean newProject = false;
            if (!project.exists()) {
                project.create(null);
                newProject = true;
            }
            if (!project.isOpen()) {
                project.open(null);
                //project.setHidden(true);
            }

            if (newProject)
                project.setDefaultCharset(ENCODING_UTF8, new NullProgressMonitor());

            // path in project that is the same as the external file's path
            IFile linkFile = project.getFile(uri.getPath());
            if (linkFile.exists())
                linkFile.refreshLocal(1, null); // don't know if needed (or should be avoided...)
            else {
                // create the link
                createLink(project, linkFile, uri);
                // linkFile.createLink(uri, IResource.ALLOW_MISSING_LOCAL, null);
            }
            return linkFile;

        } catch (CoreException e) {
            throw new WrappedException(e);
        }
    }

    // SaveAs support for linked files - saves them on local disc, not to workspace if file is in special
    // hidden external file link project.
    @Override
    protected void performSaveAs(IProgressMonitor progressMonitor) {

        Shell shell = getSite().getShell();
        final IEditorInput input = getEditorInput();

        // Customize save as if the file is linked, and it is in the special external link project
        //
        if (input instanceof IFileEditorInput && ((IFileEditorInput) input).getFile().isLinked()
                && ((IFileEditorInput) input).getFile().getProject().getName().equals(AUTOLINK_PROJECT_NAME)) {
            final IEditorInput newInput;
            IDocumentProvider provider = getDocumentProvider();

            FileDialog dialog = new FileDialog(shell, SWT.SAVE);
            IPath oldPath = URIUtil.toPath(((IURIEditorInput) input).getURI());
            if (oldPath != null) {
                dialog.setFileName(oldPath.lastSegment());
                dialog.setFilterPath(oldPath.toOSString());
            }

            dialog.setFilterExtensions(new String[] { "*.dm", "*.*" }); //$NON-NLS-1$ //$NON-NLS-2$
            String path = dialog.open();
            if (path == null) {
                if (progressMonitor != null)
                    progressMonitor.setCanceled(true);
                return;
            }

            // Check whether file exists and if so, confirm overwrite
            final File localFile = new File(path);
            if (localFile.exists()) {
                MessageDialog overwriteDialog = new MessageDialog(shell, "File Exists", null,
                        path + " already exists. Do you wish to overwrite? ", MessageDialog.WARNING,
                        new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL }, 1); // 'No' is the default
                if (overwriteDialog.open() != Window.OK) {
                    if (progressMonitor != null) {
                        progressMonitor.setCanceled(true);
                        return;
                    }
                }
            }

            IFileStore fileStore;
            try {
                fileStore = EFS.getStore(localFile.toURI());
            } catch (CoreException ex) {
                MessageDialog.openError(shell, "Error", "Couldn't write file. " + ex.getMessage());
                return;
            }

            IFile file = getWorkspaceFile(fileStore);
            if (file != null)
                newInput = new FileEditorInput(file);
            else {
                IURIEditorInput uriInput = new FileStoreEditorInput(fileStore);
                java.net.URI uri = uriInput.getURI();
                IFile linkedFile = obtainLink(uri);

                newInput = new FileEditorInput(linkedFile);
            }

            if (provider == null) {
                // editor has programmatically been closed while the dialog was open
                return;
            }

            boolean success = false;
            try {

                provider.aboutToChange(newInput);
                provider.saveDocument(progressMonitor, newInput, provider.getDocument(input), true);
                success = true;

            } catch (CoreException x) {
                final IStatus status = x.getStatus();
                if (status == null || status.getSeverity() != IStatus.CANCEL) {
                    MessageDialog.openError(shell, "Error", "Couldn't write file. " + x.getMessage());
                }
            } finally {
                provider.changed(newInput);
                if (success)
                    setInput(newInput);
            }

            if (progressMonitor != null)
                progressMonitor.setCanceled(!success);

            return;
        }

        super.performSaveAs(progressMonitor);
    }
}