org.eclipse.sirius.ui.business.internal.session.EditingSession.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.sirius.ui.business.internal.session.EditingSession.java

Source

/*******************************************************************************
 * Copyright (c) 2007, 2012 THALES GLOBAL SERVICES.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Obeo - initial API and implementation
 *******************************************************************************/
package org.eclipse.sirius.ui.business.internal.session;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.ui.URIEditorInput;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.sirius.business.api.session.Session;
import org.eclipse.sirius.business.api.session.danalysis.DAnalysisSession;
import org.eclipse.sirius.business.internal.session.ReloadingPolicyImpl;
import org.eclipse.sirius.common.ui.tools.api.util.EclipseUIUtil;
import org.eclipse.sirius.common.ui.tools.api.util.SWTUtil;
import org.eclipse.sirius.tools.api.command.ui.RefreshFilter;
import org.eclipse.sirius.tools.api.command.ui.RefreshFilterManager;
import org.eclipse.sirius.ui.business.api.dialect.DialectEditor;
import org.eclipse.sirius.ui.business.api.dialect.DialectUIManager;
import org.eclipse.sirius.ui.business.api.session.EditingSessionEvent;
import org.eclipse.sirius.ui.business.api.session.EditorNameAdapter;
import org.eclipse.sirius.ui.business.api.session.IEditingSession;
import org.eclipse.sirius.ui.business.internal.dialect.editor.DialectEditorCloser;
import org.eclipse.sirius.ui.tools.internal.util.SessionCallBackWithUI;
import org.eclipse.sirius.viewpoint.DRepresentation;
import org.eclipse.sirius.viewpoint.provider.SiriusEditPlugin;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IPathEditorInput;
import org.eclipse.ui.ISaveablePart2;
import org.eclipse.ui.ISaveablesSource;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.Saveable;
import org.eclipse.ui.part.FileEditorInput;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

/**
 * An {@link EditingSession} is responsible of keeping track of the opened
 * editors on a given model and cleaning stuffs when the every editor is closed.
 * 
 * @author cbrun
 */
public class EditingSession implements IEditingSession, ISaveablesSource, RefreshFilter {

    /** The Saveable for the Session. */
    protected Saveable saveable;

    /** Editors that focus a specific diagram file. */
    private final List<DialectEditor> editors = new ArrayList<DialectEditor>();

    private Map<DialectEditor, DialectEditorCloser> dialectEditorClosers = new HashMap<DialectEditor, DialectEditorCloser>();

    private NeedSaveOnCloseDetector needSaveOnCloseDetec = new NeedSaveOnCloseDetector();

    private boolean opened;

    private final Session session;

    /**
     * The adapter used to update name of editors on update of diagram's names
     */
    private EditorNameAdapter editorNameAdapter;

    private RestoreToLastSavePointListener restoreToSavePointListener;

    private SaveSessionWhenNoDialectEditorsListener saveSessionListener;

    /**
     * Create a new {@link EditingSession}.
     * 
     * @param session
     *            the session
     */
    public EditingSession(final Session session) {
        this.session = session;
        this.saveable = new SessionSaveable(session);
        editorNameAdapter = new EditorNameAdapter(this);
        session.setReloadingPolicy(new ReloadingPolicyImpl(new SessionCallBackWithUI()));
    }

    private void initListeners() {
        this.restoreToSavePointListener = new RestoreToLastSavePointListener(session);
        this.saveSessionListener = new SaveSessionWhenNoDialectEditorsListener(session);
        this.saveSessionListener.register();
    }

    private void removeListeners() {
        if (restoreToSavePointListener != null) {
            restoreToSavePointListener.dispose();
            restoreToSavePointListener = null;
        }

        if (this.saveSessionListener != null) {
            this.saveSessionListener.unregister();
            this.saveSessionListener = null;
        }
    }

    @Override
    public Session getSession() {
        return session;
    }

    @Override
    public Collection<DialectEditor> getEditors() {
        return editors;
    }

