Skip to content

Content of file DefaultProvider.java

/**
 * Copyright (c) 2011 Eike Stepper (Berlin, Germany) and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 * Eike Stepper - initial API and implementation
 */
package org.eclipse.emf.ecp.spi.core;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.command.BasicCommandStack;
import org.eclipse.emf.common.command.CommandStack;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecp.core.ECPProject;
import org.eclipse.emf.ecp.core.ECPRepository;
import org.eclipse.emf.ecp.core.util.ECPContainer;
import org.eclipse.emf.ecp.core.util.ECPModelContextAdapter;
import org.eclipse.emf.ecp.core.util.ECPModelContextProvider;
import org.eclipse.emf.ecp.core.util.ECPUtil;
import org.eclipse.emf.ecp.internal.core.Activator;
import org.eclipse.emf.ecp.internal.core.util.Disposable;
import org.eclipse.emf.ecp.internal.core.util.Element;
import org.eclipse.emf.ecp.spi.core.util.AdapterProvider;
import org.eclipse.emf.ecp.spi.core.util.InternalChildrenList;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.ITreeItemContentProvider;
import org.eclipse.emf.edit.provider.ItemPropertyDescriptor;

/**
 * @author Eike Stepper
 * @since 1.1
 */
public abstract class DefaultProvider extends Element implements InternalProvider {
	private final Disposable disposable = new Disposable(this) {
		@Override
		protected void doDispose() {
			uiProvider = null;

			for (final ECPRepository repository : ECPUtil.getECPRepositoryManager().getRepositories()) {
				if (repository.getProvider().getName().equals(getName())) {
					handleLifecycle(repository, LifecycleEvent.DISPOSE);
				}
			}

			for (final ECPProject project : ECPUtil.getECPProjectManager().getProjects()) {
				if (project.getProvider().getName().equals(getName())) {
					handleLifecycle(project, LifecycleEvent.DISPOSE);
				}
			}

			DefaultProvider.this.doDispose();
		}
	};

	private String label;

	private String description;

	private AdapterProvider uiProvider;

	/**
	 * Convenient constructor for an {@link org.eclipse.emf.ecp.core.ECPProvider ECPProvider}.
	 *
	 * @param name the name of the implementing provider
	 */
	protected DefaultProvider(String name) {
		super(name);
		label = name;
		description = ""; //$NON-NLS-1$
	}

	/** {@inheritDoc} */
	@Override
	public final String getType() {
		return TYPE;
	}

	/** {@inheritDoc} */
	@Override
	public final InternalProvider getProvider() {
		return this;
	}

	/** {@inheritDoc} */
	@Override
	public final String getLabel() {
		return label;
	}

	/** {@inheritDoc} */
	@Override
	public final void setLabel(String label) {
		this.label = label;
	}

	/** {@inheritDoc} */
	@Override
	public final String getDescription() {
		return description;
	}

	/** {@inheritDoc} */
	@Override
	public final void setDescription(String description) {
		this.description = description;
	}

	/** {@inheritDoc} */
	@Override
	public final AdapterProvider getUIProvider() {
		return uiProvider;
	}

	/** {@inheritDoc} */
	@Override
	public final void setUIProvider(AdapterProvider uiProvider) {
		this.uiProvider = uiProvider;
	}

	/** {@inheritDoc} */
	@Override
	public final Set<InternalProject> getOpenProjects() {
		final Set<InternalProject> result = new LinkedHashSet<InternalProject>();
		for (final ECPProject project : ECPUtil.getECPProjectManager().getProjects()) {
			if (project.isOpen()) {
				if (project.getProvider().equals(this)) {
					result.add((InternalProject) project);
				}

			}
		}

		// TODO Consider to cache the result
		return result;
	}

	/** {@inheritDoc} */
	@Override
	public final boolean isDisposed() {
		return disposable.isDisposed();
	}

	/** {@inheritDoc} */
	@Override
	public final void dispose() {
		disposable.dispose();
	}

	/** {@inheritDoc} */
	@Override
	public final void addDisposeListener(DisposeListener listener) {
		disposable.addDisposeListener(listener);
	}

	/** {@inheritDoc} */
	@Override
	public final void removeDisposeListener(DisposeListener listener) {
		disposable.removeDisposeListener(listener);
	}

	/**
	 * This method is called when a provider is disposed. Subclasses which need to dispose should overwrite this.
	 */
	protected void doDispose() {
	}

