org.eclipse.emf.compare.ide.ui.internal.logical.resolver.SynchronizedResourceSet.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.emf.compare.ide.ui.internal.logical.resolver.SynchronizedResourceSet.java

Source

/*******************************************************************************
 * Copyright (c) 2011, 2015 Obeo and others.
 * 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
 *     Michael Borkowski - public visibility
 *******************************************************************************/
package org.eclipse.emf.compare.ide.ui.internal.logical.resolver;

import com.google.common.collect.Sets;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.log4j.Logger;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.util.AbstractEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.compare.ide.internal.utils.DisposableResourceSet;
import org.eclipse.emf.compare.ide.internal.utils.INamespaceDeclarationListener;
import org.eclipse.emf.compare.ide.internal.utils.IProxyCreationListener;
import org.eclipse.emf.compare.ide.internal.utils.NoNotificationParserPool;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.ContentHandler;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.XMLResource;

/**
 * A thread-safe implementation of a ResourceSet that will prevent loading of resources unless explicitly
 * demanded through {@link #loadResource(URI)}.
 * 
 * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
 */
// Visible for testing
public class SynchronizedResourceSet extends ResourceSetImpl implements DisposableResourceSet {
    /** The logger. */
    private static final Logger LOGGER = Logger.getLogger(SynchronizedResourceSet.class);

    /** Associates URIs with their resources. */
    private final ConcurrentHashMap<URI, Resource> uriCache;

    /**
     * The list of URIs corresponding to namespaces as declared in the xml files ("xmlns:"). These must not go
     * through our usual xml parser as they have to be properly (and completely) loaded. They also need to be
     * kept in this resource set and made available for other resources to find as they might have associated
     * factories to create objects.
     */
    private final Set<URI> namespaceURIs;

    /** Keeps track of the packages manually loaded for some of this resource set's resources. */
    private final Set<Resource> loadedPackages;

    /** Never try and load the same package twice. We'll use this lock to prevent that from happening. */
    private final ReentrantLock packageLoadingLock;

    /**
     * Constructor.
     * 
     * @param proxyListener
     *            The listener to notify of proxy creations.
     */
    public SynchronizedResourceSet(IProxyCreationListener proxyListener) {
        this.uriCache = new ConcurrentHashMap<URI, Resource>();
        this.resources = new SynchronizedResourcesEList<Resource>();
        this.namespaceURIs = Sets.newSetFromMap(new ConcurrentHashMap<URI, Boolean>());
        this.loadedPackages = Sets.newSetFromMap(new ConcurrentHashMap<Resource, Boolean>());
        this.packageLoadingLock = new ReentrantLock(true);
        this.loadOptions = super.getLoadOptions();
        /*
         * This resource set is specifically designed to resolve cross resources links, it thus spends a lot
         * of time loading resources. The following set of options is what seems to give the most significant
         * boost in loading performances, though I did not fine-tune what's really needed here.
         */
        final NoNotificationParserPool parserPool = new NoNotificationParserPool(true);
        parserPool.addProxyListener(proxyListener);
        parserPool.addNamespaceDeclarationListener(new INamespaceDeclarationListener() {
            public void schemaLocationDeclared(String key, URI uri) {
                namespaceURIs.add(uri.trimFragment());
            }
        });
        loadOptions.put(XMLResource.OPTION_USE_PARSER_POOL, parserPool);
        loadOptions.put(XMLResource.OPTION_USE_DEPRECATED_METHODS, Boolean.FALSE);

        /*
         * We don't use XMLResource.OPTION_USE_XML_NAME_TO_FEATURE_MAP whereas it could bring performance
         * improvements because we are loading the resources concurrently and this map could be used (put and
         * get) by several threads. Passing a ConcurrentMap here is not an option either as EMF sometimes
         * needs to put "null" values in there. see https://bugs.eclipse.org/bugs/show_bug.cgi?id=403425 for
         * more details.
         */
        /*
         * Most of the existing options we are not using from here, since none of them seems to have a single
         * effect on loading performance, whether we're looking at time or memory. See also
         * https://www.eclipse.org/forums/index.php/t/929918/
         */
    }