    @Override
    public void attachEditor(final DialectEditor dialectEditor) {
        if (!editors.contains(dialectEditor) && dialectEditor != null) {
            editors.add(dialectEditor);
            editorNameAdapter.registerEditor(dialectEditor);
            needSaveOnCloseDetec.reInit();

            reorderEditorsIfNeeded(dialectEditor);

            dialectEditorClosers.put(dialectEditor, new DialectEditorCloser(this, dialectEditor));

        }
    }

    private void reorderEditorsIfNeeded(DialectEditor justAddedEditor) {
        List<DialectEditor> reorderedList = Lists.newArrayList();
        IEditorReference[] editorReferences = null;

        IWorkbenchPage page = EclipseUIUtil.getActivePage();
        if (page != null) {
            editorReferences = page.getEditorReferences();
        }

        if (editorReferences != null) {
            for (IEditorReference ref : Lists.newArrayList(editorReferences)) {
                IEditorPart editor2 = ref.getEditor(false);

                IEditorInput refInput = null;
                try {
                    refInput = ref.getEditorInput();
                } catch (PartInitException e) {
                    // Can happen during eclipse launch.
                }
                if (editor2 == null && justAddedEditor.getEditorInput() == refInput) {
                    reorderedList.add(justAddedEditor);
                } else if (editors.contains(editor2)) {
                    reorderedList.add((DialectEditor) editor2);
                }
            }

            if (editors.size() == reorderedList.size() && !Iterables.elementsEqual(editors, reorderedList)) {
                editors.clear();
                editors.addAll(reorderedList);
            }
        }
    }

    @Override
    public void detachEditor(final DialectEditor dialectEditor) {
        editors.remove(dialectEditor);
        editorNameAdapter.unregisterEditor(dialectEditor);
        needSaveOnCloseDetec.reInit();

        DialectEditorCloser dialectEditorCloser = dialectEditorClosers.remove(dialectEditor);
        if (dialectEditorCloser != null) {
            dialectEditorCloser.dispose();
        }
    }

    @Override
    public void detachEditor(final DialectEditor dialectEditor, boolean revertChanges) {

        // We need to compute the closeAllDetected() before to execute the
        // detachEditor since this editor will be removed from the list.
        boolean returnToSyncState = revertChanges && (getEditors().size() == 1 || closeAllDetected());
        detachEditor(dialectEditor);
        if (returnToSyncState) {
            restoreToSavePointListener.returnToSyncState();
        }
    }

    @Override
    public void closeEditors(final boolean save, final Collection<? extends DialectEditor> editorParts) {
        for (final DialectEditor editor : new ArrayList<DialectEditor>(editorParts)) {
            closeEditor(editor, save);
        }
    }

    @Override
    public void closeEditors(final boolean save, final DialectEditor... editorParts) {
        closeEditors(save, Arrays.asList(editorParts));
    }

    private void closeEditor(final DialectEditor editor, final boolean save) {
        if (DialectUIManager.INSTANCE.canHandleEditor(editor)) {
            try {
                detachEditor(editor);
            } catch (IllegalStateException e) {
                // In case of CDO server shutdown
                SiriusEditPlugin.getPlugin().log(e);
            } finally {
                DialectUIManager.INSTANCE.closeEditor(editor, save);
            }
        } else {
            PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().closeEditor(editor, save);
        }
    }

    @Override
    public boolean isEmpty() {
        return editors.size() == 0;
    }

    @Override
    public boolean isLastOpenedEditor() {
        return editors.size() == 1;
    }

    @Override
    public boolean needToBeSavedOnClose(final IEditorPart editor) {
        return needSaveOnCloseDetec.needToBeSavedOnClose(editor);
    }

    @Override
    public int promptToSaveOnClose() {
        int choice = ISaveablePart2.DEFAULT;
        if (saveable != null) {
            boolean stillOpenElsewhere = getEditors().size() > 1 && !closeAllDetected();
            boolean promptStandardDialog = !restoreToSavePointListener.isAllowedToReturnToSyncState();

            choice = SWTUtil.showSaveDialog(session, saveable.getName(), true, stillOpenElsewhere,
                    promptStandardDialog);

            if (choice == ISaveablePart2.CANCEL) {
                needSaveOnCloseDetec.reInit();
            }
        }
        return choice;
    }