	/** {@inheritDoc} */
	@Override
	public <T> T getAdapter(Object adaptable, Class<T> adapterType) {
		if (uiProvider != null) {
			return uiProvider.getAdapter(adaptable, adapterType);
		}

		return null;
	}

	/**
	 * Returns an object which is an instance of the given class associated with this object. Returns <code>null</code>
	 * if
	 * no such object can be found.
	 * <p>
	 * This implementation of the method declared by <code>IAdaptable</code> passes the request along to the platform's
	 * adapter manager; roughly <code>Platform.getAdapterManager().getAdapter(this, adapter)</code>. Subclasses may
	 * override this method (however, if they do so, they should invoke the method on their superclass to ensure that
	 * the Platform's adapter manager is consulted).
	 * </p>
	 *
	 * @param adapterType
	 *            the class to adapt to
	 * @return the adapted object or <code>null</code>
	 * @see org.eclipse.core.runtime.IAdaptable#getAdapter(Class)
	 */
	@Override
	public Object getAdapter(@SuppressWarnings("rawtypes") Class adapterType) {
		return Platform.getAdapterManager().getAdapter(this, adapterType);
} /** {@inheritDoc} */ @Override public EditingDomain createEditingDomain(InternalProject project) { final CommandStack commandStack = createCommandStack(project); final EditingDomain editingDomain = new AdapterFactoryEditingDomain(InternalProvider.EMF_ADAPTER_FACTORY, commandStack); editingDomain.getResourceSet().eAdapters().add(new ECPModelContextAdapter(project)); return editingDomain; } /** * This is used during the creation of the {@link EditingDomain}. This implementation creates an * {@link BasicCommandStack}. * * @param project the project to create the {@link CommandStack} for * @return the created {@link CommandStack} */ protected CommandStack createCommandStack(InternalProject project) { return new BasicCommandStack(); } /** {@inheritDoc} */ @Override public boolean hasCreateRepositorySupport() { return true; } /** {@inheritDoc} */ @Override public boolean isSlow(Object parent) { return false; } /** {@inheritDoc} */ @Override public ECPContainer getModelContext(Object element) { if (element instanceof ECPContainer) { return (ECPContainer) element; } if (element instanceof ECPModelContextProvider) { return ((ECPModelContextProvider) element).getModelContext(element); } if (element instanceof EObject) { final EObject eObject = (EObject) element; final ECPContainer context = getModelContextFromAdapter(eObject); if (context != null) { return context; } element = eObject.eResource(); } if (element instanceof Resource) { final Resource resource = (Resource) element; final ECPContainer context = getModelContextFromAdapter(resource); if (context != null) { return context; } element = resource.getResourceSet(); } if (element instanceof ResourceSet) { final ResourceSet resourceSet = (ResourceSet) element; final ECPContainer context = getModelContextFromAdapter(resourceSet); if (context != null) { return context; } } return null; } /** * This allows to get the {@link ECPContainer} from a {@link Notifier} using the EcoreUtil. * This first gets the {@link ECPModelContextAdapter} and from it it gets the {@link ECPContainer}. * * @param notifier the {@link Notifier} to get the {@link ECPContainer} from * @return the {@link ECPContainer} registered as an Adapter on this {@link Notifier} or null */ protected final ECPContainer getModelContextFromAdapter(Notifier notifier) { final ECPModelContextAdapter adapter = (ECPModelContextAdapter) EcoreUtil.getAdapter(notifier.eAdapters(), ECPModelContextAdapter.class); if (adapter != null) { return adapter.getContext(); } return null; } /** {@inheritDoc} */ @Override public void fillChildren(ECPContainer context, Object parent, InternalChildrenList childrenList) { if (parent == ECPUtil.getECPProjectManager()) { childrenList.addChildren(ECPUtil.getECPProjectManager().getProjects()); } else if (parent == ECPUtil.getECPRepositoryManager()) { childrenList.addChildren(ECPUtil.getECPRepositoryManager().getRepositories()); } else { // Get the adapter from the factory. final ITreeItemContentProvider treeItemContentProvider = (ITreeItemContentProvider) EMF_ADAPTER_FACTORY .adapt( parent, ITreeItemContentProvider.class); // Either delegate the call or return nothing. if (treeItemContentProvider != null) { final Collection<?> children = treeItemContentProvider.getChildren(parent); childrenList.addChildren(children); } } } /** {@inheritDoc} */ @Override public void handleLifecycle(ECPContainer context, LifecycleEvent event) { final String providerClass = getClass().getSimpleName(); final String contextClass = context.getClass().getSimpleName(); Activator.log(providerClass + " received " + event + " for " + contextClass + " " + context); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } /** * Convenient implementation of the {@link #getUnsupportedEPackages(Collection,InternalRepository)} method to return * an empty list. The * provider has to {@link Override} this method if not all {@link EPackage}s are supported. * * @param packages available packages * @param repository the repository to check * * @return the {@link Collection} of {@link EPackage EPackages} unsupported by this provider for the specified * repository */ @Override public Set<EPackage> getUnsupportedEPackages(Collection<EPackage> packages, InternalRepository repository) { return Collections.emptySet(); } /** * Convenient implementation of the {@link #getLinkElements(InternalProject, EObject, EReference)} method to use the * {@link ItemPropertyDescriptor} to get all object of an object. * * @param project * the project the call is from * @param modelElement * {@link EObject} to add the {@link EReference} to * @param eReference * the {@link EReference} to add * @return {@link Iterator} of {@link EObject} that can be linked */ @Override public Iterator<EObject> getLinkElements(InternalProject project, EObject modelElement, EReference eReference) { final EClass elementClass = modelElement.eClass(); EClassifier type = EcoreUtil.getReifiedType(elementClass, eReference.getEGenericType()).getEClassifier(); if (type == null) { type = eReference.getEType(); } return ItemPropertyDescriptor.getReachableObjectsOfType(modelElement, type).iterator(); } /** * Convenient implementation where nothing happens. * * @param project * the project to save */ @Override public void doSave(InternalProject project) { // do nothing } /** * Convenient implementation where the provider saves changes of the project automatically, so a project never gets * dirty. Thus this returns false. * * @param project * the project to check * @return false */ @Override public boolean isDirty(InternalProject project) { return false; } /** * Convenient implementation that return true during this check. * * @param project the project to check * @return true */ @Override public boolean modelExists(InternalProject project) { return true; } /** * Convenient implementation that return false. * * @return false */ @Override public boolean hasCreateProjectWithoutRepositorySupport() { return false; } /** {@inheritDoc} */ // FIXME implement only in provider @Override public boolean contains(InternalProject project, Object object) { return false; } /** * List of registered {@link ProviderChangeListener}. * * @since 1.7 */ private final Set<ProviderChangeListener> changeListeners = new CopyOnWriteArraySet<ProviderChangeListener>(); /** * Registers a new {@link ProviderChangeListener}. * * @param listener the listener * @since 1.7 */ @Override public void registerChangeListener(ProviderChangeListener listener) { changeListeners.add(listener); } /** * Unregisters a new {@link ProviderChangeListener}. * * @param listener the listener * @since 1.7 */ @Override public void unregisterChangeListener(ProviderChangeListener listener) { changeListeners.remove(listener); } /** * @param notification a {@link Notification} * @since 1.7 */ protected void notifyProviderChangeListeners(Notification notification) { for (final ProviderChangeListener listener : changeListeners) { listener.notify(notification); } } /** * {@inheritDoc} * * @see org.eclipse.emf.ecp.spi.core.InternalProvider#delete(org.eclipse.emf.ecp.spi.core.InternalProject, * java.util.Collection) * @since 1.7 */ @Override public void delete(InternalProject project, Collection<Object> objects) { final ArrayList<Object> toBeDeleted = new ArrayList<Object>(); for (final Object object : objects) { if (object instanceof EObject) { final EObject eObject = (EObject) object; final boolean canDelete = notifyCanDelete(eObject); if (!canDelete) { continue; } notifyPredelete(eObject); } toBeDeleted.add(object); } doDelete(project, toBeDeleted); for (final Object object : toBeDeleted) { if (object instanceof EObject) { final EObject eObject = (EObject) object; notifyPostDelete(eObject); } } } private void notifyPostDelete(EObject toBeDeleted) { for (final ProviderChangeListener listener : changeListeners) { listener.postDelete(toBeDeleted); } } private void notifyPredelete(EObject toBeDeleted) { for (final ProviderChangeListener listener : changeListeners) { listener.preDelete(toBeDeleted); } } private boolean notifyCanDelete(EObject toBeDeleted) { boolean canDelete = true; for (final ProviderChangeListener listener : changeListeners) { canDelete = listener.canDelete(toBeDeleted); if (!canDelete) { break; } } return canDelete; } /** * Executes the delete opertation. Is supposed to be implemented by providers. * * @param project the project from where to delete * @param objects the {@link Collection} if {@link Object Objects} to delete * @since 1.7 */ public abstract void doDelete(InternalProject project, Collection<Object> objects); }