org.eclipse.sirius.business.internal.session.danalysis.SessionResourcesSynchronizer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.sirius.business.internal.session.danalysis.SessionResourcesSynchronizer.java

Source

/*******************************************************************************
 * Copyright (c) 2007, 2015 THALES GLOBAL SERVICES and Obeo.
 * 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.business.internal.session.danalysis;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.transaction.RunnableWithResult;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.workspace.util.WorkspaceSynchronizer;
import org.eclipse.sirius.business.api.query.ResourceQuery;
import org.eclipse.sirius.business.api.session.ReloadingPolicy.Action;
import org.eclipse.sirius.business.api.session.SessionListener;
import org.eclipse.sirius.common.tools.api.resource.ResourceSetSync;
import org.eclipse.sirius.common.tools.api.resource.ResourceSetSync.ResourceStatus;
import org.eclipse.sirius.common.tools.api.resource.ResourceSyncClient;
import org.eclipse.sirius.tools.api.command.semantic.RemoveSemanticResourceCommand;
import org.eclipse.sirius.viewpoint.Messages;
import org.eclipse.sirius.viewpoint.SiriusPlugin;
import org.eclipse.sirius.viewpoint.SyncStatus;

import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;

/**
 * A {@link ResourceSyncClient} that updates a session according to the resource
 * changes it is notified of.
 * 
 * @author pcdavid
 */
public class SessionResourcesSynchronizer implements ResourceSyncClient {
    private final DAnalysisSessionImpl session;

    /**
     * Creates a new synchronizer.
     * 
     * @param session
     *            the session to synchronize.
     */
    public SessionResourcesSynchronizer(DAnalysisSessionImpl session) {
        this.session = Preconditions.checkNotNull(session);
    }

    @Override
    public void statusChanged(final Resource resource, final ResourceStatus oldStatus,
            final ResourceStatus newStatus) {
        // This method is called for each change in turn,
        // while the ResourceSetSync is still gathering/analyzing notifications.
        // Do nothing in this case, and defer all the processing to the
        // statusesChanged method, which is called only once will all the
        // changes already known and aggregated.
    }

    @Override
    public void statusesChanged(Collection<ResourceStatusChange> changes) {
        if (session.isOpen()) {
            Multimap<ResourceStatus, Resource> newStatuses = getImpactingNewStatuses(changes);
            boolean allResourcesSync = allResourcesAreInSync();
            for (ResourceStatus newStatus : newStatuses.keySet()) {
                switch (newStatus) {
                case SYNC:
                    if (allResourcesSync) {
                        session.notifyListeners(SessionListener.SYNC);
                    }
                    break;
                case CHANGED:
                    session.notifyListeners(SessionListener.DIRTY);
                    break;
                case EXTERNAL_CHANGED:
                case CONFLICTING_CHANGED:
                case CONFLICTING_DELETED:
                case DELETED:
                case CHANGES_CANCELED:
                    IProgressMonitor pm = new NullProgressMonitor();
                    for (Resource resource : newStatuses.get(newStatus)) {
                        try {
                            /*
                             * if the project was renamed, deleted or closed we
                             * should not try to reload any thing, this does not
                             * make sense
                             */
                            if (isInProjectDeletedRenamedOrClosed(resource)) {
                                processAction(Action.CLOSE_SESSION, resource, pm);
                                return;
                            }
                            processActions(session.getReloadingPolicy().getActions(session, resource, newStatus),
                                    resource, pm);

                            // CHECKSTYLE:OFF
                        } catch (final Exception e) {
                            // CHECKSTYLE:ON
                            SiriusPlugin.getDefault()
                                    .error(MessageFormat.format(
                                            Messages.SessionResourcesSynchronizer_cantHandleResourceChangeMsg,
                                            resource.getURI()), e);

                        }
                    }
                    // Reload were launched, get global status.
                    allResourcesSync = allResourcesAreInSync();
                    if (allResourcesSync) {
                        session.notifyListeners(SessionListener.SYNC);
                    } else {
                        session.notifyListeners(SessionListener.DIRTY);
                    }
                    break;
                default:
                    break;
                }
            }

            if (allResourcesSync) {
                session.setSynchronizationStatus(SyncStatus.SYNC);
            } else {
                session.setSynchronizationStatus(SyncStatus.DIRTY);
            }
        }
    }

    /**
     * Check if this resource is in an non-existent project or a closed project.
     * 
     * @param resource
     *            the resource to check
     * @return true if this resource is in an non-existent project or a closed
     *         project, false otherwise
     */
    private boolean isInProjectDeletedRenamedOrClosed(Resource resource) {
        IFile file = WorkspaceSynchronizer.getFile(resource);
        if (file != null) {
            IProject project = file.getProject();
            if (project != null) {
                return !project.exists() || !project.isOpen();
            }
        }
        return false;
    }

    private void processActions(List<Action> actions, Resource resource, IProgressMonitor pm) throws Exception {
        for (Action action : actions) {
            processAction(action, resource, pm);
        }
    }

    private void processAction(Action action, final Resource resource, IProgressMonitor pm) throws Exception {
        switch (action) {
        case CLOSE_SESSION:
            session.close(pm);
            break;
        case RELOAD:
            if (session.isOpen()) {
                reloadResource(resource);
            }
            break;
        case REMOVE:
            removeResourceFromSession(resource, pm);
            break;
        default:
            break;
        }
    }

