blob: 7bddf4ad79150e6dbbca62063231ec921cbb6303 [file] [log] [blame]
/***************************************************************************************************
* Copyright (c) 2003, 2004 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.wst.common.internal.emfworkbench.integration;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.command.BasicCommandStack;
import org.eclipse.emf.common.command.CommandStackListener;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.jem.internal.util.emf.workbench.nls.EMFWorkbenchResourceHandler;
import org.eclipse.jem.util.emf.workbench.ResourceSetWorkbenchSynchronizer;
import org.eclipse.jem.util.emf.workbench.WorkbenchResourceHelperBase;
import org.eclipse.wst.common.frameworks.internal.ISaveHandler;
import org.eclipse.wst.common.frameworks.internal.SaveFailedException;
import org.eclipse.wst.common.frameworks.internal.SaveHandlerHeadless;
import org.eclipse.wst.common.frameworks.internal.SaveHandlerRegister;
import org.eclipse.wst.common.frameworks.internal.enablement.EnablementIdentifierEvent;
import org.eclipse.wst.common.frameworks.internal.enablement.IEnablementIdentifier;
import org.eclipse.wst.common.frameworks.internal.enablement.IEnablementIdentifierListener;
import org.eclipse.wst.common.frameworks.internal.enablement.nonui.IWFTWrappedException;
import org.eclipse.wst.common.frameworks.internal.operations.IOperationHandler;
import org.eclipse.wst.common.internal.emf.resource.CompatibilityXMIResource;
import org.eclipse.wst.common.internal.emf.resource.ReferencedResource;
import org.eclipse.wst.common.internal.emf.resource.TranslatorResource;
import org.eclipse.wst.common.internal.emf.utilities.ExtendedEcoreUtil;
import org.eclipse.wst.common.internal.emf.utilities.PleaseMigrateYourCodeError;
import org.eclipse.wst.common.internal.emfworkbench.EMFWorkbenchContext;
import org.eclipse.wst.common.internal.emfworkbench.WorkbenchResourceHelper;
import org.eclipse.wst.common.internal.emfworkbench.edit.ClientAccessRegistry;
import org.eclipse.wst.common.internal.emfworkbench.edit.EditModelRegistry;
import org.eclipse.wst.common.internal.emfworkbench.edit.EditModelResource;
import org.eclipse.wst.common.internal.emfworkbench.validateedit.ResourceStateInputProvider;
import org.eclipse.wst.common.internal.emfworkbench.validateedit.ResourceStateValidator;
import org.eclipse.wst.common.internal.emfworkbench.validateedit.ResourceStateValidatorImpl;
import org.eclipse.wst.common.internal.emfworkbench.validateedit.ResourceStateValidatorPresenter;
public class EditModel implements CommandStackListener, ResourceStateInputProvider, ResourceStateValidator, IEnablementIdentifierListener {
protected BasicCommandStack commandStack;
protected List listeners;
protected List removedListeners = new ArrayList();
private Map params;
private final String editModelID;
private final boolean readOnly;
//These are the current resource uris we need to track
protected List knownResourceUris;
//This is a subset of the known resource uris, which we have requested be autoloaded
protected List preloadResourceUris;
//This is a map of identifiers to resources that we need to listen to in order to listen for
//updates to the edit model resources
protected Map resourceIdentifiers;
protected EditModelEvent dirtyModelEvent;
protected boolean isNotifing = false;
protected boolean disposing = false;
private boolean disposed = false;
protected ResourceStateValidator stateValidator;
protected boolean accessAsReadForUnKnownURIs;
protected ResourceAdapter resourceAdapter = new ResourceAdapter();
protected boolean isReverting = false;
protected List resources;
private final ClientAccessRegistry registry = new ClientAccessRegistry();
protected EMFWorkbenchContext emfContext = null;
protected IProject project = null;
private Reference reference;
private List resourcesTargetedForTermination;
protected class ResourceAdapter extends AdapterImpl {
public void notifyChanged(Notification notification) {
if (notification.getEventType() == Notification.SET && notification.getFeatureID(null) == Resource.RESOURCE__IS_LOADED) {
resourceIsLoadedChanged((Resource) notification.getNotifier(), notification.getOldBooleanValue(), notification.getNewBooleanValue());
}
}
}
public EditModel(String editModelID, EMFWorkbenchContext context, boolean readOnly) {
if (context == null)
throw new IllegalStateException("EMF context can't be null");
this.editModelID = editModelID;
this.readOnly = readOnly;
this.emfContext = context;
this.project = context.getProject();
initializeKnownResourceUris();
processLoadedResources();
processPreloadResources();
}
public EditModel(String editModelID, EMFWorkbenchContext context, boolean readOnly, boolean accessUnknownResourcesAsReadOnly) {
this(editModelID, context, readOnly);
this.accessAsReadForUnKnownURIs = accessUnknownResourcesAsReadOnly;
}
/**
* @return editModelID
*/
public String getEditModelID() {
return editModelID;
}
/**
* Insert the method's description here. Creation date: (9/27/2001 10:25:43 PM)
*
* @return boolean
*/
protected boolean isDisposing() {
return disposing;
}
public void dispose() {
synchronized (this) {
if(disposing || isDisposed())
return;
disposing = true;
}
try {
if (commandStack != null)
commandStack.removeCommandStackListener(this);
if (hasListeners())
notifyListeners(new EditModelEvent(EditModelEvent.PRE_DISPOSE, this));
if (getEmfContext() != null)
getEmfContext().removeEditModel(this, isReadOnly());
releasePreloadResources();
releaseIdentifiers();
emfContext = null;
listeners = null;
removedListeners = null;
resources = null;
project = null;
} catch(RuntimeException re) {
re.printStackTrace();
} finally {
disposing = false;
disposed = true;
}
}
protected void releaseIdentifiers() {
if (resourceIdentifiers == null)
return;
Iterator iter = resourceIdentifiers.keySet().iterator();
IEnablementIdentifier identifier = null;
while (iter.hasNext()) {
identifier = (IEnablementIdentifier) iter.next();
identifier.removeIdentifierListener(this);
}
}
private ResourceSetWorkbenchSynchronizer getResourceSetSynchronizer() {
if (emfContext == null || !emfContext.hasResourceSet())
return null;
return getEmfContext().getResourceSet().getSynchronizer();
}
protected void releasePreloadResources() {
ResourceSetWorkbenchEditSynchronizer sync = (ResourceSetWorkbenchEditSynchronizer) getResourceSetSynchronizer();
if (sync != null) {
for (int i = 0; i < preloadResourceUris.size(); i++) {
URI uri = (URI) preloadResourceUris.get(i);
sync.disableAutoload(uri);
}
}
}
/** ** BEGIN Command Stack Manipulation *** */
/**
* Return the CommandStack.
*/
protected BasicCommandStack createCommandStack() {
BasicCommandStack stack = new BasicCommandStack();
return stack;
}
/**
* This is called with the {@link CommandStack}'s state has changed.
*/
public void commandStackChanged(java.util.EventObject event) {
if (dirtyModelEvent == null)
dirtyModelEvent = new EditModelEvent(EditModelEvent.DIRTY, this);
if (hasListeners())
notifyListeners(dirtyModelEvent);
}
/**
* Flush the Commands from the CommandStack.
*/
protected void flushCommandStack() {
getCommandStack().flush();
getCommandStack().saveIsDone();
}
/**
* Return the CommandStack.
*/
public BasicCommandStack getCommandStack() {
if (commandStack == null) {
commandStack = createCommandStack();
commandStack.addCommandStackListener(this);
}
return commandStack;
}
/**
* Returns true if there are any listeners
*/
public boolean hasListeners() {
return !getListeners().isEmpty();
}
/** ** END Command Stack Manipulation *** */
/** ** BEGIN Listeners *** */
/**
* Add
*
* @aListener to the list of listeners.
*/
public void addListener(EditModelListener aListener) {
if (aListener != null && !getListeners().contains(aListener))
getListeners().add(aListener);
}
/**
* Insert the method's description here. Creation date: (4/11/2001 4:42:58 PM)
*
* @return java.util.List
*/
protected java.util.List getListeners() {
if (listeners == null)
listeners = new ArrayList();
return listeners;
}
/**
* Notify listeners of
*
* @anEvent.
*/
protected void notifyListeners(EditModelEvent anEvent) {
if (listeners == null)
return;
boolean oldIsNotifying = isNotifing;
synchronized (this) {
isNotifing = true;
}
try {
List list = getListeners();
for (int i = 0; i < list.size(); i++) {
EditModelListener listener = (EditModelListener) list.get(i);
if (!removedListeners.contains(listener))
listener.editModelChanged(anEvent);
}
} finally {
synchronized (this) {
isNotifing = oldIsNotifying;
if (!isNotifing && removedListeners != null && !removedListeners.isEmpty()) {
listeners.removeAll(removedListeners);
removedListeners.clear();
}
}
}
}
/**
* Remove
*
* @aListener from the list of listeners.
*/
public synchronized boolean removeListener(EditModelListener aListener) {
if (aListener != null) {
if (isNotifing)
return removedListeners.add(aListener);
return getListeners().remove(aListener);
}
return false;
}
/** ** END Listeners *** */
protected void makeFileEditable(IFile aFile) {
if (aFile == null)
return;
aFile.setReadOnly(false);
}
/**
* @return java.util.List of IFile; any read-only files that will be touched if this edit model
* saves
*/
public List getReadOnlyAffectedFiles() {
Iterator affected = getAffectedFiles().iterator();
List result = new ArrayList();
while (affected.hasNext()) {
IFile aFile = (IFile) affected.next();
if (aFile.isReadOnly())
result.add(aFile);
}
return result;
}
/** ** BEGIN Save Handlers *** */
protected ISaveHandler getSaveHandler() {
return SaveHandlerRegister.getSaveHandler();
}
/**
* Default is to do nothing. This method is called if a saveIfNecessary or
* saveIfNecessaryWithPrompt determines not to save. This provides subclasses with an
* opportunity to do some other action.
*/
protected void handleSaveIfNecessaryDidNotSave(IProgressMonitor monitor) {
//do nothing
}
/**
* This will force all of the referenced Resources to be saved.
*/
public void save(Object accessorKey) {
save(null, accessorKey);
}
/**
* This will force all of the referenced Resources to be saved.
*/
public void save(IProgressMonitor monitor) throws PleaseMigrateYourCodeError {
//save
}
/**
* Subclasses may override {@link #primSave}
*/
public final void save(IProgressMonitor monitor, Object accessorKey) {
assertPermissionToSave(accessorKey);
getSaveHandler().access();
try {
IWorkspaceRunnable runnable = new IWorkspaceRunnable() {
public void run(IProgressMonitor aMonitor) {
primSave(aMonitor);
}
};
runSaveOperation(runnable, monitor);
} catch (SaveFailedException ex) {
getSaveHandler().handleSaveFailed(ex, monitor);
} finally {
getSaveHandler().release();
}
}
/**
* Save only resources that need to be saved (i.e., no other references).
*/
public void saveIfNecessary(Object accessorKey) {
saveIfNecessary(null, accessorKey);
}
/**
* Save only resources that need to be saved (i.e., no other references).
*/
public void saveIfNecessary(IProgressMonitor monitor, Object accessorKey) {
if (shouldSave())
save(monitor, accessorKey);
else
handleSaveIfNecessaryDidNotSave(monitor);
}
/**
* Save only if necessary. If typically a save would not occur because this edit model is
* shared, the user will be prompted using the
*
* @operationHandler. If the prompt returns true (the user wants to save) the entire edit model
* will be saved.
*/
public void saveIfNecessaryWithPrompt(IOperationHandler operationHandler, Object accessorKey) {
saveIfNecessaryWithPrompt(null, operationHandler, accessorKey);
}
/**
* Save only if necessary. If typically a save would not occur because this edit model is
* shared, the user will be prompted using the
*
* @operationHandler. If the prompt returns true (the user wants to save) the entire edit model
* will be saved. You may pass in a boolean <code>wasDirty</code> to
* indicate whether this edit model was dirty prior to making any changes and
* calling this method. {@link EditModel#isDirty()}
*/
public void saveIfNecessaryWithPrompt(IOperationHandler operationHandler, boolean wasDirty, Object accessorKey) {
saveIfNecessaryWithPrompt(null, operationHandler, wasDirty, accessorKey);
}
/**
* Save only if necessary. If typically a save would not occur because this edit model is
* shared, the user will be prompted using the
*
* @operationHandler. If the prompt returns true (the user wants to save) the entire edit model
* will be saved.
*/
public void saveIfNecessaryWithPrompt(IProgressMonitor monitor, IOperationHandler operationHandler, Object accessorKey) {
saveIfNecessaryWithPrompt(monitor, operationHandler, true, accessorKey);
}
/**
* Save only if necessary. If typically a save would not occur because this edit model is
* shared, the user will be prompted using the
*
* @operationHandler. If the prompt returns true (the user wants to save) the entire edit model
* will be saved. You may pass in a boolean <code>wasDirty</code> to
* indicate whether this edit model was dirty prior to making any changes and
* calling this method. {@link EditModel#isDirty()}
*/
public void saveIfNecessaryWithPrompt(IProgressMonitor monitor, IOperationHandler operationHandler, boolean wasDirty, Object accessorKey) {
if (shouldSave(operationHandler, wasDirty))
save(monitor, accessorKey);
else
handleSaveIfNecessaryDidNotSave(monitor);
}
protected void assertPermissionToSave(Object accessorKey) {
if (registry != null)
registry.assertAccess(accessorKey);
}
private void runSaveOperation(IWorkspaceRunnable runnable, IProgressMonitor monitor) throws SaveFailedException {
try {
ResourcesPlugin.getWorkspace().run(runnable, monitor);
} catch (CoreException e) {
throw new SaveFailedException(e);
}
}
/**
* Should the resources be saved.
*/
protected boolean shouldSave(IOperationHandler operationHandler, boolean wasDirty) {
return !wasDirty ? shouldSave() : shouldSave(operationHandler);
}
/**
* Return true if the uri for
*
* @aResource is one of the known resource uris.
*/
public boolean isInterrestedInResource(Resource aResource) {
return isInterrestedInResourceUri(aResource.getURI());
}
protected boolean isInterrestedInResourceUri(URI resURI) {
URI uri;
List uriStrings = getKnownResourceUris();
for (int i = 0; i < uriStrings.size(); i++) {
uri = (URI) uriStrings.get(i);
if (ExtendedEcoreUtil.endsWith(resURI, uri))
return true;
}
return false;
}
/**
* Subclasses should override and add URIs (type URI) of known resources. You must add resources
* that have references to other known resources first so they will be released first.
*/
protected void initializeKnownResourceUris() {
knownResourceUris = new ArrayList();
preloadResourceUris = new ArrayList();
EditModelResource res = null;
Collection editModelResources = EditModelRegistry.getInstance().getEditModelResources(getEditModelID());
Iterator iter = editModelResources.iterator();
while (iter.hasNext()) {
res = (EditModelResource) iter.next();
addEditModelResource(res);
}
}
private void addEditModelResource(EditModelResource res) {
boolean enabled = false;
if (res.isCore()) {
enabled = true;
} else {
IEnablementIdentifier identifier = res.getEnablementIdentifier(getProject());
registerInterest(identifier, res);
enabled = identifier.isEnabled();
}
if (enabled) {
URI uri = res.getURI();
knownResourceUris.add(uri);
if (res.isAutoLoad()) {
ResourceSetWorkbenchEditSynchronizer sync = (ResourceSetWorkbenchEditSynchronizer) getEmfContext().getResourceSet().getSynchronizer();
sync.enableAutoload(uri);
preloadResourceUris.add(uri);
}
}
}
/**
* @param res
*/
private void registerInterest(IEnablementIdentifier identifier, EditModelResource res) {
getEditModelResources(identifier).add(res);
}
private List getEditModelResources(IEnablementIdentifier identifier) {
if (resourceIdentifiers == null)
resourceIdentifiers = new HashMap();
List tResources = (List) resourceIdentifiers.get(identifier);
if (tResources == null) {
tResources = new ArrayList(3);
resourceIdentifiers.put(identifier, tResources);
identifier.addIdentifierListener(this);
}
return tResources;
}
public java.util.List getKnownResourceUris() {
if (knownResourceUris == null)
initializeKnownResourceUris();
return knownResourceUris;
}
public boolean isShared() {
return registry.size() > 1;
}
/**
* @see ResourceStateInputProvider#cacheNonResourceValidateState(List)
*/
public void cacheNonResourceValidateState(List roNonResourceFiles) {
//do nothing
}
/**
* @see ResourceStateInputProvider#getNonResourceFiles()
*/
public List getNonResourceFiles() {
return null;
}
/**
* @see ResourceStateInputProvider#getNonResourceInconsistentFiles()
*/
public List getNonResourceInconsistentFiles() {
return null;
}
/**
* Gets the stateValidator.
*
* @return Returns a ResourceStateValidator
*/
public ResourceStateValidator getStateValidator() {
if (stateValidator == null)
stateValidator = createStateValidator();
return stateValidator;
}
/**
* Method createStateValidator.
*
* @return ResourceStateValidator
*/
private ResourceStateValidator createStateValidator() {
return new ResourceStateValidatorImpl(this);
}
/**
* @see ResourceStateValidator#checkActivation(ResourceStateValidatorPresenter)
*/
public void checkActivation(ResourceStateValidatorPresenter presenter) throws CoreException {
getStateValidator().checkActivation(presenter);
}
/**
* @see ResourceStateValidator#lostActivation(ResourceStateValidatorPresenter)
*/
public void lostActivation(ResourceStateValidatorPresenter presenter) throws CoreException {
getStateValidator().lostActivation(presenter);
}
/**
* @see ResourceStateValidator#validateState(ResourceStateValidatorPresenter)
*/
public IStatus validateState(ResourceStateValidatorPresenter presenter) throws CoreException {
return getStateValidator().validateState(presenter);
}
/**
* @see ResourceStateValidator#checkSave(ResourceStateValidatorPresenter)
*/
public boolean checkSave(ResourceStateValidatorPresenter presenter) throws CoreException {
return getStateValidator().checkSave(presenter);
}
/**
* @see ResourceStateValidator#checkReadOnly()
*/
public boolean checkReadOnly() {
return getStateValidator().checkReadOnly();
}
/**
* Return the ResourceSet from the Nature.
*
* @return org.eclipse.emf.ecore.resource.ResourceSet
*/
public ResourceSet getResourceSet() {
ResourceSet resourceSet = null;
if (getEmfContext() != null)
resourceSet = getEmfContext().getResourceSet();
return resourceSet;
}
protected void resourceIsLoadedChanged(Resource aResource, boolean oldValue, boolean newValue) {
if (!isReverting && hasListeners()) {
int eventCode = newValue ? EditModelEvent.LOADED_RESOURCE : EditModelEvent.UNLOADED_RESOURCE;
EditModelEvent evt = new EditModelEvent(eventCode, this);
evt.addResource(aResource);
notifyListeners(evt);
}
}
public Resource getResource(URI aUri) {
Resource res = getAndLoadLocalResource(aUri);
if (res == null)
res = WorkbenchResourceHelper.getOrCreateResource(aUri, getResourceSet());
if (res != null)
processResource(res);
return res;
}
protected void processResource(Resource aResource) {
if (aResource != null && !getResources().contains(aResource)) {
if (aResource instanceof ReferencedResource) {
access((ReferencedResource) aResource);
//We need a better way to pass this through the save options instead.
//We also need to make this dynamic based on the project target
((ReferencedResource) aResource).setFormat(CompatibilityXMIResource.FORMAT_MOF5);
} else if (aResource instanceof CompatibilityXMIResource) {
((CompatibilityXMIResource) aResource).setFormat(CompatibilityXMIResource.FORMAT_MOF5);
}
addResource(aResource);
}
}
protected void addResource(Resource aResource) {
getResources().add(aResource);
aResource.eAdapters().add(resourceAdapter);
}
/**
* Return a Resource for
*
* @aUri.
*/ // TODO The following method will only use the last segment when looking for a resource.
protected Resource getResource(List tResources, URI aUri) {
Resource resource;
for (int i = 0; i < tResources.size(); i++) {
resource = (Resource) tResources.get(i);
if (ExtendedEcoreUtil.endsWith(resource.getURI(), aUri))
return resource;
}
return null;
}
public Resource createResource(URI uri) {
Resource resource = getExistingOrCreateResource(uri);
processResource(resource);
return resource;
}
/**
* Get a cached Resource, either local or in the ResourceSet, before creating a Resource. This
* api handles the case that the Resource may be created during a demand load that failed.
*/
public Resource getExistingOrCreateResource(URI uri) {
Resource res = getAndLoadLocalResource(uri);
if (res == null)
res = WorkbenchResourceHelperBase.getExistingOrCreateResource(uri, getResourceSet());
return res;
}
/**
* Return a Resource for
*
* @aUri.
*/
protected Resource getAndLoadLocalResource(URI aUri) {
Resource resource = getLocalResource(aUri);
if (null != resource && !resource.isLoaded()) {
try {
resource.load(Collections.EMPTY_MAP); //reload it
} catch (IOException e) {
//Ignore
}
}
return resource;
}
/**
* Return a Resource for
*
* @aUri.
*/
protected Resource getLocalResource(URI aUri) {
return getResource(getResources(), aUri);
}
/*
* Return true if this is a ReadOnly EditModel or if we should only access unknown URIs as
* ReadOnly.
*/
protected boolean shouldAccessForRead(ReferencedResource aResource) {
return isReadOnly() || (accessAsReadForUnKnownURIs && !isInterrestedInResource(aResource));
}
/**
* Save only resources that need to be saved (i.e., no other references).
*/
public void resourceChanged(EditModelEvent anEvent) {
int code = anEvent.getEventCode();
switch (code) {
case EditModelEvent.REMOVED_RESOURCE : {
if (!isReverting && hasResourceReference(anEvent.getChangedResources()))
removeResources(anEvent.getChangedResources());
else
return;
break;
}
case EditModelEvent.ADDED_RESOURCE :
if (!processResourcesIfInterrested(anEvent.getChangedResources()))
return;
}
if (hasListeners()) {
anEvent.setEditModel(this);
notifyListeners(anEvent);
}
}
/**
* Return true if aResource is referenced by me.
*/
protected boolean hasResourceReference(Resource aResource) {
if (aResource != null)
return getResources().contains(aResource);
return false;
}
/**
* Return true if any Resource in the list of
*
* @resources is referenced by me.
*/
protected boolean hasResourceReference(List tResources) {
for (int i = 0; i < tResources.size(); i++) {
if (hasResourceReference((Resource) tResources.get(i)))
return true;
}
return false;
}
/**
* Remove reference to the Resource objects in
*
* @aList. This should be called when one or more Resource objects are removed from the
* ResourceSet without the reference count going to zero.
*/
protected void removeResources(List aList) {
Resource res;
for (int i = 0; i < aList.size(); i++) {
res = (Resource) aList.get(i);
if (removeResource(res) && res instanceof ReferencedResource)
removedResource((ReferencedResource) res);
}
}
private final void removedResource(ReferencedResource referencedResource) {
if (!isReadOnly() && referencedResource.wasReverted()) {
isReverting = true;
try {
reverted(referencedResource);
} finally {
isReverting = false;
}
}
}
protected boolean removeResource(URI uri) {
Resource res = getLocalResource(uri);
return removeResource(res);
}
/**
* Remove reference to the aResource.
*/
protected boolean removeResource(Resource aResource) {
if (aResource != null) {
aResource.eAdapters().remove(resourceAdapter);
return getResources().remove(aResource);
}
return false;
}
/**
* Subclasses should override to post process a removed ReferencedResource.
*
* @see J2EEEditModel#revertAllResources()
*/
protected void reverted(ReferencedResource revertedResource) {
revertAllResources();
}
protected void revertAllResources() {
List someResources = getSortedResources();
for (int i = 0; i < someResources.size(); i++)
((Resource) someResources.get(i)).unload();
getResources().removeAll(someResources);
for (int i = 0; i < someResources.size(); i++)
((Resource) someResources.get(i)).eAdapters().remove(resourceAdapter);
}
/**
* group the resources by XMI first, then XML
*/
protected List getSortedResources() {
List theResources = getResources();
int size = theResources.size();
if (size == 0)
return Collections.EMPTY_LIST;
Resource[] sorted = new Resource[size];
int xmlInsertPos = size - 1;
int xmiInsertPos = 0;
Resource res = null;
for (int i = 0; i < size; i++) {
res = (Resource) theResources.get(i);
if (res instanceof TranslatorResource)
sorted[xmlInsertPos--] = res;
else
sorted[xmiInsertPos++] = res;
}
return Arrays.asList(sorted);
}
/**
* Process Resources that we are interrested in.
*/
protected boolean processResourcesIfInterrested(List someResources) {
int size = someResources.size();
Resource res;
boolean processed = false;
for (int i = 0; i < size; i++) {
res = (Resource) someResources.get(i);
if ((res != null) && (isInterrestedInResource(res))) {
processResource(res);
processed = true;
}
}
return processed;
}
public EMFWorkbenchContext getEmfContext() {
if (isDisposed())
throw new IllegalStateException("Edit Model already disposed");
if (emfContext == null)
throw new IllegalStateException("EMF context is null");
return emfContext;
}
private boolean isDisposed() {
return disposed;
}
public IProject getProject() {
return project;
}
/**
* This method should only be called by the EMFWorkbenchContext.
*/
public void access(Object accessorKey) {
registry.access(accessorKey);
}
/**
* Access
*
* @aResource for read or write.
*/
protected void access(ReferencedResource aResource) {
if (shouldAccessForRead(aResource))
aResource.accessForRead();
else
aResource.accessForWrite();
}
/**
* This method should be called from each client when they are finished working with the
* EditModel.
*/
public void releaseAccess(Object accessorKey) {
if (!isShared())
releaseResources();
registry.release(accessorKey);
if (!isDisposing() && registry.size() == 0) {
dispose();
}
}
/**
* Release each of the referenced resources.
*/
protected void release(Resource aResource) {
removeResource(aResource);
if (aResource != null && aResource instanceof ReferencedResource)
release((ReferencedResource) aResource);
}
/**
* Release each of the referenced resources.
*/
protected void release(ReferencedResource aResource) {
if (isReadOnly())
aResource.releaseFromRead();
else
aResource.releaseFromWrite();
}
/**
* Release each of the referenced resources.
*/
protected void releaseResources() {
List tResources = getSortedResources();
Resource resource;
for (int i = 0; i < tResources.size(); i++) {
resource = (Resource) tResources.get(i);
release(resource);
}
}
public void deleteResource(Resource aResource) {
if (aResource == null || resources == null || !getResources().contains(aResource))
return;
getResourcesTargetedForTermination().add(aResource);
}
/**
* @return
*/
protected List getResourcesTargetedForTermination() {
if (resourcesTargetedForTermination == null)
resourcesTargetedForTermination = new ArrayList(5);
return resourcesTargetedForTermination;
}
/**
* Remove my reference to aResource, remove it from the ResourceSet, and delete its file from
* the Workbench. This only happens if there is currently a reference to
*
* @aResource.
*/
public void primDeleteResource(Resource aResource) {
if (primFlushResource(aResource)) {
try {
getEmfContext().deleteResource(aResource);
} catch (CoreException e) {
//what should we do here?
}
if (hasListeners()) {
EditModelEvent event = new EditModelEvent(EditModelEvent.REMOVED_RESOURCE, this);
event.addResource(aResource);
notifyListeners(event);
}
}
}
/**
* Remove my reference to aResource and remove it from the ResourceSet.
*/
public void flushResource(Resource aResource) {
if (primFlushResource(aResource)) {
if (hasListeners()) {
EditModelEvent event = new EditModelEvent(EditModelEvent.REMOVED_RESOURCE, this);
event.addResource(aResource);
notifyListeners(event);
}
}
}
public Set getAffectedFiles() {
Set aSet = new HashSet();
List mofResources = getResources();
for (int i = 0; i < mofResources.size(); i++) {
Resource aResource = (Resource) mofResources.get(i);
IFile output = WorkbenchResourceHelper.getFile(aResource);
if (output != null)
aSet.add(output);
}
return aSet;
}
protected List resetKnownResourceUris() {
initializeKnownResourceUris();
return knownResourceUris;
}
/**
* Insert the method's description here. Creation date: (4/11/2001 4:14:26 PM)
*
* @return java.util.List
*/
public List getResources() {
if (resources == null)
resources = new ArrayList(5);
return resources;
}
public String[] getResourceURIs() {
return getResourceURIs(false);
}
public String[] getResourceURIs(boolean onlyDirty) {
List list = getResources();
int dirtyCount = 0;
String[] uris = new String[list.size()];
Resource res;
for (int i = 0; i < list.size(); i++) {
res = (Resource) list.get(i);
if (!onlyDirty)
uris[i] = res.getURI().toString();
else if (res.isModified()) {
uris[i] = res.getURI().toString();
dirtyCount++;
}
}
if (onlyDirty && dirtyCount > 0) {
String[] dirty = new String[dirtyCount];
int j = 0;
for (int i = 0; i < uris.length; i++) {
if (uris[i] != null) {
dirty[j] = uris[i];
j++;
}
}
uris = dirty;
}
return uris;
}
/**
* Returns the first element in the extent of the resource; logs an error and returns null if
* the extent is empty
*/
public static EObject getRoot(Resource aResource) {
EList extent = aResource.getContents();
if (extent.size() < 1)
return null;
return (EObject) extent.get(0);
}
/**
* Handle the failure of
*
* @aResource.
*/
protected void handleSaveFailed(Resource aResource, Exception e) {
aResource.setModified(true);
if (isFailedWriteFileFailure(e) && shouldSaveReadOnly(aResource))
saveResource(aResource);
else
primHandleSaveFailed(aResource, e);
}
/**
* Return whether any of my resources has been modified.
*/
protected boolean isAnyResourceDirty() {
List list = getResources();
for (int i = 0; i < list.size(); i++) {
if (((Resource) list.get(i)).isModified())
return true;
}
return false;
}
/**
* Return whether a save is needed on the CommandStack
*/
public boolean isDirty() {
return isAnyResourceDirty();
}
protected boolean isFailedWriteFileFailure(Exception ex) {
return SaveHandlerHeadless.isFailedWriteFileFailure(ex);
}
/**
* Return true if you can only read the resources and not write.
*/
public boolean isReadOnly() {
return readOnly;
}
protected boolean isReadOnlyFailure(Exception ex) {
return false;
}
public boolean hasReadOnlyResource() {
try {
List list = getResources();
int size = list.size();
Resource res = null;
IFile file;
for (int i = 0; i < size; i++) {
res = (Resource) list.get(i);
file = WorkbenchResourceHelper.getFile(res);
if (file != null && file.isReadOnly())
return true;
}
} catch (NullPointerException e) {
System.out.println(e);
}
return false;
}
/**
* @deprecated use createResource(URI) instead
*/
public Resource makeResource(String aUri) {
return createResource(URI.createURI(aUri));
}
/**
* Return whether any of my resources has a reference count of one and it has been modified.
*/
public boolean needsToSave() {
return !isShared() && isDirty();
}
/**
* Remove my reference to aResource and remove it from the ResourceSet. Return true if aResource
* was removed.
*/
protected boolean primFlushResource(Resource aResource) {
if (aResource != null && hasResourceReference(aResource)) {
removeResource(aResource);
removeResourceSetResource(aResource);
return true;
}
return false;
}
/**
* Handle the failure of
*
* @aResource.
*/
protected void primHandleSaveFailed(Resource aResource, Exception e) {
org.eclipse.jem.util.logger.proxy.Logger.getLogger().logError(e);
Exception nested = null;
if (e instanceof IWFTWrappedException)
nested = ((IWFTWrappedException) e).getNestedException();
else
nested = e;
throw new SaveFailedException(EMFWorkbenchResourceHandler.getString("An_error_occurred_while_sa_ERROR_"), nested); //$NON-NLS-1$ = "An error occurred while saving."
}
/**
* Prompt for a save.
*/
protected boolean promptToSave(IOperationHandler operationHandler) {
if (operationHandler == null)
return false;
return operationHandler.canContinue(EMFWorkbenchResourceHandler.getString("The_following_resources_ne_UI_"), getResourceURIs(true)); //$NON-NLS-1$ = "The following resources need to be saved but are currently shared, do you want to save now?"
}
/**
* This will force all of the referenced Resources to be saved.
*/
public void primSave(IProgressMonitor monitor) {
if (isReadOnly())
return; //do nothing
deleteResourcesIfNecessary();
Resource resource;
if (getResources().isEmpty())
return; //nothing to save
List localResources = getSortedResources();
for (int i = 0; i < localResources.size(); i++) {
resource = (Resource) localResources.get(i);
saveResource(resource);
}
getCommandStack().saveIsDone();
if (hasListeners()) {
EditModelEvent event = new EditModelEvent(EditModelEvent.SAVE, this);
notifyListeners(event);
}
}
/**
*
*/
protected void deleteResourcesIfNecessary() {
if (resourcesTargetedForTermination == null || resourcesTargetedForTermination.size() == 0)
return;
Resource deadres = null;
for (int i = 0; i < getResourcesTargetedForTermination().size(); i++) {
deadres = (Resource) getResourcesTargetedForTermination().get(i);
primDeleteResource(deadres);
getResources().remove(deadres);
getResourcesTargetedForTermination().remove(deadres);
}
}
/**
* Save
*
* @aResource.
*/
protected void primSaveResource(Resource aResource) throws Exception {
if (aResource.isModified())
aResource.save(Collections.EMPTY_MAP);
}
/**
* Process resources that have already been loaded.
*/
protected void processLoadedResources() {
List loaded = getResourceSet().getResources();
if (!loaded.isEmpty())
processResourcesIfInterrested(loaded);
}
private void processPreloadResources() {
for (int i = 0; i < preloadResourceUris.size(); i++) {
URI uri = (URI) preloadResourceUris.get(i);
getResource(uri);
}
}
/**
* Remove aResource from my ResourceSet. Return true if aResource was removed.
*/
protected boolean removeResourceSetResource(Resource aResource) {
aResource.eSetDeliver(false);
aResource.unload();
aResource.eSetDeliver(true);
return getResourceSet().getResources().remove(aResource);
}
protected void saveResource(Resource resource) {
try {
primSaveResource(resource);
} catch (Exception e) {
handleSaveFailed(resource, e);
}
}
/**
* Should the resources be saved.
*/
protected boolean shouldSave() {
return !isReadOnly() && !isShared();
}
/**
* Should the resources be saved.
*/
protected boolean shouldSave(IOperationHandler operationHandler) {
return shouldSave() || promptToSave(operationHandler);
}
protected boolean shouldSaveReadOnly(Resource aResource) {
IFile aFile = WorkbenchResourceHelper.getFile(aResource);
if (aFile == null || !aFile.isReadOnly())
return false;
return getSaveHandler().shouldContinueAndMakeFileEditable(aFile);
}
/**
* Force all of the known resource URIs to be loaded if they are not already.
*/
public void forceLoadKnownResources() {
List uris = getKnownResourceUris();
URI uri = null;
for (int i = 0; i < uris.size(); i++) {
uri = (URI) uris.get(i);
getResource(uri);
}
}
/**
* This method should be called when you want to extend this edit model to handle a resource
* with a URI equal to <code>aRelativeURI</code>.
*/
public void manageExtensionResourceURI(String aRelativeURI) {
if (aRelativeURI != null && aRelativeURI.length() > 0) {
URI uri = URI.createURI(aRelativeURI);
if (!isInterrestedInResourceUri(uri)) {
getKnownResourceUris().add(uri);
//Process the resource if it is already loaded.
try {
Resource res = getEmfContext().getResource(uri);
if (res != null)
processResource(res);
} catch (Exception e) {
//Ignore
}
}
}
}
/**
* Get a cached Resource or try to load the Resource prior to creating a Resource. This api
* handles the case that the Resource may be created during the load.
*/
public Resource getOrCreateResource(URI uri) {
return getResource(uri);
}
/**
* @return boolean
*/
public boolean isAccessAsReadForUnKnownURIs() {
return accessAsReadForUnKnownURIs;
}
/**
* Use this api to indicate that you want all unknown Resources to be accessed for ReadOnly.
*
* @param b
*/
public void setAccessAsReadForUnKnownURIs(boolean b) {
accessAsReadForUnKnownURIs = b;
}
public String toString() {
StringBuffer buffer = new StringBuffer(getClass().getName());
buffer.append(": "); //$NON-NLS-1$
if (isReadOnly())
buffer.append(" R = "); //$NON-NLS-1$
else
buffer.append(" W = "); //$NON-NLS-1$
buffer.append(getRegistry().size());
buffer.append("[ID: \""); //$NON-NLS-1$
buffer.append(getEditModelID());
buffer.append("\" Known Resources: ["); //$NON-NLS-1$
List uris = getKnownResourceUris();
if (uris != null) {
int i = 0;
for (i = 0; i < (uris.size() - 1); i++)
buffer.append(uris.get(i) + ", "); //$NON-NLS-1$
buffer.append(uris.get(i));
buffer.append("]"); //$NON-NLS-1$
} else
buffer.append("none"); //$NON-NLS-1$
buffer.append("]"); //$NON-NLS-1$
return buffer.toString();
}
public Reference getReference() {
if (reference == null)
reference = new Reference();
return reference;
}
/**
* @return
*/
protected ClientAccessRegistry getRegistry() {
return registry;
}
public class Reference {
protected String tostring = null;
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
public String toString() {
if (tostring == null) {
StringBuffer result = new StringBuffer("EditModel.Reference ["); //$NON-NLS-1$
result.append("{"); //$NON-NLS-1$
result.append(getEditModelID());
result.append("} {"); //$NON-NLS-1$
result.append(getProject().getName());
result.append("}]"); //$NON-NLS-1$
tostring = result.toString();
}
return tostring;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return toString().hashCode();
}
}
/**
* Subclasses can override - by default this will return the first root object from the first
* resource referenced by the known resource URIs for this EditModel
*
* @return an EObject or Null
*/
public EObject getPrimaryRootObject() {
Resource res = getPrimaryResource();
if (res == null || res.getContents().isEmpty())
return null;
return (EObject) res.getContents().get(0);
}
/**
* Subclasses can override - by default this will return the first resource referenced by the
* known resource URIs for this EditModel
*
* @return
*/
public Resource getPrimaryResource() {
if (knownResourceUris == null)
getKnownResourceUris();
if (knownResourceUris == null || knownResourceUris.isEmpty())
return null;
URI uri = (URI) knownResourceUris.get(0);
return getResource(uri);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.wst.common.frameworks.internal.enablement.IEnablementIdentifierListener#identifierChanged(org.eclipse.wst.common.frameworks.internal.enablement.EnablementIdentifierEvent)
*/
public void identifierChanged(EnablementIdentifierEvent evt) {
if (evt.hasEnabledChanged()) {
EditModelEvent editModelEvent = new EditModelEvent(EditModelEvent.KNOWN_RESOURCES_ABOUT_TO_CHANGE, this);
notifyListeners(editModelEvent);
IEnablementIdentifier id = evt.getIdentifier();
if (id.isEnabled())
addKnownResources(id);
else
removeKnownResources(id);
editModelEvent = new EditModelEvent(EditModelEvent.KNOWN_RESOURCES_CHANGED, this);
notifyListeners(editModelEvent);
}
}
private void removeKnownResources(IEnablementIdentifier id) {
List editModelResources = getEditModelResources(id);
EditModelResource editModelResource = null;
ResourceSetWorkbenchEditSynchronizer sync = (ResourceSetWorkbenchEditSynchronizer) getResourceSetSynchronizer();
for (int i = 0; i < editModelResources.size(); i++) {
editModelResource = (EditModelResource) editModelResources.get(i);
if (editModelResource.isAutoLoad() && sync != null) {
sync.disableAutoload(editModelResource.getURI());
preloadResourceUris.remove(editModelResource.getURI());
}
knownResourceUris.remove(editModelResource.getURI());
removeResource(editModelResource.getURI());
}
}
private void addKnownResources(IEnablementIdentifier id) {
List editModelResources = getEditModelResources(id);
EditModelResource editModelResource = null;
ResourceSetWorkbenchEditSynchronizer sync = (ResourceSetWorkbenchEditSynchronizer) getResourceSetSynchronizer();
for (int i = 0; i < editModelResources.size(); i++) {
editModelResource = (EditModelResource) editModelResources.get(i);
if (editModelResource.isAutoLoad() && sync != null) {
sync.enableAutoload(editModelResource.getURI());
preloadResourceUris.add(editModelResource.getURI());
getResource(editModelResource.getURI());
}
knownResourceUris.add(editModelResource.getURI());
}
}
/**
* @return Returns the params.
*/
public Map getParams() {
return params;
}
/**
* @param params
* The params to set.
*/
public void setParams(Map params) {
this.params = params;
}
}