    /**
     * This will load the given URI as an EMF Resource.
     * <p>
     * This is the only entry point within this resource set to load an EMF resource, and it will _only_ load
     * the resource pointed at by <code>uri</code>, ignoring all cross-referenced resources (including
     * containment proxies).
     * </p>
     * 
     * @param uri
     *            The URI to load as a resource.
     * @return The loaded Resource.
     */
    public Resource loadResource(URI uri) {
        /*
         * Don't use super.getResource : we know the resource does not exist yet as there will only be one
         * "load" call for each given URI. The super implementation iterates over loaded resources before
         * doing any actual work. That causes some minimal overhead but, more importantly, it can generate
         * concurrent modification exceptions.
         */
        final URIConverter theURIConverter = getURIConverter();
        final URI normalizedURI = theURIConverter.normalize(uri);

        Resource result = null;
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".loadResource for " + normalizedURI); //$NON-NLS-1$ //$NON-NLS-2$
        }
        result = uriCache.get(normalizedURI);
        if (result == null) {
            result = delegatedGetResource(uri, true);
            if (result != null) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".loadResource - caching " //$NON-NLS-1$ //$NON-NLS-2$
                            + normalizedURI);
                }
                Resource former = uriCache.putIfAbsent(normalizedURI, result);
                if (former != null) {
                    result = former;
                }
            } else if (namespaceURIs.contains(uri)) {
                // This uri points to an EPackage (or profile) that needs to be loaded completely and
                // normally for its factory to be useable.
                result = demandPackageLoad(uri);
            }
        }

        if (result == null) {
            result = demandCreateResource(uri);
            if (getURIConverter() instanceof RevisionedURIConverter) {
                try {
                    if (!((RevisionedURIConverter) getURIConverter()).prefetchStream(uri, getLoadOptions())) {
                        // Don't try and load. This resource doesn't exist on that side
                        return result;
                    }
                } catch (IOException e) {
                    // Let EMF handle this one.
                }
            }
            if (result == null) {
                // copy/pasted from super.getResource
                throw new RuntimeException("Cannot create a resource for '" + uri //$NON-NLS-1$
                        + "'; a registered resource factory is needed"); //$NON-NLS-1$
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".loadResource - No caching for " //$NON-NLS-1$ //$NON-NLS-2$
                        + normalizedURI);
            }
            demandLoadHelper(result);
        }
        return result;
    }

    /**
     * Loads the given URI as an EMF EPackage. We will disable our custom parser pool for this one as it need
     * to be loaded properly for its factory to be functional.
     * 
     * @param uri
     *            The uri to load.
     * @param normalized
     *            the normalized form of this URI.
     * @return The loaded resource.
     */
    private Resource loadPackage(URI uri, URI normalized) {
        final URI trimmed = uri.trimFragment();
        final Resource resource = createResource(trimmed, ContentHandler.UNSPECIFIED_CONTENT_TYPE);
        if (resource == null) {
            return null;
        }

        // cache this asap. there might be recursive calls to "getResource" with this package URI (as can be
        // observed with the UML Ecore profile for example) during the loading itself; in which case we need
        // to return that same "currently loading" instance.
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".getResource - caching package " + uri); //$NON-NLS-1$ //$NON-NLS-2$
        }
        Resource former = uriCache.putIfAbsent(normalized, resource);
        if (former != null) {
            // There was already a resource cached (multi-threading makes it possible)
            return former;
        }

        InputStream stream = null;
        try {
            stream = getURIConverter().createInputStream(trimmed, null);
            resource.load(stream, Collections.emptyMap());
        } catch (IOException e) {
            handleDemandLoadException(resource, e);
        } finally {
            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException e) {
                    // handled by the outer try
                }
            }
        }
        loadedPackages.add(resource);
        return resource;
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.emf.ecore.resource.impl.ResourceSetImpl#handleDemandLoadException(org.eclipse.emf.ecore.resource.Resource,
     *      java.io.IOException)
     */
    @Override
    protected void handleDemandLoadException(Resource resource, IOException exception) {
        try {
            super.handleDemandLoadException(resource, exception);
        } catch (RuntimeException e) {
            // do nothing, continue with loading, the exception has been added to the diagnostics of the
            // resource
        }
    }

    /**
     * Unload the given resource.
     * 
     * @param resource
     *            Resource to unlod
     * @param monitor
     *            Progress monito to use (currently unused)
     */
    public void unload(Resource resource, IProgressMonitor monitor) {
        final URI uri = resource.getURI();
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".unload " + uri); //$NON-NLS-1$ //$NON-NLS-2$
        }
        uriCache.remove(uri);
        getResources().remove(resource);
        resource.eAdapters().clear();
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.emf.ecore.resource.impl.ResourceSetImpl#getResource(org.eclipse.emf.common.util.URI,
     *      boolean)
     */
    @Override
    public Resource getResource(URI uri, boolean loadOnDemand) {
        // Never load resources from here, we only care for the EPackages to prevent the XMLHandler from going
        // into a stackoverflow
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".getResource for " + uri); //$NON-NLS-1$ //$NON-NLS-2$
        }
        final URI normalized = getURIConverter().normalize(uri);
        Resource demanded = uriCache.get(normalized);
        if (demanded == null) {
            final EPackage ePackage = getPackageRegistry().getEPackage(uri.toString());
            if (ePackage != null) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) //$NON-NLS-1$
                            + ".getResource - found in package registry : " + uri); //$NON-NLS-1$
                }
                demanded = ePackage.eResource();
                if (demanded != null) {
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".getResource - caching " //$NON-NLS-1$ //$NON-NLS-2$
                                + uri);
                    }
                    Resource former = uriCache.putIfAbsent(normalized, demanded);
                    if (former != null) {
                        demanded = former;
                    }
                }
            } else if (namespaceURIs.contains(uri)) {
                // This uri points to an EPackage (or profile) that needs to be loaded completely and
                // normally.
                demanded = demandPackageLoad(uri);
            }
        } else if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".getResource - FOUND in cache " + uri); //$NON-NLS-1$ //$NON-NLS-2$
        }
        return demanded;
    }

    /**
     * This will be used to load the uris matching those from {@link #namespaceURIs} normally.
     * 
     * @param uri
     *            The uri of the package to load.
     * @return The loaded package's resource.
     */
    private Resource demandPackageLoad(URI uri) {
        final URI normalized = getURIConverter().normalize(uri);

        packageLoadingLock.lock();
        try {
            Resource demanded = uriCache.get(normalized);
            if (demanded == null) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) //$NON-NLS-1$
                            + ".getResource - loaded package normally : " + uri); //$NON-NLS-1$
                }
                demanded = loadPackage(uri, normalized);
            }
            return demanded;
        } finally {
            packageLoadingLock.unlock();
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.emf.ecore.resource.impl.ResourceSetImpl#createResource(org.eclipse.emf.common.util.URI)
     */
    @Override
    public synchronized Resource createResource(URI uri) {
        return super.createResource(uri);
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.emf.ecore.resource.impl.ResourceSetImpl#createResource(org.eclipse.emf.common.util.URI,
     *      java.lang.String)
     */
    @Override
    public synchronized Resource createResource(URI uri, String contentType) {
        return super.createResource(uri, contentType);
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.emf.ecore.resource.impl.ResourceSetImpl#getResources()
     */
    @Override
    public EList<Resource> getResources() {
        return resources;
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.emf.ecore.resource.impl.ResourceSetImpl#getLoadOptions()
     */
    @Override
    public Map<Object, Object> getLoadOptions() {
        return loadOptions;
    }

    /**
     * {@inheritDoc}
     */
    public void dispose() {
        // unload these completely instead of using #unload(Resource)
        for (Resource resource : loadedPackages) {
            final URI uri = resource.getURI();
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("SRS@" + Integer.toHexString(hashCode()) + ".unload " + uri); //$NON-NLS-1$ //$NON-NLS-2$
            }
            uriCache.remove(uri);
            resource.unload();
            getResources().remove(resource);
        }
    }

    /**
     * A synchronized implementation of {@link ResourcesEList}.
     * <p>
     * Note that this cannot be extracted out of the {@link SynchronizedResourceSet} since the
     * {@link ResourcesEList} type is not visible.
     * </p>
     * 
     * @param <E>
     *            Type of this list's contents.
     * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
     */
    private class SynchronizedResourcesEList<E extends Resource> extends ResourcesEList<E> {
        /** Generated SUID. */
        private static final long serialVersionUID = 7371376112881960414L;

        /** The lock we'll use for synchronization of the resources list. */
        private final Object lock = new Object();

        /**
         * {@inheritDoc}
         * 
         * @see java.util.AbstractCollection#containsAll(java.util.Collection)
         */
        @Override
        public boolean containsAll(Collection<?> c) {
            synchronized (lock) {
                return super.containsAll(c);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.AbstractEList#set(int, java.lang.Object)
         */
        @Override
        public E set(int index, E object) {
            synchronized (lock) {
                return super.set(index, object);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#dispatchNotification(org.eclipse.emf.common.notify.Notification)
         */
        @Override
        protected void dispatchNotification(Notification notification) {
            // do nothing
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.AbstractEList#add(java.lang.Object)
         */
        @Override
        public boolean add(E object) {
            synchronized (lock) {
                return super.add(object);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.AbstractEList#add(int, java.lang.Object)
         */
        @Override
        public void add(int index, E object) {
            synchronized (lock) {
                super.add(index, object);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.AbstractEList#addAll(java.util.Collection)
         */
        @Override
        public boolean addAll(Collection<? extends E> collection) {
            synchronized (lock) {
                return super.addAll(collection);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.AbstractEList#addAll(int, java.util.Collection)
         */
        @Override
        public boolean addAll(int index, Collection<? extends E> collection) {
            synchronized (lock) {
                return super.addAll(index, collection);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.AbstractEList#remove(java.lang.Object)
         */
        @Override
        public boolean remove(Object object) {
            synchronized (lock) {
                return super.remove(object);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.AbstractEList#retainAll(java.util.Collection)
         */
        @Override
        public boolean retainAll(Collection<?> collection) {
            synchronized (lock) {
                return super.retainAll(collection);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.AbstractEList#move(int, java.lang.Object)
         */
        @Override
        public void move(int index, E object) {
            synchronized (lock) {
                super.move(index, object);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.AbstractEList#equals(java.lang.Object)
         */
        @Override
        public boolean equals(Object object) {
            synchronized (lock) {
                return super.equals(object);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.AbstractEList#hashCode()
         */
        @Override
        public int hashCode() {
            synchronized (lock) {
                return super.hashCode();
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.AbstractEList#toString()
         */
        @Override
        public String toString() {
            synchronized (lock) {
                return super.toString();
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.AbstractEList#iterator()
         */
        @Override
        public Iterator<E> iterator() {
            return new SynchronizedEIterator();
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.AbstractEList#listIterator()
         */
        @Override
        public ListIterator<E> listIterator() {
            return new SynchronizedEListIterator();
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.AbstractEList#listIterator(int)
         */
        @Override
        public ListIterator<E> listIterator(int index) {
            synchronized (lock) {
                int curSize = size();
                if (index < 0 || index > curSize) {
                    throw new BasicIndexOutOfBoundsException(index, curSize);
                }
                return new SynchronizedEListIterator(index);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.BasicEList#indexOf(java.lang.Object)
         */
        @Override
        public int indexOf(Object object) {
            synchronized (lock) {
                return super.indexOf(object);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.BasicEList#lastIndexOf(java.lang.Object)
         */
        @Override
        public int lastIndexOf(Object object) {
            synchronized (lock) {
                return super.lastIndexOf(object);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.BasicEList#toArray()
         */
        @Override
        public Object[] toArray() {
            synchronized (lock) {
                return super.toArray();
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.BasicEList#toArray(T[])
         */
        @Override
        public <T> T[] toArray(T[] array) {
            synchronized (lock) {
                return super.toArray(array);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.BasicEList#setData(int, java.lang.Object[])
         */
        @Override
        public void setData(int size, Object[] data) {
            synchronized (lock) {
                super.setData(size, data);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.BasicEList#get(int)
         */
        @Override
        public E get(int index) {
            synchronized (lock) {
                return super.get(index);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.BasicEList#basicGet(int)
         */
        @Override
        public E basicGet(int index) {
            synchronized (lock) {
                return super.basicGet(index);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.BasicEList#shrink()
         */
        @Override
        public void shrink() {
            synchronized (lock) {
                super.shrink();
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.BasicEList#grow(int)
         */
        @Override
        public void grow(int minimumCapacity) {
            synchronized (lock) {
                super.grow(minimumCapacity);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.BasicEList#clone()
         */
        // CHECKSTYLE:OFF we're overriding...
        @Override
        public Object clone() {
            // CHECKSTYLE:ON
            synchronized (lock) {
                return super.clone();
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#addUnique(java.lang.Object)
         */
        @Override
        public void addUnique(E object) {
            synchronized (lock) {
                super.addUnique(object);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#addUnique(int, java.lang.Object)
         */
        @Override
        public void addUnique(int index, E object) {
            synchronized (lock) {
                super.addUnique(index, object);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#addAllUnique(java.util.Collection)
         */
        @Override
        public boolean addAllUnique(Collection<? extends E> collection) {
            synchronized (lock) {
                return super.addAllUnique(collection);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#addAllUnique(int, java.util.Collection)
         */
        @Override
        public boolean addAllUnique(int index, Collection<? extends E> collection) {
            synchronized (lock) {
                return super.addAllUnique(index, collection);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#addAllUnique(java.lang.Object[], int,
         *      int)
         */
        @Override
        public boolean addAllUnique(Object[] objects, int start, int end) {
            synchronized (lock) {
                return super.addAllUnique(objects, start, end);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#addAllUnique(int, java.lang.Object[],
         *      int, int)
         */
        @Override
        public boolean addAllUnique(int index, Object[] objects, int start, int end) {
            synchronized (lock) {
                return super.addAllUnique(index, objects, start, end);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#basicAdd(java.lang.Object,
         *      org.eclipse.emf.common.notify.NotificationChain)
         */
        @Override
        public NotificationChain basicAdd(E object, NotificationChain notifications) {
            synchronized (lock) {
                return super.basicAdd(object, notifications);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#remove(int)
         */
        @Override
        public E remove(int index) {
            synchronized (lock) {
                return super.remove(index);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#removeAll(java.util.Collection)
         */
        @Override
        public boolean removeAll(Collection<?> collection) {
            synchronized (lock) {
                return super.removeAll(collection);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#basicRemove(java.lang.Object,
         *      org.eclipse.emf.common.notify.NotificationChain)
         */
        @Override
        public NotificationChain basicRemove(Object object, NotificationChain notifications) {
            synchronized (lock) {
                return super.basicRemove(object, notifications);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#clear()
         */
        @Override
        public void clear() {
            synchronized (lock) {
                super.clear();
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#setUnique(int, java.lang.Object)
         */
        @Override
        public E setUnique(int index, E object) {
            synchronized (lock) {
                return super.setUnique(index, object);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#basicSet(int, java.lang.Object,
         *      org.eclipse.emf.common.notify.NotificationChain)
         */
        @Override
        public NotificationChain basicSet(int index, E object, NotificationChain notifications) {
            synchronized (lock) {
                return super.basicSet(index, object, notifications);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#move(int, int)
         */
        @Override
        public E move(int targetIndex, int sourceIndex) {
            synchronized (lock) {
                return super.move(targetIndex, sourceIndex);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.ecore.util.NotifyingInternalEListImpl#basicList()
         */
        @Override
        public List<E> basicList() {
            synchronized (lock) {
                return super.basicList();
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.ecore.util.NotifyingInternalEListImpl#basicIterator()
         */
        @Override
        public Iterator<E> basicIterator() {
            return new SynchronizedNonResolvingEIterator();
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.ecore.util.NotifyingInternalEListImpl#basicListIterator()
         */
        @Override
        public ListIterator<E> basicListIterator() {
            return new SynchronizedNonResolvingEListIterator();
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.ecore.util.NotifyingInternalEListImpl#basicListIterator(int)
         */
        @Override
        public ListIterator<E> basicListIterator(int index) {
            synchronized (lock) {
                int curSize = size();
                if (index < 0 || index > curSize) {
                    throw new BasicIndexOutOfBoundsException(index, curSize);
                }
                return new SynchronizedNonResolvingEListIterator(index);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.ResourcesEList#contains(java.lang.Object)
         */
        @Override
        public boolean contains(Object object) {
            synchronized (lock) {
                return super.contains(object);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.ecore.util.NotifyingInternalEListImpl#basicContains(java.lang.Object)
         */
        @Override
        public boolean basicContains(Object object) {
            synchronized (lock) {
                return super.basicContains(object);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.ecore.util.NotifyingInternalEListImpl#basicContainsAll(java.util.Collection)
         */
        @Override
        public boolean basicContainsAll(Collection<?> collection) {
            synchronized (lock) {
                return super.basicContainsAll(collection);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.ecore.util.NotifyingInternalEListImpl#basicIndexOf(java.lang.Object)
         */

        @Override
        public int basicIndexOf(Object object) {
            synchronized (lock) {
                return super.basicIndexOf(object);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.ecore.util.NotifyingInternalEListImpl#basicLastIndexOf(java.lang.Object)
         */
        @Override
        public int basicLastIndexOf(Object object) {
            synchronized (lock) {
                return super.basicLastIndexOf(object);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.ecore.util.NotifyingInternalEListImpl#basicToArray()
         */
        @Override
        public Object[] basicToArray() {
            synchronized (lock) {
                return super.basicToArray();
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.ecore.util.NotifyingInternalEListImpl#basicToArray(T[])
         */
        @Override
        public <T> T[] basicToArray(T[] array) {
            synchronized (lock) {
                return super.basicToArray(array);
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.BasicEList#data()
         */
        @Override
        public Object[] data() {
            synchronized (lock) {
                return super.data();
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.notify.impl.NotifyingListImpl#getFeature()
         */
        @Override
        public Object getFeature() {
            synchronized (lock) {
                return super.getFeature();
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.ResourcesEList#getFeatureID()
         */
        @Override
        public int getFeatureID() {
            synchronized (lock) {
                return super.getFeatureID();
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.ResourcesEList#getNotifier()
         */
        @Override
        public Object getNotifier() {
            synchronized (lock) {
                return super.getNotifier();
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.BasicEList#isEmpty()
         */
        @Override
        public boolean isEmpty() {
            synchronized (lock) {
                return super.isEmpty();
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.BasicEList#size()
         */
        @Override
        public int size() {
            synchronized (lock) {
                return super.size();
            }
        }

        /**
         * {@inheritDoc}
         * 
         * @see java.util.AbstractList#subList(int, int)
         */
        @Override
        public List<E> subList(int fromIndex, int toIndex) {
            synchronized (lock) {
                return super.subList(fromIndex, toIndex);
            }
        }

        /**
         * A synchronized implementation of the {@link AbstractEList.EIterator}.
         * 
         * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
         */
        private class SynchronizedEIterator extends AbstractEList<E>.EIterator<E> {
            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EIterator#hasNext()
             */
            @Override
            public boolean hasNext() {
                synchronized (lock) {
                    return super.hasNext();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EIterator#next()
             */
            @Override
            public E next() {
                synchronized (lock) {
                    return super.next();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EIterator#remove()
             */
            @Override
            public void remove() {
                synchronized (lock) {
                    super.remove();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see java.lang.Object#equals(java.lang.Object)
             */
            @Override
            public boolean equals(Object obj) {
                synchronized (lock) {
                    return super.equals(obj);
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see java.lang.Object#hashCode()
             */
            @Override
            public int hashCode() {
                synchronized (lock) {
                    return super.hashCode();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see java.lang.Object#toString()
             */
            @Override
            public String toString() {
                synchronized (lock) {
                    return super.toString();
                }
            }
        }

        /**
         * A synchronized implementation of the {@link AbstractEList.EListIterator}.
         * 
         * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
         */
        private class SynchronizedEListIterator extends AbstractEList<E>.EListIterator<E> {
            /**
             * Delegates to the super constructor.
             */
            public SynchronizedEListIterator() {
                super();
            }

            /**
             * Delegates to the super constructor.
             * 
             * @param index
             *            Index at which this list iterator should start.
             */
            public SynchronizedEListIterator(int index) {
                super(index);
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EListIterator#add(java.lang.Object)
             */
            @Override
            public void add(E object) {
                synchronized (lock) {
                    super.add(object);
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EIterator#hasNext()
             */
            @Override
            public boolean hasNext() {
                synchronized (lock) {
                    return super.hasNext();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EListIterator#hasPrevious()
             */
            @Override
            public boolean hasPrevious() {
                synchronized (lock) {
                    return super.hasPrevious();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EIterator#next()
             */
            @Override
            public E next() {
                synchronized (lock) {
                    return super.next();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EListIterator#previous()
             */
            @Override
            public E previous() {
                synchronized (lock) {
                    return super.previous();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EListIterator#previousIndex()
             */
            @Override
            public int previousIndex() {
                synchronized (lock) {
                    return super.previousIndex();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EIterator#remove()
             */
            @Override
            public void remove() {
                synchronized (lock) {
                    super.remove();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EListIterator#set(java.lang.Object)
             */
            @Override
            public void set(E object) {
                synchronized (lock) {
                    super.set(object);
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see java.lang.Object#equals(java.lang.Object)
             */
            @Override
            public boolean equals(Object obj) {
                synchronized (lock) {
                    return super.equals(obj);
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see java.lang.Object#hashCode()
             */
            @Override
            public int hashCode() {
                synchronized (lock) {
                    return super.hashCode();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see java.lang.Object#toString()
             */
            @Override
            public String toString() {
                synchronized (lock) {
                    return super.toString();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EListIterator#nextIndex()
             */
            @Override
            public int nextIndex() {
                synchronized (lock) {
                    return super.nextIndex();
                }
            }
        }

        /**
         * A synchronized implementation of the {@link AbstractEList.NonResolvingEIterator}.
         * 
         * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
         */
        private class SynchronizedNonResolvingEIterator extends AbstractEList<E>.NonResolvingEIterator<E> {
            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EIterator#hasNext()
             */
            @Override
            public boolean hasNext() {
                synchronized (lock) {
                    return super.hasNext();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EIterator#next()
             */
            @Override
            public E next() {
                synchronized (lock) {
                    return super.next();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.NonResolvingEIterator#remove()
             */
            @Override
            public void remove() {
                synchronized (lock) {
                    super.remove();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see java.lang.Object#equals(java.lang.Object)
             */
            @Override
            public boolean equals(Object obj) {
                synchronized (lock) {
                    return super.equals(obj);
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see java.lang.Object#hashCode()
             */
            @Override
            public int hashCode() {
                synchronized (lock) {
                    return super.hashCode();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see java.lang.Object#toString()
             */
            @Override
            public String toString() {
                synchronized (lock) {
                    return super.toString();
                }
            }
        }

        /**
         * A synchronized implementation of the {@link AbstractEList.NonResolvingEListIterator}.
         * 
         * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
         */
        private class SynchronizedNonResolvingEListIterator extends AbstractEList<E>.NonResolvingEListIterator<E> {
            /**
             * Delegates to the super constructor.
             */
            public SynchronizedNonResolvingEListIterator() {
                super();
            }

            /**
             * Delegates to the super constructor.
             * 
             * @param index
             *            Index at which the iteration should start.
             */
            public SynchronizedNonResolvingEListIterator(int index) {
                super(index);
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.NonResolvingEListIterator#add(java.lang.Object)
             */
            @Override
            public void add(E object) {
                synchronized (lock) {
                    super.add(object);
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EIterator#hasNext()
             */
            @Override
            public boolean hasNext() {
                synchronized (lock) {
                    return super.hasNext();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EListIterator#hasPrevious()
             */
            @Override
            public boolean hasPrevious() {
                synchronized (lock) {
                    return super.hasPrevious();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EIterator#next()
             */
            @Override
            public E next() {
                synchronized (lock) {
                    return super.next();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EListIterator#previous()
             */
            @Override
            public E previous() {
                synchronized (lock) {
                    return super.previous();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EListIterator#previousIndex()
             */
            @Override
            public int previousIndex() {
                synchronized (lock) {
                    return super.previousIndex();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.NonResolvingEListIterator#remove()
             */
            @Override
            public void remove() {
                synchronized (lock) {
                    super.remove();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.NonResolvingEListIterator#set(java.lang.Object)
             */
            @Override
            public void set(E object) {
                synchronized (lock) {
                    super.set(object);
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see java.lang.Object#equals(java.lang.Object)
             */
            @Override
            public boolean equals(Object obj) {
                synchronized (lock) {
                    return super.equals(obj);
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see java.lang.Object#hashCode()
             */
            @Override
            public int hashCode() {
                synchronized (lock) {
                    return super.hashCode();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see java.lang.Object#toString()
             */
            @Override
            public String toString() {
                synchronized (lock) {
                    return super.toString();
                }
            }

            /**
             * {@inheritDoc}
             * 
             * @see org.eclipse.emf.common.util.AbstractEList.EListIterator#nextIndex()
             */
            @Override
            public int nextIndex() {
                synchronized (lock) {
                    return super.nextIndex();
                }
            }
        }
    }
}