    private void reloadResource(final Resource resource) throws IOException {
        session.notifyListeners(SessionListener.ABOUT_TO_BE_REPLACED);
        TransactionalEditingDomain ted = session.getTransactionalEditingDomain();

        Command reloadingAnalysisCmd = null;
        ResourceQuery resourceQuery = new ResourceQuery(resource);
        boolean representationsResource = resourceQuery.isRepresentationsResource();
        if (representationsResource) {
            reloadingAnalysisCmd = new AnalysisResourceReloadedCommand(session, ted, resource);
        }
        List<Resource> resourcesBeforeReload = Lists.newArrayList(ted.getResourceSet().getResources());
        /* execute the reload operation as a read-only transaction */
        RunnableWithResult<?> reload = new RunnableWithResult.Impl<Object>() {
            @Override
            public void run() {
                session.disableCrossReferencerResolve(resource);
                resource.unload();
                session.enableCrossReferencerResolve(resource);
                try {
                    resource.load(Collections.EMPTY_MAP);
                    EcoreUtil.resolveAll(resource);
                    session.getSemanticCrossReferencer().resolveProxyCrossReferences(resource);
                } catch (IOException e) {
                    setResult(e);
                }
            }
        };
        try {
            ted.runExclusive(reload);
            if (reload.getResult() != null) {
                throw (IOException) reload.getResult();
            } else if (!reload.getStatus().isOK()) {
                SiriusPlugin.getDefault().error(Messages.SessionResourcesSynchronizer_reloadOperationFailErrorMsg,
                        null);
            } else {
                if (representationsResource) {
                    ted.getCommandStack().execute(reloadingAnalysisCmd);
                    if (resource.getURI().equals(session.getSessionResource().getURI())) {
                        session.sessionResourceReloaded(resource);
                    }
                }
                // Analyze the unknown resources to detect new semantic or
                // session resources.
                session.discoverAutomaticallyLoadedResources(resourcesBeforeReload);
                session.notifyListeners(SessionListener.REPLACED);
            }
        } catch (InterruptedException e) {
            // do nothing
        }
    }

    /**
     * Remove a resource from the current session and close the current session
     * if it contains no more analysis resource.
     * 
     * @param resource
     *            The resource to remove
     */
    private void removeResourceFromSession(Resource resource, IProgressMonitor pm) {
        if (session.getAllSemanticResources().contains(resource)) {
            session.getTransactionalEditingDomain().getCommandStack().execute(
                    new RemoveSemanticResourceCommand(session, resource, new NullProgressMonitor(), false));
        } else if (session.getAllSessionResources().contains(resource)) {
            session.removeAnalysis(resource);
        }
        if (session.getResources().contains(resource)) {
            for (final EObject root : Lists.newArrayList(resource.getContents())) {
                EcoreUtil.remove(root);
            }
            session.getResources().remove(resource);
        }
        // delete session only if no more aird file is open
        if (session.getAllSessionResources().isEmpty()) {
            session.close(pm);
        }
    }

    /**
     * Indicates whether all resources (semantic and Danalysises) of this
     * Session are whether {@link ResourceStatus#SYNC} or
     * {@link ResourceStatus#READONLY}.
     * 
     * @return true if all resources (semantic and Danalysises) of this Session
     *         are whether {@link ResourceStatus#SYNC} or
     *         {@link ResourceStatus#READONLY}, false otherwise
     */
    protected boolean allResourcesAreInSync() {
        return checkResourcesAreInSync(getAllSessionResources());
    }

    /**
     * Indicates whether considered resources are whether
     * {@link ResourceStatus#SYNC} or {@link ResourceStatus#READONLY}.
     * 
     * @param resourcesToConsider
     *            the resources to inspect.
     * @return true if all considered are whether {@link ResourceStatus#SYNC} or
     *         {@link ResourceStatus#READONLY}, false otherwise
     */
    protected boolean checkResourcesAreInSync(Iterable<? extends Resource> resourcesToConsider) {
        boolean allResourceAreInSync = true;
        for (Resource resource : resourcesToConsider) {
            ResourceStatus status = ResourceSetSync.getStatus(resource);
            // Test also resource.modified field in case ResourceStatus ==
            // UNKNOWN
            allResourceAreInSync = status == ResourceStatus.SYNC || !resource.isModified();
            if (!allResourceAreInSync) {
                break;
            }
        }
        return allResourceAreInSync;
    }

    private Multimap<ResourceStatus, Resource> getImpactingNewStatuses(Collection<ResourceStatusChange> changes) {
        Multimap<ResourceStatus, Resource> impactingNewStatuses = LinkedHashMultimap.create();
        Multimap<ResourceStatus, Resource> representationResourcesNewStatuses = LinkedHashMultimap.create();
        Iterable<Resource> semanticOrControlledresources = getAllSemanticResources();
        Set<Resource> representationResources = session.getAllSessionResources();
        for (ResourceStatusChange change : changes) {
            if (session.isResourceOfSession(change.getResource(), semanticOrControlledresources)) {
                impactingNewStatuses.put(change.getNewStatus(), change.getResource());
            } else if (session.isResourceOfSession(change.getResource(), representationResources)) {
                representationResourcesNewStatuses.put(change.getNewStatus(), change.getResource());
            }
        }
        // Add session resource impacting status after semantic ones.
        impactingNewStatuses.putAll(representationResourcesNewStatuses);
        return impactingNewStatuses;
    }

    private Iterable<Resource> getAllSessionResources() {
        return Iterables.concat(session.getSemanticResources(), session.getAllSessionResources(),
                session.getControlledResources());
    }

    private Iterable<Resource> getAllSemanticResources() {
        return Iterables.concat(session.getSemanticResources(), session.getControlledResources());
    }

}