blob: 1686b3866c85aa347f31553b66f7ed1961b34067 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.bpel.common.ui.editmodel;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.bpel.model.resource.BPELResourceSetImpl;
import org.eclipse.bpel.common.ui.CommonUIPlugin;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
/**
* EditModel is shared between editor with the same primary file input. It holds
* on to a command stack, and a resource set.
*
* This class has a reference count cache of EditModels. Once all editors that
* reference a editModel are closed all resource in the shared resource set are
* unloaded.
*
* Life-cycle call getEditModel using its primary file. the shared resource set
* reference count is incremented; call getResourceInfo using any file that need
* to be loaded the shared resource reference count is incremented; call
* releaseReference the shared resource reference count is decremented; call
* release if it is the last reference the shared resource set is disposed
*/
public class EditModel {
protected static EditModelCache cache = new EditModelCache();
protected Map<IFile,ResourceInfo> fileToResourceInfo = new HashMap<IFile,ResourceInfo>();
protected ResourceSet resourceSet;
protected int referenceCount = 0;
protected IResource primaryFile;
protected EditModelCommandStack commandStack;
private List<IEditModelListener> updateListeners = null;
/**
* Get the edit model based on the primary resource file.
*
* @param primaryFile
* @return the edit model
*/
public static EditModel getEditModel(IResource primaryFile) {
return cache.getEditModel(primaryFile, new Factory());
}
/**
* Get the edit model based on the primary resource file.
* @param primaryFile primary file (the BPEL resource)
* @param factory factory
* @return the editor model
*
*/
public static EditModel getEditModel(IResource primaryFile, Factory factory) {
return cache.getEditModel(primaryFile, factory);
}
/**
* Private constructor. Use the static factory methods.
*/
protected EditModel(ResourceSet rSet, IResource bpelFile) {
this.resourceSet = rSet;
this.primaryFile = bpelFile;
this.updateListeners = new ArrayList<IEditModelListener>();
}
/**
* Add a model listener.
* @param listener
*/
public void addListener (IEditModelListener listener) {
if (updateListeners.contains(listener)) {
return;
}
updateListeners.add(listener);
}
/**
* Remove a model listener.
* @param listener
*/
public void removeListener(IEditModelListener listener) {
updateListeners.remove(listener);
}
protected void fireModelDirtyStateChanged(ResourceInfo sr) {
for (IEditModelListener next : updateListeners ) {
next.modelDirtyStateChanged(sr);
}
}
protected void fireModelDeleted(ResourceInfo sr) {
// bugzilla 324006
// Prevent concurrent list modification exception:
// When a resource is deleted, the BPEL editor shuts itself down for no
// apparently good reason (I'm sure it's to avoid some other kinds of disasters
// that it isn't equipped to deal with!) This causes the editor to remove itself
// from our update listeners list.
ArrayList<IEditModelListener> listeners = new ArrayList<IEditModelListener>(updateListeners.size());
listeners.addAll(updateListeners);
for (IEditModelListener next : listeners ) {
next.modelDeleted(sr);
}
}
protected void fireModelReloaded(ResourceInfo sr) {
ArrayList<IEditModelListener> listeners = new ArrayList<IEditModelListener>(updateListeners.size());
listeners.addAll(updateListeners);
for (IEditModelListener next : listeners ) {
next.modelReloaded(sr);
}
}
protected void fireModelLocationChanged(ResourceInfo sr, IFile movedToFile) {
ArrayList<IEditModelListener> listeners = new ArrayList<IEditModelListener>(updateListeners.size());
listeners.addAll(updateListeners);
for (IEditModelListener next : listeners ) {
next.modelLocationChanged(sr,movedToFile);
}
}
protected void fireModelMarkersChanged (ResourceInfo sr, IMarkerDelta[] markerDelta ) {
ArrayList<IEditModelListener> listeners = new ArrayList<IEditModelListener>(updateListeners.size());
listeners.addAll(updateListeners);
for (IEditModelListener next : listeners ) {
next.modelMarkersChanged(sr,markerDelta );
}
}
/**
* Return the resource set associated with this editModel.
* @return the resource set used by this Edit Model client.
*/
public ResourceSet getResourceSet() {
return resourceSet;
}
/**
* Returns the cached ResourceInfo for <code>file</code>
*
* Creates a new ResourceInfo it is not found in the cache, otherwise
* increment the reference count and return it.
*/
ResourceInfo getResourceInfoForLoadedResource(Resource resource) {
URI uri = resource.getURI();
IFile file = getIFileForURI(uri);
if (file == null)
return null;
ResourceInfo resourceInfo = fileToResourceInfo.get(file);
if (resourceInfo == null) {
resourceInfo = new ResourceInfo(this, file);
resourceInfo.setResource(resource);
addResourceInfo(resourceInfo);
resourceInfo.resourceLoaded();
}
if (!resourceInfo.loading)
resourceInfo.referenceCount++;
return resourceInfo;
}
/**
* Returns the cached ResourceInfo for <code>file</code>
*
* Creates a new ResourceInfo it is not found in the cache, otherwise
* increment the reference count and return it.
*
* @param file the file
* @return the cached resource info for that file.
*/
public ResourceInfo getResourceInfo(IFile file) {
ResourceInfo resourceInfo = fileToResourceInfo.get(file);
if (resourceInfo == null) {
resourceInfo = new ResourceInfo(this, file);
addResourceInfo(resourceInfo);
try {
resourceInfo.load();
} catch (RuntimeException ex) {
resourceInfo.referenceCount++;
releaseReference(resourceInfo);
throw ex;
}
}
resourceInfo.referenceCount++;
return resourceInfo;
}
static ResourceInfo [] EMPTY_RESOURCE_ARRAY = {};
/**
*
* @return The resource infos
*
*/
public ResourceInfo[] getResourceInfos() {
return fileToResourceInfo.values().toArray(EMPTY_RESOURCE_ARRAY);
}
private void setPrimaryFile(IFile newFile) {
IResource oldFile = primaryFile;
primaryFile = newFile;
cache.updatePrimaryFile(oldFile, newFile);
}
/**
* @return the primary resource (primary file) for this edit model.
*/
public IResource getPrimaryFile() {
return primaryFile;
}
/**
* Decrement the reference count for <code>resourceInfo</code> and dispose
* if it is the last reference.
* @param resourceInfo
*/
public void releaseReference (ResourceInfo resourceInfo) {
resourceInfo.referenceCount--;
if (resourceInfo.referenceCount == 0) {
resourceInfo.dispose();
removeResourceInfo(resourceInfo);
}
}
/**
* Add resourceInfo to the cache
*/
protected void addResourceInfo(ResourceInfo sr) {
fileToResourceInfo.put(sr.getFile(), sr);
}
/*
* Remove the resourceInfo from the cache.
*/
protected void removeResourceInfo(ResourceInfo sr) {
fileToResourceInfo.remove(sr.getFile());
}
/**
* Dispose this EditModel if there is no other reference to it;
*/
public void release() {
referenceCount--;
if (referenceCount == 0) {
cache.remove(this);
for (ResourceInfo resourceInfo : fileToResourceInfo.values().toArray(EMPTY_RESOURCE_ARRAY) ) {
resourceInfo.dispose();
}
fileToResourceInfo.clear();
}
}
public static IFile getIFileForURI(URI uri) {
String filePath = null;
String scheme = uri.scheme();
IFile file = null;
if ("file".equals(scheme)) { //$NON-NLS-1$
filePath = uri.toFileString();
if (filePath == null)
return null;
file = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(
new Path(filePath));
} else if ("platform".equals(scheme) && uri.segmentCount() > 1 && "resource".equals(uri.segment(0))) { //$NON-NLS-1$//$NON-NLS-2$
StringBuffer platformResourcePath = new StringBuffer();
for (int i = 1, size = uri.segmentCount(); i < size; ++i) {
platformResourcePath.append('/');
platformResourcePath.append(uri.segment(i));
}
filePath = platformResourcePath.toString();
if (filePath == null)
return null;
file = ResourcesPlugin.getWorkspace().getRoot().getFile(
new Path(filePath));
}
return file;
}
/**
* Get the command stack.
*
* @return the command stack.
*/
public EditModelCommandStack getCommandStack() {
return commandStack;
}
/**
* Set the command stack.
*
* @param stack the command stack.
*/
public void setCommandStack(EditModelCommandStack stack) {
this.commandStack = stack;
}
/**
* Saves the model
*
* @param progressMonitor progress monitor.
* @return if all went OK true, false otherwise.
*/
public boolean saveAll(IProgressMonitor progressMonitor) {
WorkspaceModifyOperation operation = new WorkspaceModifyOperation() {
@Override
protected void execute(IProgressMonitor monitor)
throws InvocationTargetException {
getCommandStack().markSaveLocation();
try {
ResourceInfo[] sResource = getResourceInfos();
for (int i = 0; i < sResource.length; i++) {
ResourceInfo resource = sResource[i];
if (resource.isDirty())
resource.save();
}
} catch (IOException e) {
throw new InvocationTargetException(e);
}
}
};
try {
operation.run(progressMonitor);
} catch (InvocationTargetException e) {
return false;
} catch (InterruptedException e) {
return false;
}
return true;
}
/**
* Save primary resource as ...
*
* @param resourceInfo
* @param savedFile
* @param progressMonitor
* @return save result (true is OK).
*/
public boolean savePrimaryResourceAs(final ResourceInfo resourceInfo,
final IFile savedFile, IProgressMonitor progressMonitor) {
boolean result = saveResourceAs(resourceInfo, savedFile,
progressMonitor);
if (result)
setPrimaryFile(savedFile);
return result;
}
/**
* Save resource as ...
* @param resourceInfo
* @param savedFile
* @param progressMonitor
* @return the status of the save ...
*/
public boolean saveResourceAs(final ResourceInfo resourceInfo,
final IFile savedFile, IProgressMonitor progressMonitor) {
WorkspaceModifyOperation operation = new WorkspaceModifyOperation() {
@Override
protected void execute(IProgressMonitor monitor)
throws InvocationTargetException {
try {
getCommandStack().markSaveLocation();
resourceInfo.saveAs(savedFile);
} catch (IOException e) {
throw new InvocationTargetException(e);
}
}
};
try {
operation.run(progressMonitor);
} catch (InvocationTargetException e) {
return false;
} catch (InterruptedException e) {
return false;
}
return true;
}
/**
*
* @author IBM
*
*/
public static class Factory {
protected EditModel createEditModel(ResourceSet resourceSet,
IResource primaryFile) {
return new EditModel(resourceSet, primaryFile);
}
}
static class EditModelCache {
protected Map<ResourceSet,EditModel> resourceSetToEditModel = new HashMap<ResourceSet,EditModel>();
protected Map<IResource,ResourceSet> fileToResourceSet = new HashMap<IResource,ResourceSet>();
/**
* Return a new ResourceSet for the specified file.
* @param primaryFile
* @param factory
* @return the edit model.
*/
public EditModel getEditModel(IResource primaryFile, Factory factory) {
ResourceSet resourceSet = getResourceSet(primaryFile);
return getEditModel(resourceSet, primaryFile, factory);
}
/**
* Return the EditModel for specified resource set.
*
* Creates a new EditModel it is not found in the cache, otherwise
* increment the reference count and return it.
*/
@SuppressWarnings("unchecked")
private EditModel getEditModel(ResourceSet resourceSet,
IResource primaryFile, Factory factory) {
EditModel editModel = resourceSetToEditModel.get(resourceSet);
if (editModel != null) {
editModel.referenceCount++;
return editModel;
}
editModel = factory.createEditModel(resourceSet, primaryFile);
editModel.referenceCount++;
resourceSetToEditModel.put(resourceSet, editModel);
final EditModel finalEditModel = editModel;
resourceSet.eAdapters().add(new AdapterImpl() {
@Override
public void notifyChanged(Notification msg) {
Resource r = (Resource) msg.getNewValue();
finalEditModel.getResourceInfoForLoadedResource(r);
}
});
return editModel;
}
/*
* Return a new ResourceSet for the specified file.
*/
private ResourceSet getResourceSet(IResource primaryFile) {
ResourceSet resourceSet = fileToResourceSet.get(primaryFile);
if(resourceSet != null)
return resourceSet;
// TODO: Extensibility
resourceSet = new BPELResourceSetImpl();
fileToResourceSet.put(primaryFile,resourceSet);
return resourceSet;
}
/**
*
* @param editModel
*/
public void remove(EditModel editModel) {
resourceSetToEditModel.remove(editModel.resourceSet);
fileToResourceSet.remove(editModel.primaryFile);
}
void updatePrimaryFile(IResource oldFile, IResource newFile) {
ResourceSet rs = fileToResourceSet.get(oldFile);
fileToResourceSet.remove(oldFile);
fileToResourceSet.put(newFile, rs);
}
}
static String EXTPT_RESOURCE_SET_PROVIDER = "resourceSetProvider"; //$NON-NLS-1$
static String ELM_PROVIDER = "provider"; //$NON-NLS-1$
static String ATT_CLASS = "class"; //$NON-NLS-1$
static private IResourceSetProvider gfResourceSetProvider;
static IResourceSetProvider getResourceSetProvider () {
if (gfResourceSetProvider != null) {
return gfResourceSetProvider;
}
for(IConfigurationElement elm : CommonUIPlugin.getConfigurationElements( EXTPT_RESOURCE_SET_PROVIDER)) {
if (ELM_PROVIDER.equals(elm.getName()) == false) {
continue;
}
String clazz = elm.getAttribute(ATT_CLASS);
if (clazz != null) {
try {
gfResourceSetProvider = (IResourceSetProvider) elm.createExecutableExtension(ATT_CLASS);
} catch ( CoreException ce ) {
CommonUIPlugin.getDefault().getLog().log(ce.getStatus());
}
}
if (gfResourceSetProvider != null) {
break;
}
}
if (gfResourceSetProvider == null) {
gfResourceSetProvider = new IResourceSetProvider () {
public ResourceSet getResourceSet (IResource resource) {
return new ResourceSetImpl();
}
};
}
return gfResourceSetProvider;
}
}