org.eclipse.sirius.business.internal.movida.DynamicVSMLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.sirius.business.internal.movida.DynamicVSMLoader.java

Source

/*******************************************************************************
 * Copyright (c) 2011, 2015 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.business.internal.movida;

import java.text.MessageFormat;
import java.util.Set;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.sirius.business.internal.movida.registry.ViewpointRegistry;
import org.eclipse.sirius.business.internal.movida.registry.ViewpointRegistryListener;
import org.eclipse.sirius.ext.base.Option;

import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;

/**
 * A VSM Reloader is responsible for keeping the VSM resources loaded in a
 * resource set in sync with the logical Viewpoints actually needed in the
 * resource set's context. It makes sure that all the VSMs required by the
 * context, and only them, are loaded and properly resolved.
 * 
 * @author pierre-charles.david@obeo.fr
 */
public class DynamicVSMLoader implements ViewpointRegistryListener {
    /**
     * The registry to use for information about which logical viewpoints are
     * available, which concrete resources they map to, and when and how they
     * change.
     */
    private final ViewpointRegistry registry;

    /**
     * The resource set to manage.
     */
    private final ResourceSet resourceSet;

    /**
     * The logical URIs of all the Viewpoints which are explicitly required.
     */
    private final Set<URI> requiredViewpoints = Sets.newHashSet();

    /**
     * The physical URIs of all the VSMs which provide the required Viewpoints.
     */
    private final Set<URI> requiredProviders = Sets.newHashSet();

    private final Set<URI> protectedResources = Sets.newHashSet();

    private Runnable errorHandler;

    /**
     * Constructor.
     * 
     * @param resourceSet
     *            the resource set to manage.
     * @param registry
     *            the registry to use.
     */
    public DynamicVSMLoader(ResourceSet resourceSet, ViewpointRegistry registry) {
        this.resourceSet = Preconditions.checkNotNull(resourceSet);
        this.registry = Preconditions.checkNotNull(registry);
        this.registry.addListener(this);
    }

    /**
     * Returns the registry used by this loader.
     * 
     * @return the registry used by this loader.
     */
    public ViewpointRegistry getRegistry() {
        return registry;
    }

    /**
     * Returns the logical URIs of all the Viewpoints required.
     * 
     * @return the logical URIs of all the Viewpoints required.
     */
    public Set<URI> getRequiredViewpoints() {
        return requiredViewpoints;
    }

    public synchronized void setErrorHandler(Runnable errorHandler) {
        this.errorHandler = errorHandler;
    }

    /**
     * Mark the resource identified by the specified URI as protected: the
     * loader will not attempt to load, reload or unload it.
     * 
     * @param uri
     *            the physical URI of the resource to consider as protected.
     */
    public synchronized void protect(URI uri) {
        this.protectedResources.add(Preconditions.checkNotNull(uri));
    }

    /**
     * Mark the resource identified by the specified URI as not protected.
     * 
     * @param uri
     *            the physical URI of the resource to consider as not protected.
     */
    public synchronized void unprotect(URI uri) {
        this.protectedResources.remove(Preconditions.checkNotNull(uri));
    }

    /**
     * Tell this loader that the viewpoint specified by the supplied logical URI
     * should always be loaded in the resource set, along with its dependencies.
     * 
     * @param viewpoint
     *            the logical Sirius URI of the Sirius which is required.
     */
    public synchronized void require(URI viewpoint) {
        Preconditions.checkNotNull(viewpoint);
        Option<URI> provider = registry.getProvider(viewpoint);
        Preconditions.checkState(provider.some(),
                MessageFormat.format("The specified viewpoint {0} is not available.", viewpoint.toString())); //$NON-NLS-1$
        boolean added = this.requiredViewpoints.add(viewpoint);
        if (added) {
            refresh();
        }
    }

    /**
     * Tell this loader that the viewpoint specified by the supplied logical URI
     * is no longer explicitly required. The underlying resource may be unloaded
     * from the resource set if the loader detects that it is no longer
     * required, even indirectly. Does nothing if the specified viewpoint was
     * not an explicit requirement.
     * 
     * @param viewpoint
     *            the logical Sirius URI of the Sirius which is no longer
     *            directly required.
     */
    public synchronized void unrequire(URI viewpoint) {
        boolean removed = this.requiredViewpoints.remove(Preconditions.checkNotNull(viewpoint));
        if (removed) {
            refresh();
        }
    }

    /**
     * Stop managing the resource set and release all resources.
     */
    public void dispose() {
        this.registry.removeListener(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized void registryChanged(ViewpointRegistry reg, Set<URI> removed, Set<URI> added,
            Set<URI> changed) {
        assert reg == this.registry;
        SetView<URI> absentRequirements = Sets.intersection(requiredViewpoints, removed);
        if (!absentRequirements.isEmpty()) {
            if (errorHandler != null) {
                errorHandler.run();
            } else {
                throw new IllegalStateException("The required viewpoints " //$NON-NLS-1$
                        + Joiner.on(", ").join(absentRequirements) + " are not available anymore."); //$NON-NLS-1$//$NON-NLS-2$
            }
        }
        refresh();
    }

    private void refresh() {
        Set<URI> providers = computeRequiredProviders();
        Set<Resource> maybeUnneeded = getAndUnloadPotentialUnneededProviders(providers);
        loadAndResolve(providers);
        removeUnneededResources(maybeUnneeded);
        setRequiredProviders(providers);
    }

    private Set<URI> computeRequiredProviders() {
        Set<URI> newProviders = Sets.newHashSet();
        for (URI uri : requiredViewpoints) {
            Option<URI> provider = registry.getProvider(uri);
            assert provider.some();
            newProviders.add(provider.get());
        }
        return newProviders;
    }

    private Set<Resource> getAndUnloadPotentialUnneededProviders(Set<URI> required) {
        Set<Resource> maybeUnneeded = Sets.newHashSet();
        for (Resource res : resourceSet.getResources()) {
            if (!required.contains(res.getURI()) && res.getURI().isPlatform()
                    && !protectedResources.contains(res.getURI())) {
                maybeUnneeded.add(res);
                res.unload();
            }
        }
        return maybeUnneeded;
    }

    private void loadAndResolve(Set<URI> providers) {
        for (URI uri : providers) {
            if (!requiredProviders.contains(uri)) {
                Resource res = resourceSet.getResource(uri, true);
                EcoreUtil.resolveAll(res);
            }
        }
    }

    /**
     * Resources identified as potentially unneeded can be completely removed of
     * the resolution step has not loaded them.
     */
    private void removeUnneededResources(Set<Resource> maybeUnneeded) {
        for (Resource resource : maybeUnneeded) {
            if (!resource.isLoaded()) {
                resourceSet.getResources().remove(resource);
            }
        }
    }

    private void setRequiredProviders(Set<URI> newProviders) {
        requiredProviders.clear();
        requiredProviders.addAll(newProviders);
    }
}