diff options
author | Christian W. Damus | 2013-08-27 19:47:09 +0000 |
---|---|---|
committer | Christian W. Damus | 2013-09-16 20:02:45 +0000 |
commit | ee7752c06ac58a4cf1bba5053b2e6212c05a92c1 (patch) | |
tree | 82519d48ece38def62924aba9a8596ad9b2fe070 /extraplugins | |
parent | 9124789021c6428e4ed9710848d0b6ba4f159be0 (diff) | |
download | org.eclipse.papyrus-ee7752c06ac58a4cf1bba5053b2e6212c05a92c1.tar.gz org.eclipse.papyrus-ee7752c06ac58a4cf1bba5053b2e6212c05a92c1.tar.xz org.eclipse.papyrus-ee7752c06ac58a4cf1bba5053b2e6212c05a92c1.zip |
415369: [CDO] Support controlled resources and lazy loading
https://bugs.eclipse.org/bugs/show_bug.cgi?id=415369
Import models with sub-units from the workspace, preserving the sub-unit structure.
Also fix a problem of missed proxy updates in re-integration of sub-units where there are bidirectional references across sub-unit boundaries.
Diffstat (limited to 'extraplugins')
3 files changed, 321 insertions, 53 deletions
diff --git a/extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/controlmode/CDOControlModeParticipant.java b/extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/controlmode/CDOControlModeParticipant.java index 712f783f85b..272b9f3b9d2 100644 --- a/extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/controlmode/CDOControlModeParticipant.java +++ b/extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/controlmode/CDOControlModeParticipant.java @@ -36,10 +36,13 @@ import org.eclipse.emf.cdo.view.CDOView; 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.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.ecore.util.InternalEList; import org.eclipse.emf.spi.cdo.CDOStore; import org.eclipse.emf.spi.cdo.InternalCDOView; import org.eclipse.gmf.runtime.common.core.command.CommandResult; @@ -56,8 +59,6 @@ import org.eclipse.papyrus.infra.services.controlmode.participants.IUncontrolCom import com.google.common.base.Function; import com.google.common.base.Predicate; -import com.google.common.collect.ImmutableCollection; -import com.google.common.collect.ImmutableCollection.Builder; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @@ -105,18 +106,39 @@ public class CDOControlModeParticipant implements IControlCommandParticipant, IU return new AbstractCDOControlCommand(request) { @Override - protected void buildUpdates(ControlModeRequest request, Builder<? super Update> updates) { - for(final EObject object : getAllPersistentSubunitContents(getRequest(), true)) { - // replace references to the element by a proxy - final CDOID proxy = CDOIDUtil.createExternal(CDOProxyManager.createPapyrusCDOURI(object)); - for(EStructuralFeature.Setting next : getExternalCrossReferences(object)) { - updates.add(new ControlUpdate(next, object, proxy)); - } - } + protected void buildUpdates(ControlModeRequest request, IUpdate.Collector updates) { + collectProxyCrossReferenceUpdates(updates, request.getModelSet(), request.getNewURI()); } }; } + /** + * Obtains an {@linkplain IUpdate update} operation that converts external cross-references to the elements + * in a logical model unit indicated by the given representative {@code resource} to proxies. + * + * @param resource + * a resource that is a component of a logical model unit + * + * @return an operation that, when {@linkplain IUpdate#apply() applied}, will convert incoming cross-references to proxies + */ + public IUpdate getProxyCrossReferencesUpdate(Resource resource) { + IUpdate.Compound result = new CompoundUpdate(); + + collectProxyCrossReferenceUpdates(result, resource.getResourceSet(), resource.getURI()); + + return result; + } + + private void collectProxyCrossReferenceUpdates(IUpdate.Collector updates, ResourceSet resourceSet, URI unitURI) { + for(final EObject object : getAllPersistentSubunitContents(resourceSet, unitURI)) { + // replace references to the element by a proxy + final CDOID proxy = CDOIDUtil.createExternal(CDOProxyManager.createPapyrusCDOURI(object)); + for(EStructuralFeature.Setting next : getExternalCrossReferences(object)) { + updates.add(new ControlUpdate(next, object, proxy)); + } + } + } + public ICommand getPreUncontrolCommand(ControlModeRequest request) { return new AbstractCDOControlCommand(request) { @@ -128,8 +150,13 @@ public class CDOControlModeParticipant implements IControlCommandParticipant, IU } @Override - protected void buildUpdates(ControlModeRequest request, Builder<? super Update> updates) { - for(final EObject object : getAllPersistentSubunitContents(getRequest(), false)) { + protected void buildUpdates(ControlModeRequest request, IUpdate.Collector updates) { + final URI sourceURI = getRequest().getSourceURI(); + + IUpdate resolveProxies = IUpdate.EMPTY; + + // update references from other units to the unit being re-integrated + for(final EObject object : getAllPersistentSubunitContents(getRequest().getModelSet(), sourceURI)) { // replace proxy references to the element by the element or an updated proxy // which will be located in the destination resource Resource sourceResource = object.eResource(); @@ -140,8 +167,22 @@ public class CDOControlModeParticipant implements IControlCommandParticipant, IU final CDOID proxy = CDOIDUtil.createExternal(proxyURI); // internal cross-references within the unit won't be affected, as they are all moving - for(EStructuralFeature.Setting next : getExternalCrossReferences(object)) { - updates.add(new UncontrolUpdate(next, object, sourceProxy, destinationResource.getURI(), proxy)); + for(final EStructuralFeature.Setting next : getExternalCrossReferences(object)) { + if(next.getEObject().eIsProxy()) { + // the cross-reference will be from a proxy only when it is a proxy owned by the cross-referenced object, + // by virtue of an opposite reference. In this case, it's only interesting if the proxy targets the + // parent (re-integrated-into) resource. So, resolve this proxy because we need to update the + // reference, below + resolveProxies = resolveProxies.chain(new OneWayUpdate() { + + public void apply() { + resolve(object, (EReference)next.getEStructuralFeature(), next.getEObject()); + } + }); + + } else { + updates.add(new UncontrolUpdate(next, object, sourceProxy, destinationResource.getURI(), proxy)); + } } // upon removal from their resources, root elements will nonetheless retain a @@ -151,10 +192,43 @@ public class CDOControlModeParticipant implements IControlCommandParticipant, IU objectsToClearResource.add(object); } } + + // resolve proxies as per above, after completing the loop, to avoid concurrent modifications + resolveProxies.apply(); + + // update references from the unit being re-integrated to the unit it is re-integrating into + for(final EObject object : getAllPersistentSubunitContents(getRequest().getModelSet(), getRequest().getNewURI())) { + // replace proxy references to the element by the element or an updated proxy + // which will be located in the destination resource + final CDOID targetProxy = CDOIDUtil.createExternal(CDOProxyManager.createPapyrusCDOURI(object)); + + for(EStructuralFeature.Setting next : getExternalCrossReferences(object)) { + if(inUnit(next.getEObject(), sourceURI)) { + // replace proxy references from the sub-unit into the parent with a direct reference, as this will + // no longer be a cross-unit reference + updates.add(new UncontrolUpdate(next, object, targetProxy)); + } + } + } } }; } + void resolve(EObject object, EReference reference, EObject proxy) { + EReference opposite = reference.getEOpposite(); + if(opposite != null) { + if(opposite.isMany()) { + InternalEList<?> list = (InternalEList<?>)object.eGet(opposite, false); + int index = list.basicIndexOf(proxy); + if(index >= 0) { + list.get(index); // resolve just this index + } + } else { + object.eGet(opposite, true); // resolve the scalar reference + } + } + } + public ICommand getPostUncontrolCommand(ControlModeRequest request) { return new AbstractCDOControlCommand(request) { @@ -168,7 +242,7 @@ public class CDOControlModeParticipant implements IControlCommandParticipant, IU } @Override - protected void buildUpdates(ControlModeRequest request, Builder<? super Update> updates) { + protected void buildUpdates(ControlModeRequest request, IUpdate.Collector updates) { for(EObject next : objectsToClearResource) { // some former resource roots may not be reattached (e.g., SashWindowsMgr) if(CDOUtil.getCDOObject(next).cdoState() != CDOState.TRANSIENT) { @@ -216,20 +290,33 @@ public class CDOControlModeParticipant implements IControlCommandParticipant, IU static boolean inSameUnit(EObject object, EObject other) { // is the one object in the other's unit? - return inUnit(object, other.eResource().getURI()); + URI uri = other.eIsProxy() ? ((InternalEObject)other).eProxyURI().trimFragment() : other.eResource().getURI(); + return inUnit(object, uri); } static boolean inUnit(EObject object, URI unit) { // get the extension-less model URIs - URI uri = object.eResource().getURI().trimFileExtension(); + URI uri = object.eIsProxy() ? ((InternalEObject)object).eProxyURI().trimFragment() : object.eResource().getURI(); + uri = uri.trimFileExtension(); URI otherURI = unit.trimFileExtension(); return uri.equals(otherURI); } - static Iterable<EObject> getAllPersistentSubunitContents(final ControlModeRequest request, boolean isControl) { - final URI base = isControl ? request.getNewURI().trimFileExtension() : request.getSourceURI().trimFileExtension(); - Iterable<Resource> resources = filter(request.getModelSet().getResources(), new Predicate<Resource>() { + /** + * Iterates all of the proper contents of the resources comprising the logical model unit + * indicated by the representative {@code unitURI}. + * + * @param rset + * a resource set in which to load and/or iterate the resource contents + * @param unitURI + * the URI of one of the resources in the logical model unit, which therefore is representative of the unit + * + * @return an iterator over the entire logical model unit's proper contents + */ + static Iterable<EObject> getAllPersistentSubunitContents(ResourceSet rset, URI unitURI) { + final URI base = unitURI.trimFileExtension(); + Iterable<Resource> resources = filter(rset.getResources(), new Predicate<Resource>() { public boolean apply(Resource input) { return input.getURI().trimFileExtension().equals(base); @@ -262,7 +349,122 @@ public class CDOControlModeParticipant implements IControlCommandParticipant, IU // Nested types // - private static abstract class Update { + public static interface IUpdate { + + /** + * An update that does nothing. It is {@linkplain IUpdate#isEmpty() empty}. + */ + IUpdate EMPTY = new IUpdate() { + + public boolean isEmpty() { + return true; + } + + public void apply() { + // pass + } + + public void revert() { + // revert + } + + public IUpdate chain(IUpdate update) { + return (update == null) ? this : update; + } + + }; + + /** + * Queries whether I have nothing to do on {@link #apply() apply}. + * + * @return whether I am empty + */ + boolean isEmpty(); + + void apply(); + + void revert(); + + IUpdate chain(IUpdate update); + + interface Collector { + + void add(IUpdate update); + } + + interface Compound extends IUpdate, Collector { + // all methods inherited + } + + } + + static final class CompoundUpdate implements IUpdate.Compound { + + private final List<IUpdate> updates = Lists.newArrayList(); + + public void add(IUpdate update) { + updates.add(update); + } + + public Compound chain(IUpdate update) { + if((update != null) && !update.isEmpty()) { + add(update); + } + + return this; + } + + public boolean isEmpty() { + return updates.isEmpty(); + } + + public void apply() { + for(IUpdate next : updates) { + next.apply(); + } + } + + public void revert() { + for(ListIterator<IUpdate> iter = updates.listIterator(updates.size()); iter.hasPrevious();) { + iter.previous().revert(); + } + } + + static IUpdate compose(IUpdate first, IUpdate second) { + IUpdate result; + + if((second == null) || second.isEmpty()) { + result = (first == null) ? IUpdate.EMPTY : first; + } else if((first == null) || first.isEmpty()) { + // we already know it's not null or empty + result = second; + } else { + IUpdate.Compound compound = new CompoundUpdate(); + compound.add(first); + compound.add(second); + result = compound; + } + + return result; + } + } + + private static abstract class OneWayUpdate implements IUpdate { + + public void revert() { + throw new UnsupportedOperationException("OneWayUpdate cannot be reverted"); + } + + public IUpdate chain(IUpdate update) { + return CompoundUpdate.compose(this, update); + } + + public boolean isEmpty() { + return false; + } + } + + private static abstract class Update implements IUpdate { final EStructuralFeature.Setting setting; @@ -286,9 +488,14 @@ public class CDOControlModeParticipant implements IControlCommandParticipant, IU this.store = (view instanceof InternalCDOView) ? ((InternalCDOView)view).getStore() : null; } - abstract void apply(); + public IUpdate chain(IUpdate update) { + return CompoundUpdate.compose(this, update); + } - abstract void revert(); + public boolean isEmpty() { + // a leaf update is, by definition, not empty + return false; + } } private static final class ControlUpdate extends Update { @@ -319,8 +526,7 @@ public class CDOControlModeParticipant implements IControlCommandParticipant, IU } } - @Override - void apply() { + public void apply() { EStructuralFeature feature = setting.getEStructuralFeature(); if((index >= 0) || !feature.isMany()) { @@ -329,8 +535,7 @@ public class CDOControlModeParticipant implements IControlCommandParticipant, IU } } - @Override - void revert() { + public void revert() { EStructuralFeature feature = setting.getEStructuralFeature(); if(index >= 0 || !feature.isMany()) { @@ -374,14 +579,17 @@ public class CDOControlModeParticipant implements IControlCommandParticipant, IU } } - @Override - void apply() { + UncontrolUpdate(EStructuralFeature.Setting setting, EObject originalObject, CDOID originalProxy) { + this(setting, originalObject, originalProxy, null, null); + } + + public void apply() { EStructuralFeature feature = setting.getEStructuralFeature(); if((index >= 0) || !feature.isMany()) { InternalEObject owner = (InternalEObject)setting.getEObject(); - if(inUnit(owner, destinationURI)) { + if((destinationURI == null) || inUnit(owner, destinationURI)) { // direct reference store.set(owner, feature, index, CDOUtils.getCDOID(originalObject)); } else { @@ -391,8 +599,7 @@ public class CDOControlModeParticipant implements IControlCommandParticipant, IU } } - @Override - void revert() { + public void revert() { EStructuralFeature feature = setting.getEStructuralFeature(); if(index >= 0 || !feature.isMany()) { @@ -416,41 +623,39 @@ public class CDOControlModeParticipant implements IControlCommandParticipant, IU this.object = CDOUtil.getCDOObject(object); } - @Override - void apply() { + public void apply() { InternalEObject object = (InternalEObject)CDOUtil.getEObject(this.object); store.setContainer(object, null, object.eInternalContainer(), object.eContainerFeatureID()); } - @Override - void revert() { + public void revert() { // there is no need to revert clearing the resource reference; it will be // reestablished naturally by undo of the base command } } - private static abstract class AbstractCDOControlCommand extends AbstractControlCommand { + private static abstract class AbstractCDOControlCommand extends AbstractControlCommand implements IUpdate.Compound { AbstractCDOControlCommand(ControlModeRequest request) { super(Messages.CDOControlModeParticipant_commandLabel, Collections.emptyList(), request); } - private List<Update> updates; + private List<IUpdate> updates; @Override protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { - final ImmutableList.Builder<Update> updates = ImmutableList.builder(); + final ImmutableListCollector collector = new ImmutableListCollector(); - buildUpdates(getRequest(), updates); + buildUpdates(getRequest(), collector); - this.updates = updates.build(); + this.updates = collector.close(); - applyUpdates(); + apply(); return CommandResult.newOKCommandResult(); } - protected abstract void buildUpdates(ControlModeRequest request, ImmutableCollection.Builder<? super Update> updates); + protected abstract void buildUpdates(ControlModeRequest request, IUpdate.Collector updates); @Override protected IStatus doUndo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { @@ -458,7 +663,7 @@ public class CDOControlModeParticipant implements IControlCommandParticipant, IU if(result.isOK()) { // setting proxies in the way we did is not recorded by EMF, so we have to undo it ourselves - revertUpdates(); + revert(); } return result; @@ -470,22 +675,47 @@ public class CDOControlModeParticipant implements IControlCommandParticipant, IU if(result.isOK()) { // setting proxies in the way we did is not recorded by EMF, so we have to redo it ourselves - applyUpdates(); + apply(); } return result; } - private void applyUpdates() { - for(Update next : updates) { + public boolean isEmpty() { + return (updates == null) || updates.isEmpty(); + } + + public void apply() { + for(IUpdate next : updates) { next.apply(); } } - private void revertUpdates() { - for(ListIterator<Update> iter = updates.listIterator(updates.size()); iter.hasPrevious();) { + public void revert() { + for(ListIterator<IUpdate> iter = updates.listIterator(updates.size()); iter.hasPrevious();) { iter.previous().revert(); } } + + public void add(IUpdate update) { + throw new UnsupportedOperationException("AbtsractCDOControlCommand is not externally modifiable"); //$NON-NLS-1$ + } + + public Compound chain(IUpdate update) { + throw new UnsupportedOperationException("AbtsractCDOControlCommand is not externally modifiable"); //$NON-NLS-1$ + } }; + + private static final class ImmutableListCollector implements IUpdate.Collector { + + private final ImmutableList.Builder<IUpdate> builder = ImmutableList.builder(); + + public void add(IUpdate update) { + builder.add(update); + } + + List<IUpdate> close() { + return builder.build(); + } + } } diff --git a/extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/importer/ModelImporter.java b/extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/importer/ModelImporter.java index cf09cb008bd..209403de4e8 100644 --- a/extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/importer/ModelImporter.java +++ b/extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/importer/ModelImporter.java @@ -37,6 +37,7 @@ import org.eclipse.papyrus.cdo.core.importer.IModelTransferNode; import org.eclipse.papyrus.cdo.core.importer.IModelTransferOperation; import org.eclipse.papyrus.cdo.internal.core.Activator; import org.eclipse.papyrus.cdo.internal.core.IInternalPapyrusRepository; +import org.eclipse.papyrus.cdo.internal.core.controlmode.CDOControlModeParticipant; import org.eclipse.papyrus.cdo.internal.core.l10n.Messages; import org.eclipse.papyrus.infra.core.sashwindows.di.DiPackage; import org.eclipse.papyrus.infra.core.sashwindows.di.PageList; @@ -88,9 +89,9 @@ public class ModelImporter implements IModelImporter { // imported, all proxies have been resolved that can be. So, // there's no need for a further EcoreUtil.resolveAll() or such - // 1 for transaction commit, 1 for saving affected non-imported - // models, and 1 for clean-up - SubMonitor sub = SubMonitor.convert(monitor, Messages.ModelImporter_4, configuration.getModelsToTransfer().size() + 3); + // 1/resource for import and 1/resource for sub-unit proxies + // 1 for each transaction commit, 1 for saving affected non-imported models, and 1 for clean-up + SubMonitor sub = SubMonitor.convert(monitor, Messages.ModelImporter_4, configuration.getModelsToTransfer().size() + 4); IInternalPapyrusRepository repository = (IInternalPapyrusRepository)mapping.getRepository(); ResourceSet destination = repository.createTransaction(new ResourceSetImpl()); @@ -107,6 +108,24 @@ public class ModelImporter implements IModelImporter { result.add(new BasicDiagnostic(IStatus.ERROR, Activator.PLUGIN_ID, 0, Messages.ModelImporter_5, new Object[]{ e })); } + // can't create CDO-style proxies until the resources have been committed, because only then + // will the objects be persisted and have OIDs to reference + boolean hasSubUnits = false; + for(IModelTransferNode model : configuration.getModelsToTransfer()) { + if(createSubUnitProxies(model, mapping.getMapping(model), transaction, sub.newChild(1))) { + hasSubUnits = true; + } + } + if(hasSubUnits) { + try { + transaction.commit(sub.newChild(1)); + } catch (CommitException e) { + result.add(new BasicDiagnostic(IStatus.ERROR, Activator.PLUGIN_ID, 0, Messages.ModelImporter_5, new Object[]{ e })); + } + } else { + sub.worked(1); // nothing to commit but still count progress + } + try { saveNonimportedModels(mapping, transaction, sub.newChild(1)); } catch (Exception e) { @@ -143,6 +162,25 @@ public class ModelImporter implements IModelImporter { return result; } + protected boolean createSubUnitProxies(IModelTransferNode model, IPath toPath, CDOTransaction transaction, IProgressMonitor monitor) { + boolean result; + + IPath basePath = toPath.removeFileExtension(); + URI uri = model.getPrimaryResourceURI(); + + SubMonitor sub = SubMonitor.convert(monitor, model.getName(), 1); + + Resource destination = transaction.getResource(basePath.addFileExtension(uri.fileExtension()).toString()); + CDOControlModeParticipant.IUpdate update = new CDOControlModeParticipant().getProxyCrossReferencesUpdate(destination); + result = !update.isEmpty(); + update.apply(); // no harm in applying an empty update + sub.worked(1); + + sub.done(); + + return result; + } + protected Diagnostic importResource(Resource source, Resource destination) { if(!destination.getContents().isEmpty()) { ContentType contentType = getContentType(source); diff --git a/extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/importer/WorkspaceDIDependentsProvider.java b/extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/importer/WorkspaceDIDependentsProvider.java index 0f8c8bd5050..25e93d25a6f 100644 --- a/extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/importer/WorkspaceDIDependentsProvider.java +++ b/extraplugins/cdo/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/internal/core/importer/WorkspaceDIDependentsProvider.java @@ -92,7 +92,7 @@ public class WorkspaceDIDependentsProvider implements IModelDependentsProvider { try { for(IFile next : filter(Arrays.asList(container.members()), IFile.class)) { - if((next != file) && baseName.equals(next.getFullPath().removeFileExtension().lastSegment())) { + if(!next.equals(file) && baseName.equals(next.getFullPath().removeFileExtension().lastSegment())) { uris.add(baseURI.appendSegment(next.getName())); } } |