blob: 977ffaefdf2346aff4121b99a573345a7ade060f [file] [log] [blame]
/**
* 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);
}