blob: 3ba80e3a490476a34782454c60d7acdfd00f62e1 [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 v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eike Stepper - initial API and implementation
*
*******************************************************************************/
package org.eclipse.emf.ecp.internal.core;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.EList;
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.EPackage.Registry;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecp.core.ECPRepository;
import org.eclipse.emf.ecp.core.util.ECPContainer;
import org.eclipse.emf.ecp.core.util.ECPElement;
import org.eclipse.emf.ecp.core.util.ECPFilterProvider;
import org.eclipse.emf.ecp.core.util.ECPModelContextAdapter;
import org.eclipse.emf.ecp.core.util.ECPProperties;
import org.eclipse.emf.ecp.core.util.ECPUtil;
import org.eclipse.emf.ecp.core.util.observer.ECPProjectPreDeleteObserver;
import org.eclipse.emf.ecp.internal.core.util.PropertiesElement;
import org.eclipse.emf.ecp.spi.core.InternalProject;
import org.eclipse.emf.ecp.spi.core.InternalProvider;
import org.eclipse.emf.ecp.spi.core.InternalProvider.LifecycleEvent;
import org.eclipse.emf.ecp.spi.core.InternalRepository;
import org.eclipse.emf.ecp.spi.core.util.ECPDisposable;
import org.eclipse.emf.ecp.spi.core.util.ECPDisposable.DisposeListener;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* @author Eike Stepper
* @author Eugen Neufeld
*/
public final class ECPProjectImpl extends PropertiesElement implements InternalProject, DisposeListener {
private InternalRepository repository;
private InternalProvider provider;
private Object providerSpecificData;
private Set<EPackage> filteredEPackages = Collections.emptySet();
private Set<EClass> filteredEClasses = Collections.emptySet();
private EditingDomain editingDomain;
private boolean open;
/**
* Constructor used when an offline project is created.
*
* @param provider the {@link InternalProvider} of this project
* @param name the name of the project
* @param properties the properties of the project
*/
public ECPProjectImpl(InternalProvider provider, String name, ECPProperties properties) {
super(name, properties);
this.provider = provider;
open = true;
setupFilteredEPackages();
notifyProvider(LifecycleEvent.INIT);
}
/**
* Constructor used when an online project is created.
*
* @param repository the {@link ECPRepository} of this project
* @param name the name of the project
* @param properties the properties of the project
*/
public ECPProjectImpl(ECPRepository repository, String name, ECPProperties properties) {
super(name, properties);
if (repository == null) {
throw new IllegalArgumentException("Repository is null");
}
setRepository((InternalRepository) repository);
provider = getRepository().getProvider();
open = true;
setupFilteredEPackages();
notifyProvider(LifecycleEvent.INIT);
}
/**
* Constructor used to load persisted projects on startup.
*
* @param in the {@link ObjectInput} to parse
* @throws IOException is thrown when file can't be read.
*/
public ECPProjectImpl(ObjectInput in) throws IOException {
super(in);
boolean shared = in.readBoolean();
if (shared) {
String repositoryName = in.readUTF();
InternalRepository repository = (InternalRepository) ECPUtil.getECPRepositoryManager().getRepository(
repositoryName);
if (repository == null) {
repository = new Disposed(repositoryName);
}
setRepository(repository);
provider = repository.getProvider();
} else {
String providerName = in.readUTF();
provider = (InternalProvider) ECPUtil.getECPProviderRegistry().getProvider(providerName);
if (provider == null) {
throw new IllegalStateException("Provider not found: " + providerName);
}
}
open = in.readBoolean();
int filteredPackageSize = in.readInt();
filteredEPackages = new HashSet<EPackage>();
for (int i = 0; i < filteredPackageSize; i++) {
EPackage ePackage = Registry.INSTANCE.getEPackage(in.readUTF());
if (ePackage != null) {
filteredEPackages.add(ePackage);
}
}
int filteredEClassSize = in.readInt();
filteredEClasses = new HashSet<EClass>();
for (int i = 0; i < filteredEClassSize; i++) {
EPackage ePackage = Registry.INSTANCE.getEPackage(in.readUTF());
EClassifier eClassifier = ePackage.getEClassifier(in.readUTF());
if (eClassifier instanceof EClass) {
filteredEClasses.add((EClass) eClassifier);
}
}
// do not initialize on startup, will be initializes by view
// notifyProvider(LifecycleEvent.INIT);
}
/**
* this method sets all known {@link EPackage}s as the filter.
*/
private void setupFilteredEPackages() {
List<ECPFilterProvider> filterProviders = new ArrayList<ECPFilterProvider>();
IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(
"org.eclipse.emf.ecp.core.filters");
for (IExtension extension : extensionPoint.getExtensions()) {
IConfigurationElement configurationElement = extension.getConfigurationElements()[0];
try {
ECPFilterProvider filterProvider = (ECPFilterProvider) configurationElement
.createExecutableExtension("class");
filterProviders.add(filterProvider);
} catch (CoreException ex) {
Activator.log(ex);
}
}
Set<EPackage> ePackages = new HashSet<EPackage>();
Set<String> filteredNsUris = new HashSet<String>();
for (ECPFilterProvider filterProvider : filterProviders) {
filteredNsUris.addAll(filterProvider.getHiddenPackages());
}
Set<String> relevantURIs = new HashSet<String>(Registry.INSTANCE.keySet());
relevantURIs.removeAll(filteredNsUris);
for (String nsUri : relevantURIs) {
EPackage ePackage = Registry.INSTANCE.getEPackage(nsUri);
ePackages.add(ePackage);
}
setVisiblePackages(ePackages);
}
@Override
public void write(ObjectOutput out) throws IOException {
super.write(out);
if (repository != null) {
out.writeBoolean(true);
out.writeUTF(repository.getName());
} else {
out.writeBoolean(false);
out.writeUTF(provider.getName());
}
out.writeBoolean(open);
out.writeInt(filteredEPackages.size());
for (EPackage ePackage : filteredEPackages) {
out.writeUTF(ePackage.getNsURI());
}
out.writeInt(filteredEClasses.size());
for (EClass eClass : filteredEClasses) {
out.writeUTF(eClass.getEPackage().getNsURI());
out.writeUTF(eClass.getName());
}
}
/** {@inheritDoc} */
@Override
public String getType() {
return TYPE;
}
/** {@inheritDoc} */
public void disposed(ECPDisposable disposable) {
if (disposable == repository) {
dispose();
}
}
/** {@inheritDoc} */
public boolean isStorable() {
return true;
}
/** {@inheritDoc} */
public InternalProject getProject() {
return this;
}
/** {@inheritDoc} */
public InternalRepository getRepository() {
return repository;
}
private void setRepository(InternalRepository repository) {
if (this.repository != null) {
this.repository.removeDisposeListener(this);
}
this.repository = repository;
if (this.repository != null) {
this.repository.addDisposeListener(this);
}
}
/** {@inheritDoc} */
public InternalProvider getProvider() {
return provider;
}
/** {@inheritDoc} */
public Object getProviderSpecificData() {
return providerSpecificData;
}
/** {@inheritDoc} */
public void setProviderSpecificData(Object providerSpecificData) {
this.providerSpecificData = providerSpecificData;
}
/** {@inheritDoc} */
public void notifyObjectsChanged(Collection<Object> objects, boolean structural) {
if (objects != null && objects.size() != 0) {
// if (getProvider().isDirty(this)) {
// getProvider().doSave(this);
// }
ECPProjectManagerImpl.INSTANCE.notifyObjectsChanged(this, objects, structural);
}
}
/** {@inheritDoc} */
public synchronized EditingDomain getEditingDomain() {
if (editingDomain == null) {
editingDomain = getProvider().createEditingDomain(this);
}
return editingDomain;
}
/**
* 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) IAdaptable#getAdapter(Class)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public Object getAdapter(Class adapterType) {
InternalProvider provider = getProvider();
if (provider != null && !provider.isDisposed()) {
Object result = provider.getAdapter(this, adapterType);
if (result != null) {
return result;
}
}
return Platform.getAdapterManager().getAdapter(this, adapterType);
}
/** {@inheritDoc} */
public ECPContainer getContext() {
return this;
}
/** {@inheritDoc} */
public boolean canDelete() {
return true;
}
/** {@inheritDoc} */
public void delete() {
ECPUtil.getECPObserverBus().notify(ECPProjectPreDeleteObserver.class).projectDelete(this);
getProvider().handleLifecycle(this, LifecycleEvent.REMOVE);
ECPProjectManagerImpl.INSTANCE.changeElements(Collections.singleton(getName()), null);
}
/** {@inheritDoc} */
public synchronized boolean isOpen() {
return !isRepositoryDisposed() && open;
}
/** {@inheritDoc} */
public synchronized void open() {
if (!isRepositoryDisposed()) {
setOpen(true);
}
}
/** {@inheritDoc} */
public synchronized void close() {
if (!isRepositoryDisposed()) {
setOpen(false);
}
}
private boolean isRepositoryDisposed() {
return repository != null && repository.isDisposed();
}
private void setOpen(boolean open) {
boolean modified = false;
synchronized (this) {
if (open != this.open) {
this.open = open;
modified = true;
notifyProvider(open ? LifecycleEvent.INIT : LifecycleEvent.DISPOSE);
if (!open) {
providerSpecificData = null;
editingDomain = null;
}
}
}
if (modified) {
ECPProjectManagerImpl.INSTANCE.changeProject(this, open, true);
}
}
/** {@inheritDoc} */
public void notifyProvider(LifecycleEvent event) {
InternalProvider provider = getProvider();
provider.handleLifecycle(this, event);
if (event == LifecycleEvent.INIT) {
Notifier root = provider.getRoot(this);
if (root != null) {
root.eAdapters().add(new ECPModelContextAdapter(this));
}
}
}
/** {@inheritDoc} */
public void undispose(InternalRepository repository) {
setRepository(repository);
notifyProvider(LifecycleEvent.INIT);
if (open) {
ECPProjectManagerImpl.INSTANCE.changeProject(this, true, true);
}
}
private void dispose() {
notifyProvider(LifecycleEvent.DISPOSE);
if (repository != null) {
setRepository(new Disposed(repository.getName()));
}
providerSpecificData = null;
editingDomain = null;
ECPProjectManagerImpl.INSTANCE.changeProject(this, false, false);
}
/**
* @author Eike Stepper
*/
private static final class Disposed implements InternalRepository {
private final String name;
public Disposed(String name) {
this.name = name;
}
/** {@inheritDoc} */
public String getType() {
return TYPE;
}
/** {@inheritDoc} */
public String getName() {
return name;
}
/** {@inheritDoc} */
public boolean isDisposed() {
return true;
}
/** {@inheritDoc} */
public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
return null;
}
/** {@inheritDoc} */
public String getLabel() {
return null;
}
/** {@inheritDoc} */
public String getDescription() {
return null;
}
/** {@inheritDoc} */
public int compareTo(ECPElement o) {
return 0;
}
/** {@inheritDoc} */
public ECPContainer getContext() {
return null;
}
/** {@inheritDoc} */
public ECPProperties getProperties() {
return null;
}
/** {@inheritDoc} */
public boolean canDelete() {
return false;
}
/** {@inheritDoc} */
public void delete() {
}
/** {@inheritDoc} */
public boolean isStorable() {
return false;
}
/** {@inheritDoc} */
public void write(ObjectOutput out) throws IOException {
}
/** {@inheritDoc} */
public void setLabel(String label) {
}
/** {@inheritDoc} */
public void setDescription(String description) {
}
/** {@inheritDoc} */
public void dispose() {
}
/** {@inheritDoc} */
public void addDisposeListener(DisposeListener listener) {
}
/** {@inheritDoc} */
public void removeDisposeListener(DisposeListener listener) {
}
/** {@inheritDoc} */
public InternalProvider getProvider() {
return null;
}
/** {@inheritDoc} */
public Object getProviderSpecificData() {
return null;
}
/** {@inheritDoc} */
public void setProviderSpecificData(Object data) {
}
/** {@inheritDoc} */
public void notifyObjectsChanged(Collection<Object> objects) {
}
}
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
public EList<Object> getContents() {
return (EList<Object>) getProvider().getElements(this);
}
/** {@inheritDoc} */
public Set<EPackage> getUnsupportedEPackages() {
return getProvider().getUnsupportedEPackages(ECPUtil.getAllRegisteredEPackages(), getRepository());
}
/** {@inheritDoc} */
public void setVisiblePackages(Set<EPackage> filteredPackages) {
filteredEPackages = filteredPackages;
ECPProjectManagerImpl.INSTANCE.changeProject(this, open, true);
}
/** {@inheritDoc} */
public Set<EPackage> getVisiblePackages() {
return filteredEPackages;
}
/** {@inheritDoc} */
public Set<EClass> getVisibleEClasses() {
return filteredEClasses;
}
/** {@inheritDoc} */
public void setVisibleEClasses(Set<EClass> filteredEClasses) {
this.filteredEClasses = filteredEClasses;
ECPProjectManagerImpl.INSTANCE.changeProject(this, open, true);
}
/** {@inheritDoc} */
public Iterator<EObject> getReferenceCandidates(EObject modelElement, EReference eReference) {
return getProvider().getLinkElements(this, modelElement, eReference);
}
/** {@inheritDoc} */
public void saveContents() {
getProvider().doSave(this);
}
/** {@inheritDoc} */
public boolean hasDirtyContents() {
return getProvider().isDirty(this);
}
/** {@inheritDoc} */
public void deleteElements(Collection<Object> objects) {
getProvider().delete(this, objects);
notifyObjectsChanged((Collection) Collections.singleton(this), true);
}
/** {@inheritDoc} */
public InternalProject clone(String name) {
try {
super.clone();
} catch (CloneNotSupportedException ex) {
Activator.log(ex);
}
InternalProject project = new ECPProjectImpl(getProvider(), name, ECPUtil.createProperties());
project.setVisibleEClasses(getVisibleEClasses());
project.setVisiblePackages(getVisiblePackages());
getProvider().cloneProject(this, project);
return project;
}
/** {@inheritDoc} */
public void saveProperties() {
ECPProjectManagerImpl.INSTANCE.storeElement(this);
}
/** {@inheritDoc} */
public boolean isModelRoot(Object object) {
return getProvider().getRoot(this).equals(object);
}
/** {@inheritDoc} */
public boolean contains(Object object) {
return getProvider().contains(this, object);
}
}