    private boolean closeAllDetected() {
        return needSaveOnCloseDetec.closeAllDetected();
    }

    @Override
    public void close() {
        if (opened) {
            close(true);
        }
    }

    @Override
    public void close(final boolean save) {
        if (opened) {
            if (!isEmpty()) {
                closeEditors(save, editors);
            }
            RefreshFilterManager.INSTANCE.removeRefreshFilter(this);
            removeListeners();
            opened = false;
        }
        /*
         * close editors not yet registered with an UI session
         */
        closeOthersEditors(save);
    }

    private void closeOthersEditors(final boolean save) {
        Display.getDefault().asyncExec(new Runnable() {
            @Override
            public void run() {
                final IWorkbenchPage page = EclipseUIUtil.getActivePage();
                if (page != null) {
                    final IEditorReference[] editorReferences = page.getEditorReferences();
                    final List<IEditorReference> editorsToClose = new ArrayList<IEditorReference>();
                    for (final IEditorReference editor : editorReferences) {
                        try {
                            final IEditorInput input = editor.getEditorInput();
                            final IEditorPart editorPart = editor.getEditor(false);
                            if (editorPart == null && input instanceof URIEditorInput) {
                                if (shouldCloseEditor((URIEditorInput) input)) {
                                    editorsToClose.add(editor);
                                }
                            }
                        } catch (final PartInitException e) {
                            // do nothing
                        }
                    }

                    if (!editorsToClose.isEmpty()) {
                        final IEditorReference[] toClose = editorsToClose
                                .toArray(new IEditorReference[editorsToClose.size()]);
                        page.closeEditors(toClose, save);
                    }
                }
            }

            private boolean shouldCloseEditor(URIEditorInput input) {
                if (session instanceof DAnalysisSession) {
                    for (final Resource resource : ((DAnalysisSession) session).getAllSessionResources()) {
                        if (resource.getURI() != null && resource.getURI().equals(input.getURI().trimFragment())) {
                            return true;
                        }
                    }
                }
                return false;
            }
        });
    }

    @Override
    public void open() {
        if (!opened) {
            RefreshFilterManager.INSTANCE.addRefreshFilter(this);
            initListeners();
            opened = true;
        }
    }

    @Override
    public boolean isOpen() {
        return opened;
    }

    @Override
    public boolean isSessionFor(final IEditorInput editorInput) {
        boolean result = false;
        final String key = keyFromInput(editorInput);
        if (key != null) {
            for (final IEditorPart editorPart : this.editors) {
                if (key.equals(keyFromInput(editorPart.getEditorInput()))) {
                    result = true;
                    break;
                }
            }
        }
        return result;
    }

