Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Improve SVN locking upport
Implemented basic support for checking if diagrams are editable and
unlocking using validateEdit. Improves SVN support. Closes #1217
  • Loading branch information
philip-alldredge committed Aug 1, 2018
1 parent c864f56 commit 145bc20
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 84 deletions.
29 changes: 29 additions & 0 deletions ge/org.osate.ge/setups/svn_test_dev.setup
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<setup:Project
xmi:version="2.0"
xmlns:xmi="http://www.omg.org/XMI"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:setup="http://www.eclipse.org/oomph/setup/1.0"
xmlns:setup.targlets="http://www.eclipse.org/oomph/setup/targlets/1.0"
xsi:schemaLocation="http://www.eclipse.org/oomph/setup/targlets/1.0 http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/SetupTarglets.ecore"
name="svn_test_dev"
label="Subversive for GE Testing">
<stream name="develop"
label="develop">
<setupTask
xsi:type="setup.targlets:TargletTask">
<targlet
name="Subversive">
<requirement
name="org.tigris.subversion.clientadapter.javahl.feature.feature.group"/>
<requirement
name="org.tigris.subversion.subclipse.feature.group"/>
<repositoryList>
<repository
url="https://dl.bintray.com/subclipse/releases/subclipse/latest/"/>
</repositoryList>
</targlet>
</setupTask>
</stream>
<description>Adds Subversive</description>
</setup:Project>
Expand Up @@ -10,11 +10,13 @@
import java.util.Objects;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
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.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.e4.core.contexts.EclipseContextFactory;
Expand All @@ -31,6 +33,8 @@
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
import org.eclipse.ltk.core.refactoring.participants.RenameParticipant;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.xtext.resource.IFragmentProvider;
import org.eclipse.xtext.resource.IReferenceDescription;
import org.eclipse.xtext.resource.IResourceDescription;
Expand Down Expand Up @@ -63,7 +67,8 @@ public class AgeRenameParticipant extends RenameParticipant {
private DiagramService diagramService;
private ResourceSet refactoringResourceSet;
private IProject project;
private DiagramService.ReferenceCollection originalReferences; // The original set of references during the refactoring. They are stored before the change because the model change will have occured before the change is executed.
private DiagramService.ReferenceCollection originalReferences; // The original set of references during the refactoring. They are stored before the change
// because the model change will have occured before the change is executed.

private static class UriAndRelativeReference {
public final URI uri;
Expand All @@ -79,19 +84,20 @@ public UriAndRelativeReference(final URI uri, final RelativeBusinessObjectRefere
protected boolean initialize(final Object element) {
originalCanRefToNewInfoMap.clear();

if(!(element instanceof IRenameElementContext)) {
if (!(element instanceof IRenameElementContext)) {
return false;
}

ctx = (IRenameElementContext)element;
ctx = (IRenameElementContext) element;

final URI targetElementUri = ctx.getTargetElementURI();
if(targetElementUri == null) {
if (targetElementUri == null) {
return false;
}

final IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(targetElementUri.toPlatformString(true)));
if(resource == null) {
final IResource resource = ResourcesPlugin.getWorkspace().getRoot()
.findMember(new Path(targetElementUri.toPlatformString(true)));
if (resource == null) {
return false;
}

Expand All @@ -100,29 +106,31 @@ protected boolean initialize(final Object element) {
.getInstance(LiveScopeResourceSetInitializer.class).initialize(tmpRs);

targetObject = tmpRs.getEObject(targetElementUri, true);
if(targetObject == null || !(targetObject.eResource() instanceof XtextResource)) {
if (targetObject == null || !(targetObject.eResource() instanceof XtextResource)) {
return false;
}

final XtextResource xtextResource = (XtextResource) targetObject.eResource();

// Get the provider for the refactoring resource set
final RefactoringResourceSetProvider refactoringResourceSetProvider = xtextResource.getResourceServiceProvider().get(RefactoringResourceSetProvider.class);
final RefactoringResourceSetProvider refactoringResourceSetProvider = xtextResource.getResourceServiceProvider()
.get(RefactoringResourceSetProvider.class);
project = ProjectUtil.getProject(targetElementUri);
if (project == null) {
return false;
}

// Get the refactoring resource set
refactoringResourceSet = refactoringResourceSetProvider.get(project);
if(refactoringResourceSet == null) {
if (refactoringResourceSet == null) {
return false;
}

// Get global services
final Bundle bundle = FrameworkUtil.getBundle(getClass());
final IEclipseContext context = EclipseContextFactory.getServiceContext(bundle.getBundleContext());
referenceService = Objects.requireNonNull(context.get(ReferenceService.class), "Unable to get reference service");
referenceService = Objects.requireNonNull(context.get(ReferenceService.class),
"Unable to get reference service");
diagramService = Objects.requireNonNull(context.get(DiagramService.class), "Unable to get diagram service");

// Get projects with are affected by the refactoring.
Expand All @@ -132,16 +140,20 @@ protected boolean initialize(final Object element) {
final Map<URI, Set<URI>> externalReferencesMap = buildExternalReferenceMap(relevantProjects);

// Find all dependent objects
final Set<EObject> dependentObjects = getDependentObjects(targetObject, targetObject.eResource().getResourceSet(), externalReferencesMap);
final Set<EObject> dependentObjects = getDependentObjects(targetObject,
targetObject.eResource().getResourceSet(), externalReferencesMap);
dependentObjects.add(targetObject);

for(final EObject dirtyObject : dependentObjects) {
for (final EObject dirtyObject : dependentObjects) {
final URI uri = getNameIndependentUri(dirtyObject);
if(uri != null) {
final CanonicalBusinessObjectReference canonicalReference = referenceService.getCanonicalReference(dirtyObject);
final RelativeBusinessObjectReference relativeReference = referenceService.getRelativeReference(dirtyObject);
if(canonicalReference != null && relativeReference != null) {
originalCanRefToNewInfoMap.put(canonicalReference, new UriAndRelativeReference(uri, relativeReference));
if (uri != null) {
final CanonicalBusinessObjectReference canonicalReference = referenceService
.getCanonicalReference(dirtyObject);
final RelativeBusinessObjectReference relativeReference = referenceService
.getRelativeReference(dirtyObject);
if (canonicalReference != null && relativeReference != null) {
originalCanRefToNewInfoMap.put(canonicalReference,
new UriAndRelativeReference(uri, relativeReference));
}
}
}
Expand All @@ -160,6 +172,35 @@ public String getName() {
@Override
public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context)
throws OperationCanceledException {

final Set<IFile> relatedDiagramFiles = originalReferences.getRelatedDiagramFiles();
final IFile[] relatedDiagramFilesArray = relatedDiagramFiles.toArray(new IFile[relatedDiagramFiles.size()]);
if (relatedDiagramFilesArray.length > 0) {
final Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow() == null ? null
: PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();

final IStatus status = ResourcesPlugin.getWorkspace().validateEdit(relatedDiagramFilesArray, shell);
final String errorReason;
if (!status.isOK()) {
errorReason = (status.getMessage() == null || status.getMessage().length() == 0)
? "One or more related diagrams are not editable"
: status.getMessage();
} else if (relatedDiagramFiles.stream().anyMatch(file -> file.isReadOnly())) {
errorReason = "One ore more related diagrams are read-only.";
} else {
errorReason = null;
}

if (errorReason != null) {
final RefactoringStatus refactoringStatus = new RefactoringStatus();
refactoringStatus
.addError(
errorReason
+ " Read-only diagrams will not be updated and broken linkages may result.");
return refactoringStatus;
}
}

return null;
}

Expand All @@ -176,7 +217,8 @@ public void initializeValidationData(final IProgressMonitor pm) {
}

@Override
public RefactoringStatus isValid(final IProgressMonitor pm) throws CoreException, OperationCanceledException {
public RefactoringStatus isValid(final IProgressMonitor pm)
throws CoreException, OperationCanceledException {
return new RefactoringStatus();
}

Expand All @@ -188,15 +230,18 @@ public Change perform(final IProgressMonitor pm) throws CoreException {
final Map<CanonicalBusinessObjectReference, RelativeBusinessObjectReference> originalCanRefToNewRelRefMap = new HashMap<>();
final Map<CanonicalBusinessObjectReference, CanonicalBusinessObjectReference> undoOriginalCanRefToNewCanRefMap = new HashMap<>();
final Map<CanonicalBusinessObjectReference, RelativeBusinessObjectReference> undoOriginalCanRefToNewRelRefMap = new HashMap<>();
for(final Entry<CanonicalBusinessObjectReference, UriAndRelativeReference> entry : originalCanRefToNewInfoMap.entrySet()) {
for (final Entry<CanonicalBusinessObjectReference, UriAndRelativeReference> entry : originalCanRefToNewInfoMap
.entrySet()) {
final EObject newObject = refactoringResourceSet.getEObject(entry.getValue().uri, true);
if(newObject != null) {
if (newObject != null) {
final CanonicalBusinessObjectReference originalCanRef = entry.getKey();
final CanonicalBusinessObjectReference newCanRef = referenceService.getCanonicalReference(newObject);
final RelativeBusinessObjectReference newRelRef = referenceService.getRelativeReference(newObject);
final CanonicalBusinessObjectReference newCanRef = referenceService
.getCanonicalReference(newObject);
final RelativeBusinessObjectReference newRelRef = referenceService
.getRelativeReference(newObject);
final RelativeBusinessObjectReference originalRelRef = entry.getValue().relRef;

if(newCanRef != null && newRelRef != null) {
if (newCanRef != null && newRelRef != null) {
originalCanRefToNewCanRefMap.put(originalCanRef, newCanRef);
originalCanRefToNewRelRefMap.put(originalCanRef, newRelRef);
undoOriginalCanRefToNewCanRefMap.put(originalCanRef, originalCanRef);
Expand Down Expand Up @@ -226,13 +271,14 @@ public Object getModifiedElement() {
// Builds a mapping between EObject URIs and the URIs of EObjects that it affects based on the EMF Index.
private static final Map<URI, Set<URI>> buildExternalReferenceMap(final Set<IProject> projects) {
final Map<URI, Set<URI>> externalReferencesMap = new HashMap<>();
for(final IResourceDescription resourceDescription : ScopedEMFIndexRetrieval.calculateResourceDescriptions(projects)) {
for(final IReferenceDescription refDescription : resourceDescription.getReferenceDescriptions()) {
for (final IResourceDescription resourceDescription : ScopedEMFIndexRetrieval
.calculateResourceDescriptions(projects)) {
for (final IReferenceDescription refDescription : resourceDescription.getReferenceDescriptions()) {
final EReference ref = refDescription.getEReference();
if(isHandledRefinedReference(ref)) {
if(refDescription.getSourceEObjectUri() != null && refDescription.getTargetEObjectUri() != null) {
if (isHandledRefinedReference(ref)) {
if (refDescription.getSourceEObjectUri() != null && refDescription.getTargetEObjectUri() != null) {
Set<URI> affectedUris = externalReferencesMap.get(refDescription.getTargetEObjectUri());
if(affectedUris == null) {
if (affectedUris == null) {
affectedUris = new HashSet<>();
externalReferencesMap.put(refDescription.getTargetEObjectUri(), affectedUris);
}
Expand All @@ -258,102 +304,98 @@ private static URI getNameIndependentUri(final EObject obj) {
IFragmentProvider oldFragmentProvider = null;
XtextResource res = null;
try {
if(obj.eResource() instanceof XtextResource) {
res = (XtextResource)obj.eResource();
if (obj.eResource() instanceof XtextResource) {
res = (XtextResource) obj.eResource();
oldFragmentProvider = res.getFragmentProvider();
res.setFragmentProvider(null);
}

// Store the URIs
return EcoreUtil.getURI(obj);


} finally {
// Restore the old fragment processor
if(oldFragmentProvider != null) {
if (oldFragmentProvider != null) {
res.setFragmentProvider(oldFragmentProvider);
}
}
}

private static Set<EObject> getDependentObjects(final EObject obj, final ResourceSet rs, final Map<URI, Set<URI>> externalReferencesMap) {
private static Set<EObject> getDependentObjects(final EObject obj, final ResourceSet rs,
final Map<URI, Set<URI>> externalReferencesMap) {
final Set<EObject> results = new HashSet<>();
final EObject objectOfInterest;

// If the object is a component type rename, replace it with the component type it renames. This will result in additional objects being returned but it
// is the only known way of getting types related to the renames.
if(obj instanceof ComponentTypeRename) {
final ComponentType renamedComponentType = ((ComponentTypeRename)obj).getRenamedComponentType();
if (obj instanceof ComponentTypeRename) {
final ComponentType renamedComponentType = ((ComponentTypeRename) obj).getRenamedComponentType();
objectOfInterest = renamedComponentType == null ? null : renamedComponentType;
} else {
objectOfInterest = obj;
}

if(objectOfInterest != null) {
getRelatedObjects(Collections.singleton(objectOfInterest), rs, results, externalReferencesMap, obj instanceof Feature);
if (objectOfInterest != null) {
getRelatedObjects(Collections.singleton(objectOfInterest), rs, results, externalReferencesMap,
obj instanceof Feature);
}

return results;
}

// Gets objects related to the specified objects of interest
private static void getRelatedObjects(Collection<EObject> objectsOfInterest,
final ResourceSet rs,
final Set<EObject> results,
final Map<URI, Set<URI>> externalReferencesMap,
final boolean recursive) {
private static void getRelatedObjects(Collection<EObject> objectsOfInterest, final ResourceSet rs,
final Set<EObject> results, final Map<URI, Set<URI>> externalReferencesMap, final boolean recursive) {
final Collection<EObject> newObjects = new ArrayList<>();
for(final Collection<Setting> settings : UsageCrossReferencer.findAll(objectsOfInterest, rs).values()) {
for(final Setting s : settings) {
for (final Collection<Setting> settings : UsageCrossReferencer.findAll(objectsOfInterest, rs).values()) {
for (final Setting s : settings) {
final EStructuralFeature sf = s.getEStructuralFeature();
if(isHandledRefinedReference(sf)) {
if (isHandledRefinedReference(sf)) {
newObjects.add(s.getEObject());
} else if(sf == Aadl2Package.eINSTANCE.getRealization_Implemented()) {
} else if (sf == Aadl2Package.eINSTANCE.getRealization_Implemented()) {
// Get the component implementation from the realization
final Realization realization = (Realization)s.getEObject();
final Realization realization = (Realization) s.getEObject();
newObjects.add(realization.getSpecific());
}
}
}

for(final EObject objectOfInterest : objectsOfInterest) {
for (final EObject objectOfInterest : objectsOfInterest) {
final URI objectOfInterestUri = EcoreUtil.getURI(objectOfInterest);
if(objectOfInterestUri != null) {
if (objectOfInterestUri != null) {
// Add objects references in the external references map
final Set<URI> affectedUris = externalReferencesMap.get(objectOfInterestUri);
if(affectedUris != null) {
for(final URI affectedUri : affectedUris) {
if (affectedUris != null) {
for (final URI affectedUri : affectedUris) {
final EObject affectedObject = rs.getEObject(affectedUri, true);
if(affectedObject != null) {
if (affectedObject != null) {
newObjects.add(affectedObject);
}
}
}
}
}

if(results.addAll(newObjects)) {
if(recursive) {
if (results.addAll(newObjects)) {
if (recursive) {
getRelatedObjects(newObjects, rs, results, externalReferencesMap, recursive);
}
}
}

private static boolean isHandledRefinedReference(final EStructuralFeature sf) {
return sf == Aadl2Package.eINSTANCE.getFeature_Refined() ||
sf == Aadl2Package.eINSTANCE.getConnection_Refined() ||
sf == Aadl2Package.eINSTANCE.getSubcomponent_Refined() ||
sf == Aadl2Package.eINSTANCE.getSubcomponent_Refined() ||
sf == Aadl2Package.eINSTANCE.getFlowSpecification_Refined();
return sf == Aadl2Package.eINSTANCE.getFeature_Refined() || sf == Aadl2Package.eINSTANCE.getConnection_Refined()
|| sf == Aadl2Package.eINSTANCE.getSubcomponent_Refined()
|| sf == Aadl2Package.eINSTANCE.getSubcomponent_Refined()
|| sf == Aadl2Package.eINSTANCE.getFlowSpecification_Refined();
}



private static class SimpleUpdatedReferenceValueProvider implements DiagramService.UpdatedReferenceValueProvider {
private final Map<CanonicalBusinessObjectReference, CanonicalBusinessObjectReference> originalCanRefToNewCanRefMap;
private final Map<CanonicalBusinessObjectReference, RelativeBusinessObjectReference> originalCanRefToNewRelRefMap;

public SimpleUpdatedReferenceValueProvider(final Map<CanonicalBusinessObjectReference, CanonicalBusinessObjectReference> originalCanRefToNewCanRefMap,
public SimpleUpdatedReferenceValueProvider(
final Map<CanonicalBusinessObjectReference, CanonicalBusinessObjectReference> originalCanRefToNewCanRefMap,
final Map<CanonicalBusinessObjectReference, RelativeBusinessObjectReference> originalCanRefToNewRelRefMap) {
this.originalCanRefToNewCanRefMap = originalCanRefToNewCanRefMap;
this.originalCanRefToNewRelRefMap = originalCanRefToNewRelRefMap;
Expand Down
Expand Up @@ -19,6 +19,8 @@
import org.osate.ge.internal.diagram.runtime.RelativeBusinessObjectReference;
import org.osate.ge.internal.ui.editor.AgeDiagramEditor;

import com.google.common.collect.ImmutableSet;

/**
* Service for finding, opening, and creating diagrams. Registered as an Eclipse service.
*
Expand Down Expand Up @@ -98,6 +100,8 @@ default AgeDiagramEditor openOrCreateDiagramForBusinessObject(final Object bo) {

interface ReferenceCollection {
void update(UpdatedReferenceValueProvider newReferenceValues);

ImmutableSet<IFile> getRelatedDiagramFiles();
}

// Used to provide new reference values when updating a reference collection
Expand Down

0 comments on commit 145bc20

Please sign in to comment.