diff options
-rw-r--r-- | plugins/infra/org.eclipse.papyrus.infra.ui.resources/src/org/eclipse/papyrus/infra/ui/resources/refactoring/RenameModelChange.java | 1005 |
1 files changed, 508 insertions, 497 deletions
diff --git a/plugins/infra/org.eclipse.papyrus.infra.ui.resources/src/org/eclipse/papyrus/infra/ui/resources/refactoring/RenameModelChange.java b/plugins/infra/org.eclipse.papyrus.infra.ui.resources/src/org/eclipse/papyrus/infra/ui/resources/refactoring/RenameModelChange.java index c08d89ead8e..bf54613712d 100644 --- a/plugins/infra/org.eclipse.papyrus.infra.ui.resources/src/org/eclipse/papyrus/infra/ui/resources/refactoring/RenameModelChange.java +++ b/plugins/infra/org.eclipse.papyrus.infra.ui.resources/src/org/eclipse/papyrus/infra/ui/resources/refactoring/RenameModelChange.java @@ -1,497 +1,508 @@ -/*****************************************************************************
- * Copyright (c) 2009 Atos Origin - CEA LIST.
- *
- *
- * 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:
- * <a href="mailto:thomas.szadel@atosorigin.com">Thomas Szadel</a> - Initial API and implementation
- * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr
- *
- *****************************************************************************/
-package org.eclipse.papyrus.infra.ui.resources.refactoring;
-
-import static org.eclipse.papyrus.infra.ui.resources.Activator.log;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.NullProgressMonitor;
-import org.eclipse.core.runtime.OperationCanceledException;
-import org.eclipse.core.runtime.Path;
-import org.eclipse.core.runtime.SubProgressMonitor;
-import org.eclipse.emf.common.notify.Notifier;
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.EStructuralFeature;
-import org.eclipse.emf.ecore.EStructuralFeature.Setting;
-import org.eclipse.emf.ecore.resource.Resource;
-import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
-import org.eclipse.emf.ecore.util.EcoreUtil;
-import org.eclipse.emf.transaction.RecordingCommand;
-import org.eclipse.emf.transaction.TransactionalEditingDomain;
-import org.eclipse.ltk.core.refactoring.Change;
-import org.eclipse.ltk.core.refactoring.RefactoringStatus;
-import org.eclipse.ltk.core.refactoring.resource.RenameResourceChange;
-import org.eclipse.papyrus.infra.core.editor.IMultiDiagramEditor;
-import org.eclipse.papyrus.infra.core.modelsetquery.IModelSetQueryAdapter;
-import org.eclipse.papyrus.infra.core.modelsetquery.ModelSetQuery;
-import org.eclipse.papyrus.infra.core.resource.ModelSet;
-import org.eclipse.papyrus.infra.core.resource.ModelsReader;
-import org.eclipse.papyrus.infra.core.resource.ReadOnlyAxis;
-import org.eclipse.papyrus.infra.core.resource.sasheditor.DiModelUtils;
-import org.eclipse.papyrus.infra.core.services.ServiceException;
-import org.eclipse.papyrus.infra.core.utils.EditorUtils;
-import org.eclipse.papyrus.infra.emf.readonly.ReadOnlyManager;
-import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
-import org.eclipse.papyrus.infra.services.controlmode.mm.history.ControledResource;
-import org.eclipse.papyrus.infra.services.controlmode.mm.history.historyPackage;
-import org.eclipse.papyrus.infra.ui.resources.Activator;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.part.FileEditorInput;
-
-/**
- * Rename the model.<BR>
- * <b>Note</b>: That change should be called inside a rename operation as it assumes that a {@link RenameResourceChange} occured.
- *
- * @author tszadel
- *
- */
-public class RenameModelChange extends Change {
-
- private final Map<URI, URI> uriMap = new HashMap<URI, URI>();
-
- private final IFile oldFile;
-
- private final IFile newFile;
-
- private final Set<IResource> relatedFiles;
-
- private final Collection<? extends IResource> impacted;
-
- private TransactionalEditingDomain domain;
-
- private ModelSet resourceSet;
-
- private List<IMultiDiagramEditor> openedEditors;
-
- private boolean isUndoOperation;
-
- /**
- * Constructor.
- *
- * @param resourceSet
- * The resource set being changed.
- * @param oldFile
- * The old file.
- * @param newFile
- * The new file.
- * @param impacted
- */
- public RenameModelChange(IFile oldFile, IFile newFile, Collection<? extends IResource> impacted) {
- this.oldFile = oldFile;
- this.newFile = newFile;
- this.impacted = impacted;
-
- IPath newPathWithoutExt = newFile.getFullPath().removeFileExtension();
-
- // Create the map of URI that are being modified in the resource set
- relatedFiles = ModelParticipantHelpers.getRelatedFiles(oldFile);
- relatedFiles.add(oldFile);
- for(IResource file : relatedFiles) {
- IPath path = file.getFullPath();
- URI oldURI = getPlatformURI(path);
- URI newURI = getPlatformURI(newPathWithoutExt.addFileExtension(path.getFileExtension()));
- uriMap.put(oldURI, newURI);
- }
- }
-
- /**
- * Overrides getModifiedElement.
- *
- * {@inheritDoc}
- *
- * @see org.eclipse.ltk.core.refactoring.Change#getModifiedElement()
- */
- @Override
- public Object getModifiedElement() {
- return oldFile;
- }
-
- /**
- * Overrides getName.
- *
- * {@inheritDoc}
- *
- * @see org.eclipse.ltk.core.refactoring.Change#getName()
- */
- @Override
- public String getName() {
- return Messages.bind(Messages.RenameModelChange_Name, oldFile.getName());
- }
-
- /**
- * Overrides initializeValidationData.
- *
- * {@inheritDoc}
- *
- * @see org.eclipse.ltk.core.refactoring.Change#initializeValidationData(org.eclipse.core.runtime.IProgressMonitor)
- */
- @Override
- public void initializeValidationData(IProgressMonitor pm) {
- // Nothing
- }
-
- /**
- * Overrides isValid.
- *
- * {@inheritDoc}
- *
- * @see org.eclipse.ltk.core.refactoring.Change#isValid(org.eclipse.core.runtime.IProgressMonitor)
- */
- @Override
- public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException, OperationCanceledException {
- // That change assumes that the model resource has already been renamed
- // So, the first thing to do is to get the new file, to restore it in order
- // to change all the resources.
- pm.subTask(Messages.RenameModelChange_LoadingEMF);
-
- pm.subTask(Messages.RenameModelChange_DaveDirtyEditor);
- // We need to get the current workbench... so we have to use the UI-Thread!
- openedEditors = new ArrayList<IMultiDiagramEditor>();
- Display.getDefault().syncExec(new Runnable() {
-
- public void run() {
- IMultiDiagramEditor[] multiEditors = EditorUtils.getRelatedEditors(oldFile);
- if(multiEditors != null && multiEditors.length > 0) {
- for(IMultiDiagramEditor editor : multiEditors) {
- if(editor.isDirty()) {
- editor.doSave(new NullProgressMonitor());
- }
- openedEditors.add(editor);
- }
- }
- }
- });
- pm.worked(10);
-
- /*
- * Load ModelSet
- */
- resourceSet = new ModelSet();
- try {
- ModelsReader reader = new ModelsReader();
- reader.readModel(resourceSet);
- resourceSet.loadModels(oldFile);
- for(IResource r : impacted) {
- if(r instanceof IFile) {
- IFile file = (IFile)r;
- try {
- resourceSet.getResource(URI.createPlatformResourceURI(file.getFullPath().toString(), true), true);
- } catch (Exception e) {
- // to avoid load errors
- }
- }
- }
- } catch (Exception e) {
- Activator.log.error(e);
- }
-
- // Force EMF resolve and load all the resources
- try {
- EcoreUtil.resolveAll(resourceSet);
- } catch (Exception ex) {
- // the resolve all does not have to break the operation
- }
-
- pm.worked(4);
- domain = resourceSet.getTransactionalEditingDomain();
-
- // TODO improve when impact analysis will be effective
- return manageResourceSet(pm, resourceSet);
- }
-
- /**
- * Get a platform resource URI of the given path
- *
- * @param path
- * the path
- * @return the uri
- */
- private URI getPlatformURI(IPath path) {
- return URI.createPlatformResourceURI(path.toString(), true);
- }
-
-
- /**
- * Overrides perform.
- *
- * {@inheritDoc}
- *
- * @see org.eclipse.ltk.core.refactoring.Change#perform(org.eclipse.core.runtime.IProgressMonitor)
- */
- @Override
- public Change perform(IProgressMonitor pm) throws CoreException {
-
- String lMsg = Messages.bind(Messages.RenameModelChange_Change, oldFile.getName(), newFile.getName());
- isUndoOperation = oldFile.exists() && !newFile.exists();
-
- if(!isUndoOperation) {
- //The file has already been renamed. Undo the rename change, then do the full refactoring in the resource set
- newFile.move(oldFile.getFullPath(), true, new SubProgressMonitor(pm, 1));
- }
-
- Set<IResource> revertImpactedFiles = getRevertImpactedResources();
-
- pm.beginTask(lMsg, 30);
- try {
- doRun(pm, resourceSet, domain);
- // Now, save all the resources
- pm.subTask(Messages.RenameModelChange_savingResource);
- for(Resource res : resourceSet.getResources()) {
- if(res.getURI().isPlatformResource()) {
- try {
- res.save(null);
- } catch (Exception e) {
- log.error(Messages.bind(Messages.RenameModelChange_ErrorLoading, res.getURI()), e);
- }
- }
- }
- pm.worked(5);
-
- // Do not forget to unload all the resources to avoid memory leak
- pm.subTask(Messages.RenameModelChange_Unloading);
- resourceSet.unload();
- pm.worked(1);
-
- // Now, notify the editor of the change
- if(!openedEditors.isEmpty()) {
- Display.getDefault().syncExec(new Runnable() {
-
- public void run() {
- // Get the DI file as the rename could occur on any model's file.
- IFile newDiFile = DiModelUtils.getRelatedDiFile(newFile);
- for(IMultiDiagramEditor editor : openedEditors) {
- try {
- ModelSet diRes = editor.getServicesRegistry().getService(ModelSet.class);
- if(diRes != null) {
- diRes.saveAs(newFile.getFullPath());
- }
- editor.setEditorInput(new FileEditorInput(newDiFile));
-
- } catch (ServiceException e) {
- log.error(e);
- } catch (IOException e) {
- log.error(e);
- }
- }
- }
- });
- }
-
- // Then, remove the old model files
- pm.subTask(Messages.RenameModelChange_RemoveOldFile);
- for(IResource file : relatedFiles) {
- if(file.exists()) {
- file.delete(true, new NullProgressMonitor());
- }
- }
- pm.worked(4);
-
- RenameModelChange undoChange = new RenameModelChange(newFile, oldFile, revertImpactedFiles);
-
- if(isUndoOperation) {
- //Restore the expected state for the basic rename change
- newFile.move(oldFile.getFullPath(), true, new SubProgressMonitor(pm, 1));
- }
-
- //Invert the change
- return undoChange;
- } finally {
- pm.done();
- }
- }
-
- private Set<IResource> getRevertImpactedResources() {
- Set<IResource> result = new HashSet<IResource>();
-
- Set<IResource> relatedFiles = ModelParticipantHelpers.getRelatedFiles(oldFile);
- relatedFiles.add(oldFile);
-
- for(IResource initialResource : impacted) {
- if(relatedFiles.contains(initialResource)) {
- IResource invertedResource = invertFileName(initialResource);
- result.add(invertedResource); //Participant model (the file is renamed)
- } else {
- result.add(initialResource); //Client model (only links are modified)
- }
- }
-
- return result;
- }
-
- private IResource invertFileName(IResource initialResource) {
- String newName;
-
- IPath pathWithoutExtension = newFile.getFullPath().removeFileExtension();
- IPath pathWithExtension = pathWithoutExtension.addFileExtension(initialResource.getFileExtension());
- newName = pathWithExtension.lastSegment();
-
- IFile newFile = oldFile.getParent().getFile(new Path(newName));
-
- return newFile;
- }
-
- private RefactoringStatus manageResourceSet(final IProgressMonitor pm, final ModelSet resourceSet) {
-
- final Collection<Resource> readOnlies = new HashSet<Resource>();
- // for each object of the resources renamed, the refactor will search if a resource is read only or not
- domain.getCommandStack().execute(new RecordingCommand(domain) {
-
- @Override
- protected void doExecute() {
-
- // got though resources, to find if a resources that reference one of uri map is read only
- for(URI uri : uriMap.keySet()) {
- Resource r = resourceSet.getResource(uri, false);
- ECrossReferenceAdapter adapter = ECrossReferenceAdapter.getCrossReferenceAdapter(resourceSet);
- if(adapter == null) {
- adapter = new ECrossReferenceAdapter();
- adapter.setTarget(resourceSet);
- }
- if(r != null) {
- for(Iterator<EObject> i = EcoreUtil.getAllProperContents(r, false); i.hasNext();) {
- EObject e = i.next();
-
- //look for all references where e is playing
- Collection<Setting> references = adapter.getInverseReferences(e);
- for(Setting s : references) {
- // get the EObject that play with e
- EObject eObject = s.getEObject();
-
- //this is the same resource --> not interesting
- if((eObject.eResource() != null) && !(eObject.eResource().equals(e.eResource()))) {
- //this is a external resource that references uri map
- //if not not interesting
- EStructuralFeature eFeature = s.getEStructuralFeature();
-
- if((!eFeature.isDerived()) && (eObject.eClass().getEAllStructuralFeatures().contains(eFeature))) {
- if(eObject.eResource() != null && EMFHelper.isReadOnly(eObject.eResource(), domain)) {
- boolean isWritable = EMFHelper.canMakeWritable(eObject.eResource(), domain);
- if(isWritable) {
- readOnlies.add(eObject.eResource());
- }
- }
- }
- }
- }
- }
- }
- }
- }
-
- });
- // if read only => error to the user
- if(!readOnlies.isEmpty()) {
- ReadOnlyManager readOnlyManager = new ReadOnlyManager(domain);
- ArrayList<URI> uris = new ArrayList<URI>();
- for(Iterator<Resource> iterator = readOnlies.iterator(); iterator.hasNext();) {
- Resource r = (Resource)iterator.next();
- uris.add(r.getURI());
- }
-
- readOnlyManager.makeWritable(ReadOnlyAxis.anyAxis(), uris.toArray(new URI[uris.size()]));
- }
- return new RefactoringStatus();
-
- }
-
- private void doRun(final IProgressMonitor pm, final ModelSet resourceSet, final TransactionalEditingDomain domain) {
- domain.getCommandStack().execute(new RecordingCommand(domain) {
-
- @Override
- protected void doExecute() {
- // Manage Controlled map to ensure consistent history
- // TODO change this code when history will be useless
- URI modifiedURI = URI.createPlatformResourceURI(oldFile.getFullPath().removeFileExtension().toString(), true);
- IModelSetQueryAdapter controledResourcesAdapter = ModelSetQuery.getExistingTypeCacheAdapter(resourceSet);
- if(controledResourcesAdapter != null) {
- EObject first = null;
- for(Iterator<Notifier> i = resourceSet.getAllContents(); i.hasNext();) {
- Notifier n = i.next();
- if(n instanceof EObject) {
- first = (EObject)n;
- break;
- }
- }
- if(first != null) {
- Collection<EObject> resources = null;
- try {
- resources = controledResourcesAdapter.getReachableObjectsOfType(first, historyPackage.Literals.CONTROLED_RESOURCE);
- } catch (RuntimeException e) {
- // in case of errors integrity must be valid
- // even performances are bad
- resources = new LinkedList<EObject>();
- for(int i = 0; i < resourceSet.getResources().size(); i++) {
- Resource r = resourceSet.getResources().get(i);
- for(Iterator<EObject> it = r.getAllContents(); it.hasNext();) {
- EObject tmp = it.next();
- if(tmp instanceof ControledResource) {
- ControledResource controled = (ControledResource)tmp;
- resources.add(controled);
- }
- }
- }
- }
- for(EObject e : resources) {
- if(e instanceof ControledResource) {
- ControledResource controled = (ControledResource)e;
- URI baseURI = URI.createURI(e.eResource().getURI().trimSegments(1).trimFragment().toString() + "/");
- URI resolvedURI = URI.createURI(controled.getResourceURL()).resolve(baseURI);
- if(resolvedURI.trimFileExtension().equals(modifiedURI.trimFileExtension())) {
- String ext = resolvedURI.fileExtension();
- URI newURL = URI.createURI(resolvedURI.trimSegments(1).toString() + "/" + newFile.getFullPath().removeFileExtension().lastSegment().toString() + "." + ext);
- controled.setResourceURL(newURL.deresolve(baseURI).toString());
- }
- }
- }
- }
- }
- }
- });
- // Change the uri of the files
- pm.subTask(Messages.RenameModelChange_ModifyURI);
- for(Resource res : resourceSet.getResources()) {
- if(res.getURI().isPlatformResource()) {
- URI newURI = uriMap.get(res.getURI());
- if(newURI != null) {
- if(log.isDebugEnabled()) {
- log.debug(Messages.bind(Messages.RenameModelChange_6, Arrays.asList(res.getURI(), newURI)));
- }
- res.setURI(newURI);
- }
- }
-
- }
- pm.worked(5);
-
-
- }
-}
+/***************************************************************************** + * Copyright (c) 2009 Atos Origin - CEA LIST. + * + * + * 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: + * <a href="mailto:thomas.szadel@atosorigin.com">Thomas Szadel</a> - Initial API and implementation + * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr + * Benoit Maggi (CEA LIST) benoit.maggi@cea.fr - Save only modified resources and delete only old version of renamed resources + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.resources.refactoring; + +import static org.eclipse.papyrus.infra.ui.resources.Activator.log; + +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.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.EStructuralFeature.Setting; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.URIConverter; +import org.eclipse.emf.ecore.util.ECrossReferenceAdapter; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.ltk.core.refactoring.resource.RenameResourceChange; +import org.eclipse.papyrus.infra.core.editor.IMultiDiagramEditor; +import org.eclipse.papyrus.infra.core.modelsetquery.IModelSetQueryAdapter; +import org.eclipse.papyrus.infra.core.modelsetquery.ModelSetQuery; +import org.eclipse.papyrus.infra.core.resource.ModelSet; +import org.eclipse.papyrus.infra.core.resource.ModelsReader; +import org.eclipse.papyrus.infra.core.resource.ReadOnlyAxis; +import org.eclipse.papyrus.infra.core.resource.sasheditor.DiModelUtils; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.utils.EditorUtils; +import org.eclipse.papyrus.infra.emf.readonly.ReadOnlyManager; +import org.eclipse.papyrus.infra.emf.utils.EMFHelper; +import org.eclipse.papyrus.infra.emf.utils.ResourceUtils; +import org.eclipse.papyrus.infra.services.controlmode.mm.history.ControledResource; +import org.eclipse.papyrus.infra.services.controlmode.mm.history.historyPackage; +import org.eclipse.papyrus.infra.ui.resources.Activator; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.part.FileEditorInput; + +/** + * Rename the model.<BR> + * <b>Note</b>: That change should be called inside a rename operation as it assumes that a {@link RenameResourceChange} occured. + * + * @author tszadel + * + */ +public class RenameModelChange extends Change { + + private final Map<URI, URI> uriMap = new HashMap<URI, URI>(); + + private final IFile oldFile; + + private final IFile newFile; + + private final Set<IResource> relatedFiles; + + private final Collection<? extends IResource> impacted; + + private TransactionalEditingDomain domain; + + private ModelSet resourceSet; + + private List<IMultiDiagramEditor> openedEditors; + + private boolean isUndoOperation; + + /** + * Constructor. + * + * @param resourceSet + * The resource set being changed. + * @param oldFile + * The old file. + * @param newFile + * The new file. + * @param impacted + */ + public RenameModelChange(IFile oldFile, IFile newFile, Collection<? extends IResource> impacted) { + this.oldFile = oldFile; + this.newFile = newFile; + this.impacted = impacted; + + IPath newPathWithoutExt = newFile.getFullPath().removeFileExtension(); + + // Create the map of URI that are being modified in the resource set + relatedFiles = ModelParticipantHelpers.getRelatedFiles(oldFile); + relatedFiles.add(oldFile); + + for(IResource iResource : relatedFiles) { + IPath path = iResource.getFullPath(); + URI oldURI = getPlatformURI(path); + URI newURI = getPlatformURI(newPathWithoutExt.addFileExtension(path.getFileExtension())); + uriMap.put(oldURI, newURI); + } + } + + /** + * Overrides getModifiedElement. + * + * {@inheritDoc} + * + * @see org.eclipse.ltk.core.refactoring.Change#getModifiedElement() + */ + @Override + public Object getModifiedElement() { + return oldFile; + } + + /** + * Overrides getName. + * + * {@inheritDoc} + * + * @see org.eclipse.ltk.core.refactoring.Change#getName() + */ + @Override + public String getName() { + return Messages.bind(Messages.RenameModelChange_Name, oldFile.getName()); + } + + /** + * Overrides initializeValidationData. + * + * {@inheritDoc} + * + * @see org.eclipse.ltk.core.refactoring.Change#initializeValidationData(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public void initializeValidationData(IProgressMonitor pm) { + // Nothing + } + + /** + * Overrides isValid. + * + * {@inheritDoc} + * + * @see org.eclipse.ltk.core.refactoring.Change#isValid(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException, OperationCanceledException { + // That change assumes that the model resource has already been renamed + // So, the first thing to do is to get the new file, to restore it in order + // to change all the resources. + pm.subTask(Messages.RenameModelChange_LoadingEMF); + + pm.subTask(Messages.RenameModelChange_DaveDirtyEditor); + // We need to get the current workbench... so we have to use the UI-Thread! + openedEditors = new ArrayList<IMultiDiagramEditor>(); + Display.getDefault().syncExec(new Runnable() { + + public void run() { + IMultiDiagramEditor[] multiEditors = EditorUtils.getRelatedEditors(oldFile); + if(multiEditors != null && multiEditors.length > 0) { + for(IMultiDiagramEditor editor : multiEditors) { + if(editor.isDirty()) { + editor.doSave(new NullProgressMonitor()); + } + openedEditors.add(editor); + } + } + } + }); + pm.worked(10); + + /* + * Load ModelSet + */ + resourceSet = new ModelSet(); + try { + ModelsReader reader = new ModelsReader(); + reader.readModel(resourceSet); + resourceSet.loadModels(oldFile); + for(IResource r : impacted) { + if(r instanceof IFile) { + IFile file = (IFile)r; + try { + resourceSet.getResource(URI.createPlatformResourceURI(file.getFullPath().toString(), true), true); + } catch (Exception e) { + // to avoid load errors + } + } + } + } catch (Exception e) { + Activator.log.error(e); + } + + // Force EMF resolve and load all the resources + try { + EcoreUtil.resolveAll(resourceSet); + } catch (Exception ex) { + // the resolve all does not have to break the operation + } + + pm.worked(4); + domain = resourceSet.getTransactionalEditingDomain(); + + // TODO improve when impact analysis will be effective + return manageResourceSet(pm, resourceSet); + } + + /** + * Get a platform resource URI of the given path + * + * @param path + * the path + * @return the uri + */ + private URI getPlatformURI(IPath path) { + return URI.createPlatformResourceURI(path.toString(), true); + } + + /** + * Overrides perform. + * + * {@inheritDoc} + * + * @see org.eclipse.ltk.core.refactoring.Change#perform(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public Change perform(IProgressMonitor pm) throws CoreException { + + String lMsg = Messages.bind(Messages.RenameModelChange_Change, oldFile.getName(), newFile.getName()); + isUndoOperation = oldFile.exists() && !newFile.exists(); + + if(!isUndoOperation) { + //The file has already been renamed. Undo the rename change, then do the full refactoring in the resource set + newFile.move(oldFile.getFullPath(), true, new SubProgressMonitor(pm, 1)); + } + + Set<IResource> revertImpactedFiles = getRevertImpactedResources(); + + pm.beginTask(lMsg, 30); + try { + doRun(pm, resourceSet, domain); + // Now, save all the resources + pm.subTask(Messages.RenameModelChange_savingResource); + for(Resource res : resourceSet.getResources()) { + if(res.getURI().isPlatformResource()) { + try { + if (res.isModified()){ + res.save(ResourceUtils.getSaveOptions()); + } + } catch (Exception e) { + log.error(Messages.bind(Messages.RenameModelChange_ErrorLoading, res.getURI()), e); + } + } + } + pm.worked(5); + + // Do not forget to unload all the resources to avoid memory leak + pm.subTask(Messages.RenameModelChange_Unloading); + resourceSet.unload(); + pm.worked(1); + + // Now, notify the editor of the change + if(!openedEditors.isEmpty()) { + Display.getDefault().syncExec(new Runnable() { + + public void run() { + // Get the DI file as the rename could occur on any model's file. + IFile newDiFile = DiModelUtils.getRelatedDiFile(newFile); + for(IMultiDiagramEditor editor : openedEditors) { + try { + ModelSet diRes = editor.getServicesRegistry().getService(ModelSet.class); + if(diRes != null) { + diRes.saveAs(newFile.getFullPath()); + } + editor.setEditorInput(new FileEditorInput(newDiFile)); + + } catch (ServiceException e) { + log.error(e); + } catch (IOException e) { + log.error(e); + } + } + } + }); + } + + // Then, remove the old model files + pm.subTask(Messages.RenameModelChange_RemoveOldFile); + for(IResource iResource : relatedFiles) { + if(iResource.exists()) { + IPath path = iResource.getFullPath(); + URI oldURI = getPlatformURI(path); + URI newURI = uriMap.get(oldURI); + URIConverter uriConverter = resourceSet.getURIConverter(); + if (uriConverter.exists(newURI, Collections.EMPTY_MAP)){ + iResource.delete(true, new NullProgressMonitor()); + } + } + } + pm.worked(4); + + RenameModelChange undoChange = new RenameModelChange(newFile, oldFile, revertImpactedFiles); + + if(isUndoOperation) { + //Restore the expected state for the basic rename change + newFile.move(oldFile.getFullPath(), true, new SubProgressMonitor(pm, 1)); + } + + //Invert the change + return undoChange; + } finally { + pm.done(); + } + } + + private Set<IResource> getRevertImpactedResources() { + Set<IResource> result = new HashSet<IResource>(); + + Set<IResource> relatedFiles = ModelParticipantHelpers.getRelatedFiles(oldFile); + relatedFiles.add(oldFile); + + for(IResource initialResource : impacted) { + if(relatedFiles.contains(initialResource)) { + IResource invertedResource = invertFileName(initialResource); + result.add(invertedResource); //Participant model (the file is renamed) + } else { + result.add(initialResource); //Client model (only links are modified) + } + } + + return result; + } + + private IResource invertFileName(IResource initialResource) { + String newName; + + IPath pathWithoutExtension = newFile.getFullPath().removeFileExtension(); + IPath pathWithExtension = pathWithoutExtension.addFileExtension(initialResource.getFileExtension()); + newName = pathWithExtension.lastSegment(); + + IFile newFile = oldFile.getParent().getFile(new Path(newName)); + + return newFile; + } + + private RefactoringStatus manageResourceSet(final IProgressMonitor pm, final ModelSet resourceSet) { + + final Collection<Resource> readOnlies = new HashSet<Resource>(); + // for each object of the resources renamed, the refactor will search if a resource is read only or not + domain.getCommandStack().execute(new RecordingCommand(domain) { + + @Override + protected void doExecute() { + + // got though resources, to find if a resources that reference one of uri map is read only + for(URI uri : uriMap.keySet()) { + Resource r = resourceSet.getResource(uri, false); + ECrossReferenceAdapter adapter = ECrossReferenceAdapter.getCrossReferenceAdapter(resourceSet); + if(adapter == null) { + adapter = new ECrossReferenceAdapter(); + adapter.setTarget(resourceSet); + } + if(r != null) { + for(Iterator<EObject> i = EcoreUtil.getAllProperContents(r, false); i.hasNext();) { + EObject e = i.next(); + + //look for all references where e is playing + Collection<Setting> references = adapter.getInverseReferences(e); + for(Setting s : references) { + // get the EObject that play with e + EObject eObject = s.getEObject(); + + //this is the same resource --> not interesting + if((eObject.eResource() != null) && !(eObject.eResource().equals(e.eResource()))) { + //this is a external resource that references uri map + //if not not interesting + EStructuralFeature eFeature = s.getEStructuralFeature(); + + if((!eFeature.isDerived()) && (eObject.eClass().getEAllStructuralFeatures().contains(eFeature))) { + if(eObject.eResource() != null && EMFHelper.isReadOnly(eObject.eResource(), domain)) { + boolean isWritable = EMFHelper.canMakeWritable(eObject.eResource(), domain); + if(isWritable) { + readOnlies.add(eObject.eResource()); + } + } + } + } + } + } + } + } + } + + }); + // if read only => error to the user + if(!readOnlies.isEmpty()) { + ReadOnlyManager readOnlyManager = new ReadOnlyManager(domain); + Collection<URI> uris = new ArrayList<URI>(); + for(Iterator<Resource> iterator = readOnlies.iterator(); iterator.hasNext();) { + Resource r = (Resource)iterator.next(); + uris.add(r.getURI()); + } + + readOnlyManager.makeWritable(ReadOnlyAxis.anyAxis(), uris.toArray(new URI[uris.size()])); + } + return new RefactoringStatus(); + + } + + private void doRun(final IProgressMonitor pm, final ModelSet resourceSet, final TransactionalEditingDomain domain) { + domain.getCommandStack().execute(new RecordingCommand(domain) { + + @Override + protected void doExecute() { + // Manage Controlled map to ensure consistent history + // TODO change this code when history will be useless + URI modifiedURI = URI.createPlatformResourceURI(oldFile.getFullPath().removeFileExtension().toString(), true); + IModelSetQueryAdapter controledResourcesAdapter = ModelSetQuery.getExistingTypeCacheAdapter(resourceSet); + if(controledResourcesAdapter != null) { + EObject first = null; + for(Iterator<Notifier> i = resourceSet.getAllContents(); i.hasNext();) { + Notifier n = i.next(); + if(n instanceof EObject) { + first = (EObject)n; + break; + } + } + if(first != null) { + Collection<EObject> resources = null; + try { + resources = controledResourcesAdapter.getReachableObjectsOfType(first, historyPackage.Literals.CONTROLED_RESOURCE); + } catch (RuntimeException e) { + // in case of errors integrity must be valid + // even performances are bad + resources = new LinkedList<EObject>(); + for(int i = 0; i < resourceSet.getResources().size(); i++) { + Resource r = resourceSet.getResources().get(i); + for(Iterator<EObject> it = r.getAllContents(); it.hasNext();) { + EObject tmp = it.next(); + if(tmp instanceof ControledResource) { + ControledResource controled = (ControledResource)tmp; + resources.add(controled); + } + } + } + } + for(EObject e : resources) { + if(e instanceof ControledResource) { + ControledResource controled = (ControledResource)e; + URI baseURI = URI.createURI(e.eResource().getURI().trimSegments(1).trimFragment().toString() + "/"); + URI resolvedURI = URI.createURI(controled.getResourceURL()).resolve(baseURI); + if(resolvedURI.trimFileExtension().equals(modifiedURI.trimFileExtension())) { + String ext = resolvedURI.fileExtension(); + URI newURL = URI.createURI(resolvedURI.trimSegments(1).toString() + "/" + newFile.getFullPath().removeFileExtension().lastSegment().toString() + "." + ext); + controled.setResourceURL(newURL.deresolve(baseURI).toString()); + } + } + } + } + } + } + }); + // Change the uri of the files + pm.subTask(Messages.RenameModelChange_ModifyURI); + for(Resource res : resourceSet.getResources()) { + if(res.getURI().isPlatformResource()) { + URI newURI = uriMap.get(res.getURI()); + if(newURI != null) { + if(log.isDebugEnabled()) { + log.debug(Messages.bind(Messages.RenameModelChange_6, Arrays.asList(res.getURI(), newURI))); + } + res.setURI(newURI); + } + } + + } + pm.worked(5); + + + } +} |