    /**
     * Returns a key with the editor input.
     * 
     * @param input
     *            the input.
     * @return a key with the editor input.
     */
    private String keyFromInput(final IEditorInput input) {
        final String separator = "/"; //$NON-NLS-1$
        String result = null;
        if (input instanceof URIEditorInput) {
            final URI uri = ((URIEditorInput) input).getURI();
            final URI deresolved = uri.deresolve(URI.createURI("platform:/resource/")); //$NON-NLS-1$
            final String workspacePAth = separator + deresolved.path();
            final IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(workspacePAth));
            if (file != null && file.isAccessible() && file.exists()) {
                String filePath = file.getLocation().toString();
                if (filePath.startsWith(separator)) {
                    filePath = filePath.substring(1);
                }
                result = filePath;
            }
            if (result == null) {
                String path = uri.path();
                /*
                 * On windows we get some "/C:" uri ! remove that leading "/"
                 */
                if (path.startsWith(separator)) {
                    path = path.substring(1);
                }
                result = path;
            }
        }
        if (result == null && input instanceof IPathEditorInput) {
            result = ((IPathEditorInput) input).getPath().toString();
        }
        if (result == null && input instanceof FileEditorInput) {
            final java.net.URI uri = ((FileEditorInput) input).getURI();
            String path = uri.getPath();
            /*
             * On windows we get some "/C:" uri ! remove that leading "/"
             */
            if (path.startsWith(separator)) {
                path = path.substring(1);
            }
            result = path;
        }
        if (result == null) {
            result = input.toString();
        }
        return result;
    }

    @Override
    public DialectEditor getEditor(final DRepresentation representation) {
        for (final DialectEditor editorPart : this.editors) {
            if (DialectUIManager.INSTANCE.isRepresentationManagedByEditor(representation, editorPart)) {
                return editorPart;
            }
        }
        return null;
    }

    @Override
    public boolean handleEditor(final IEditorPart editor) {
        return this.editors.contains(editor);
    }

    @Override
    public Collection<DRepresentation> getOpenedRepresantationsToRefresh() {
        Collection<DRepresentation> openedRepresantationsToRefresh = new ArrayList<DRepresentation>();
        for (DialectEditor dialectEditor : getEditors()) {
            DRepresentation dRepresentation = dialectEditor.getRepresentation();
            if (dRepresentation != null) {
                openedRepresantationsToRefresh.add(dRepresentation);
            }
        }
        return openedRepresantationsToRefresh;
    }

    @Override
    public boolean shouldRefresh(DRepresentation representation) {
        return getOpenedRepresantationsToRefresh().contains(representation);
    }

    /**
     * Helper to detect whether the contents of this part should be saved when
     * the given part is closed.
     * 
     * The needToBeSavedOnClose method is called just before closing the
     * editors, re-initialization of the detector on attachment/detachment of an
     * editor with its EditingSession allow to safely detect close all, close
     * other and close workbench events.
     * 
     * @see IEditingSession#needToBeSavedOnClose(IEditorPart)
     * 
     * @author mporhel
     */
    private class NeedSaveOnCloseDetector {

        private Set<IEditorPart> closingEditors = Sets.newHashSet();

        /**
         * 
         * @see IEditingSession#needToBeSavedOnClose(IEditorPart)
         * 
         */
        public boolean needToBeSavedOnClose(IEditorPart editor) {
            boolean needToBeSavedOnClose = isLastOpenedEditor();

            // Closing the workbench case is also handled by the counter
            // || ((PlatformUI.getWorkbench().isClosing()) &&
            // editors.indexOf(editor) == 0);

            if (!needToBeSavedOnClose) {
                IPreferenceStore platformUIPrefStore = PlatformUI.getPreferenceStore();
                boolean closeAll = checkCloseAllEditor(editor);
                needToBeSavedOnClose = platformUIPrefStore
                        .getBoolean(IWorkbenchPreferenceConstants.PROMPT_WHEN_SAVEABLE_STILL_OPEN) || closeAll;
            }

            return needToBeSavedOnClose;
        }

        public boolean closeAllDetected() {
            return closingEditors.size() == editors.size();
        }

        /**
         * Try to detect closing of all editors on a same session from system
         * actions:
         * <ol>
         * <li>close all</li>
         * <li>close other</li>
         * <li>close workbench</li>
         * </ol>
         * 
         * It check that all editor of the given session are receiving the
         * 
         * @param editor
         *            the editor being closed.
         * @return true if "close all" editors of the given session is detected.
         */
        private boolean checkCloseAllEditor(IEditorPart editor) {
            if (editors.indexOf(editor) == 0) {
                reInit();
                closingEditors.add(editor);
            } else if (closingEditors.size() > 0) {
                closingEditors.add(editor);
            }
            return closeAllDetected();
        }

        /**
         * Re-init detector.
         */
        public void reInit() {
            closingEditors.clear();
            closingEditors = Sets.newHashSet();
        }

    }

    @Override
    public Saveable[] getSaveables() {
        return saveable != null ? new Saveable[] { saveable } : new Saveable[0];
    }

    @Override
    public Saveable[] getActiveSaveables() {
        return getSaveables();
    }

    @Override
    public void notify(EditingSessionEvent event) {
        saveSessionListener.notify(event);
    }
}