[ChangeSerializer] update references with FQNs and semi-FQNs properly

Signed-off-by: Moritz Eysholdt <moritz.eysholdt@typefox.io>
This commit is contained in:
Moritz Eysholdt 2017-10-10 11:28:12 +02:00 committed by Moritz Eysholdt
parent 85f07a23c0
commit 6c671f9aaf
16 changed files with 449 additions and 157 deletions

View file

@ -235,6 +235,72 @@ class ChangeSerializerTest {
'''
}
@Test
def void testRenameFqn1() {
val fs = new InMemoryURIHandler()
fs += "inmemory:/file1.pstl" -> '''
#1 r {
X refs a1.a2 X.a1.a2 r.X.a1.a2 { a1 { a2 refs a2 { a3 { ref a3 } } } }
Y refs b1.b2 Y.b1.b2 r.Y.b1.b2 { b1 { b2 { ref b2 } } }
}
'''
val rs = fs.createResourceSet
val model = rs.contents("inmemory:/file1.pstl", Node)
val serializer = newChangeSerializer()
serializer.addModification(model.eResource) [
model.children.head.children.head.children.head.name = "b"
]
Assert.assertEquals(1, model.eResource.resourceSet.resources.size)
serializer.endRecordChangesToTextDocuments === '''
----------------- inmemory:/file1.pstl (syntax: <offset|text>) -----------------
#1 r {
X refs <15:5|a1.b> <21:7|a1.b> <29:9|a1.b> { a1 { <46:2|b> refs <54:2|b> { a3 { ref a3 } } } }
Y refs b1.b2 Y.b1.b2 r.Y.b1.b2 { b1 { b2 { ref b2 } } }
}
--------------------------------------------------------------------------------
15 5 "a1.a2" -> "a1.b"
21 7 "X.a1.a2" -> "a1.b"
29 9 "r.X.a1.a2" -> "a1.b"
46 2 "a2" -> "b"
54 2 "a2" -> "b"
'''
}
@Test
def void testRenameFqn1ValueConversion() {
val fs = new InMemoryURIHandler()
fs += "inmemory:/file1.pstl" -> '''
#1 r {
X refs ^a1.^a2 ^X.^a1.^a2 ^r.^X.^a1.^a2 { a1 { a2 refs ^a2 { a3 { ref ^a3 } } } }
Y refs ^b1.^b2 ^Y.^b1.^b2 ^r.^Y.^b1.^b2 { b1 { b2 { ref b2 } } }
}
'''
val rs = fs.createResourceSet
val model = rs.contents("inmemory:/file1.pstl", Node)
val serializer = newChangeSerializer()
serializer.addModification(model.eResource) [
model.children.head.children.head.children.head.name = "b"
]
Assert.assertEquals(1, model.eResource.resourceSet.resources.size)
serializer.endRecordChangesToTextDocuments === '''
----------------- inmemory:/file1.pstl (syntax: <offset|text>) -----------------
#1 r {
X refs <15:7|a1.b> <23:10|a1.b> <34:13|a1.b> { a1 { <55:2|b> refs <63:3|b> { a3 { ref ^a3 } } } }
Y refs ^b1.^b2 ^Y.^b1.^b2 ^r.^Y.^b1.^b2 { b1 { b2 { ref b2 } } }
}
--------------------------------------------------------------------------------
15 7 "^a1.^a2" -> "a1.b"
23 10 "^X.^a1.^a2" -> "a1.b"
34 13 "^r.^X.^a1.^a2" -> "a1.b"
55 2 "a2" -> "b"
63 3 "^a2" -> "b"
'''
}
@Test
def void testResourceURIChange() {
val fs = new InMemoryURIHandler()

View file

@ -12,7 +12,6 @@ import org.eclipse.lsp4j.RenameParams
import org.eclipse.lsp4j.TextDocumentIdentifier
import org.eclipse.xtext.testing.AbstractLanguageServerTest
import org.junit.Test
import org.junit.Ignore
/**
* @author koehnlein - Initial contribution and API
@ -45,7 +44,7 @@ class RenameTest2 extends AbstractLanguageServerTest {
'''.toString, toExpectation(workspaceEdit))
}
@Test@Ignore
@Test
def void testRenameContainer() {
val model = '''
package foo
@ -66,8 +65,8 @@ class RenameTest2 extends AbstractLanguageServerTest {
assertEquals('''
changes :
Foo.fileawaretestlanguage : Baz [[2, 8] .. [2, 11]]
Baz [[3, 9] .. [3, 12]]
Baz [[3, 6] .. [3, 9]]
Bar [[5, 5] .. [5, 16]]
Bar [[6, 5] .. [6, 12]]
documentChanges :
'''.toString, toExpectation(workspaceEdit))
}

View file

@ -9,11 +9,12 @@ package org.eclipse.xtext.ide.tests.testlanguage
import com.google.inject.Binder
import com.google.inject.name.Names
import org.eclipse.xtext.ide.serializer.hooks.IReferenceUpdater
import org.eclipse.xtext.ide.tests.testlanguage.ide.serializer.PartialSerializationTestLanguageReferenceUpdater
import org.eclipse.xtext.resource.IResourceDescriptions
import org.eclipse.xtext.resource.impl.LiveShadowedResourceDescriptions
import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider
import org.eclipse.xtext.ide.serializer.hooks.IReferenceUpdater
import org.eclipse.xtext.ide.tests.testlanguage.ide.serializer.PartialSerializationTestLanguageReferenceUpdater
import org.eclipse.xtext.ide.tests.testlanguage.scoping.PartialSerializationTestLanguageValueConverter
/**
* Use this class to register components to be used at runtime / without the Equinox extension registry.
@ -28,4 +29,9 @@ class PartialSerializationTestLanguageRuntimeModule extends AbstractPartialSeria
def Class<? extends IReferenceUpdater> bindCleanupStrategy() {
return PartialSerializationTestLanguageReferenceUpdater
}
override bindIValueConverterService() {
PartialSerializationTestLanguageValueConverter
}
}

View file

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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
*******************************************************************************/
package org.eclipse.xtext.ide.tests.testlanguage.scoping
import com.google.inject.Inject
import org.eclipse.xtext.common.services.DefaultTerminalConverters
import org.eclipse.xtext.conversion.IValueConverter
import org.eclipse.xtext.conversion.ValueConverter
import org.eclipse.xtext.conversion.impl.QualifiedNameValueConverter
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
class PartialSerializationTestLanguageValueConverter extends DefaultTerminalConverters {
@Inject QualifiedNameValueConverter fqnc
@ValueConverter(rule="QualifiedName")
def IValueConverter<String> QualifiedName() {
return fqnc
}
}

View file

@ -26,6 +26,7 @@ import org.eclipse.xtext.testing.InjectWith;
import org.eclipse.xtext.testing.XtextRunner;
import org.eclipse.xtext.testing.util.InMemoryURIHandler;
import org.eclipse.xtext.xbase.lib.Extension;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.eclipse.xtext.xbase.lib.ObjectExtensions;
import org.eclipse.xtext.xbase.lib.Pair;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
@ -355,6 +356,114 @@ public class ChangeSerializerTest {
this._changeSerializerTestHelper.operator_tripleEquals(_endRecordChangesToTextDocuments, _builder_2);
}
@Test
public void testRenameFqn1() {
final InMemoryURIHandler fs = new InMemoryURIHandler();
StringConcatenation _builder = new StringConcatenation();
_builder.append("#1 r {");
_builder.newLine();
_builder.append("\t");
_builder.append("X refs a1.a2 X.a1.a2 r.X.a1.a2 { a1 { a2 refs a2 { a3 { ref a3 } } } }");
_builder.newLine();
_builder.append("\t");
_builder.append("Y refs b1.b2 Y.b1.b2 r.Y.b1.b2 { b1 { b2 { ref b2 } } }");
_builder.newLine();
_builder.append("}");
_builder.newLine();
Pair<String, String> _mappedTo = Pair.<String, String>of("inmemory:/file1.pstl", _builder.toString());
this._changeSerializerTestHelper.operator_add(fs, _mappedTo);
final ResourceSet rs = this._changeSerializerTestHelper.createResourceSet(fs);
final Node model = this._changeSerializerTestHelper.<Node>contents(rs, "inmemory:/file1.pstl", Node.class);
final IChangeSerializer serializer = this._changeSerializerTestHelper.newChangeSerializer();
final IChangeSerializer.IModification<Resource> _function = (Resource it) -> {
Node _head = IterableExtensions.<Node>head(IterableExtensions.<Node>head(IterableExtensions.<Node>head(model.getChildren()).getChildren()).getChildren());
_head.setName("b");
};
serializer.<Resource>addModification(model.eResource(), _function);
Assert.assertEquals(1, model.eResource().getResourceSet().getResources().size());
Collection<IEmfResourceChange> _endRecordChangesToTextDocuments = this._changeSerializerTestHelper.endRecordChangesToTextDocuments(serializer);
StringConcatenation _builder_1 = new StringConcatenation();
_builder_1.append("----------------- inmemory:/file1.pstl (syntax: <offset|text>) -----------------");
_builder_1.newLine();
_builder_1.append("#1 r {");
_builder_1.newLine();
_builder_1.append("\t");
_builder_1.append("X refs <15:5|a1.b> <21:7|a1.b> <29:9|a1.b> { a1 { <46:2|b> refs <54:2|b> { a3 { ref a3 } } } }");
_builder_1.newLine();
_builder_1.append("\t");
_builder_1.append("Y refs b1.b2 Y.b1.b2 r.Y.b1.b2 { b1 { b2 { ref b2 } } }");
_builder_1.newLine();
_builder_1.append("}");
_builder_1.newLine();
_builder_1.append("--------------------------------------------------------------------------------");
_builder_1.newLine();
_builder_1.append("15 5 \"a1.a2\" -> \"a1.b\"");
_builder_1.newLine();
_builder_1.append("21 7 \"X.a1.a2\" -> \"a1.b\"");
_builder_1.newLine();
_builder_1.append("29 9 \"r.X.a1.a2\" -> \"a1.b\"");
_builder_1.newLine();
_builder_1.append("46 2 \"a2\" -> \"b\"");
_builder_1.newLine();
_builder_1.append("54 2 \"a2\" -> \"b\"");
_builder_1.newLine();
this._changeSerializerTestHelper.operator_tripleEquals(_endRecordChangesToTextDocuments, _builder_1);
}
@Test
public void testRenameFqn1ValueConversion() {
final InMemoryURIHandler fs = new InMemoryURIHandler();
StringConcatenation _builder = new StringConcatenation();
_builder.append("#1 r {");
_builder.newLine();
_builder.append("\t");
_builder.append("X refs ^a1.^a2 ^X.^a1.^a2 ^r.^X.^a1.^a2 { a1 { a2 refs ^a2 { a3 { ref ^a3 } } } }");
_builder.newLine();
_builder.append("\t");
_builder.append("Y refs ^b1.^b2 ^Y.^b1.^b2 ^r.^Y.^b1.^b2 { b1 { b2 { ref b2 } } }");
_builder.newLine();
_builder.append("}");
_builder.newLine();
Pair<String, String> _mappedTo = Pair.<String, String>of("inmemory:/file1.pstl", _builder.toString());
this._changeSerializerTestHelper.operator_add(fs, _mappedTo);
final ResourceSet rs = this._changeSerializerTestHelper.createResourceSet(fs);
final Node model = this._changeSerializerTestHelper.<Node>contents(rs, "inmemory:/file1.pstl", Node.class);
final IChangeSerializer serializer = this._changeSerializerTestHelper.newChangeSerializer();
final IChangeSerializer.IModification<Resource> _function = (Resource it) -> {
Node _head = IterableExtensions.<Node>head(IterableExtensions.<Node>head(IterableExtensions.<Node>head(model.getChildren()).getChildren()).getChildren());
_head.setName("b");
};
serializer.<Resource>addModification(model.eResource(), _function);
Assert.assertEquals(1, model.eResource().getResourceSet().getResources().size());
Collection<IEmfResourceChange> _endRecordChangesToTextDocuments = this._changeSerializerTestHelper.endRecordChangesToTextDocuments(serializer);
StringConcatenation _builder_1 = new StringConcatenation();
_builder_1.append("----------------- inmemory:/file1.pstl (syntax: <offset|text>) -----------------");
_builder_1.newLine();
_builder_1.append("#1 r {");
_builder_1.newLine();
_builder_1.append("\t");
_builder_1.append("X refs <15:7|a1.b> <23:10|a1.b> <34:13|a1.b> { a1 { <55:2|b> refs <63:3|b> { a3 { ref ^a3 } } } }");
_builder_1.newLine();
_builder_1.append("\t");
_builder_1.append("Y refs ^b1.^b2 ^Y.^b1.^b2 ^r.^Y.^b1.^b2 { b1 { b2 { ref b2 } } }");
_builder_1.newLine();
_builder_1.append("}");
_builder_1.newLine();
_builder_1.append("--------------------------------------------------------------------------------");
_builder_1.newLine();
_builder_1.append("15 7 \"^a1.^a2\" -> \"a1.b\"");
_builder_1.newLine();
_builder_1.append("23 10 \"^X.^a1.^a2\" -> \"a1.b\"");
_builder_1.newLine();
_builder_1.append("34 13 \"^r.^X.^a1.^a2\" -> \"a1.b\"");
_builder_1.newLine();
_builder_1.append("55 2 \"a2\" -> \"b\"");
_builder_1.newLine();
_builder_1.append("63 3 \"^a2\" -> \"b\"");
_builder_1.newLine();
this._changeSerializerTestHelper.operator_tripleEquals(_endRecordChangesToTextDocuments, _builder_1);
}
@Test
public void testResourceURIChange() {
final InMemoryURIHandler fs = new InMemoryURIHandler();

View file

@ -14,7 +14,6 @@ import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtext.testing.AbstractLanguageServerTest;
import org.eclipse.xtext.xbase.lib.Exceptions;
import org.junit.Ignore;
import org.junit.Test;
/**
@ -65,7 +64,6 @@ public class RenameTest2 extends AbstractLanguageServerTest {
}
@Test
@Ignore
public void testRenameContainer() {
try {
StringConcatenation _builder = new StringConcatenation();
@ -105,10 +103,10 @@ public class RenameTest2 extends AbstractLanguageServerTest {
_builder_1.append("Foo.fileawaretestlanguage : Baz [[2, 8] .. [2, 11]]");
_builder_1.newLine();
_builder_1.append(" ");
_builder_1.append("Baz [[3, 9] .. [3, 12]]");
_builder_1.append("Bar [[5, 5] .. [5, 16]]");
_builder_1.newLine();
_builder_1.append(" ");
_builder_1.append("Baz [[3, 6] .. [3, 9]]");
_builder_1.append("Bar [[6, 5] .. [6, 12]]");
_builder_1.newLine();
_builder_1.append("documentChanges : ");
_builder_1.newLine();

View file

@ -9,9 +9,11 @@ package org.eclipse.xtext.ide.tests.testlanguage;
import com.google.inject.Binder;
import com.google.inject.name.Names;
import org.eclipse.xtext.conversion.IValueConverterService;
import org.eclipse.xtext.ide.serializer.hooks.IReferenceUpdater;
import org.eclipse.xtext.ide.tests.testlanguage.AbstractPartialSerializationTestLanguageRuntimeModule;
import org.eclipse.xtext.ide.tests.testlanguage.ide.serializer.PartialSerializationTestLanguageReferenceUpdater;
import org.eclipse.xtext.ide.tests.testlanguage.scoping.PartialSerializationTestLanguageValueConverter;
import org.eclipse.xtext.resource.IResourceDescriptions;
import org.eclipse.xtext.resource.impl.LiveShadowedResourceDescriptions;
import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider;
@ -30,4 +32,9 @@ public class PartialSerializationTestLanguageRuntimeModule extends AbstractParti
public Class<? extends IReferenceUpdater> bindCleanupStrategy() {
return PartialSerializationTestLanguageReferenceUpdater.class;
}
@Override
public Class<? extends IValueConverterService> bindIValueConverterService() {
return PartialSerializationTestLanguageValueConverter.class;
}
}

View file

@ -58,7 +58,7 @@ public class PartialSerializationTestLanguageReferenceUpdater extends ReferenceU
List<IUpdatableReference> _updatableReferences = context.getUpdatableReferences();
for (final IUpdatableReference target : _updatableReferences) {
{
final EObjectDescriptionDeltaProvider.Delta delta = context.getEObjectDescriptionDeltas().findContainingDelta(target.getTargetEObject());
final EObjectDescriptionDeltaProvider.Delta delta = this.findContainingDelta(context.getEObjectDescriptionDeltas(), target.getTargetEObject());
if ((delta != null)) {
final QualifiedName original = IterableExtensions.<IEObjectDescription>head(delta.getSnapshot().getDescriptions()).getQualifiedName();
final QualifiedName modified = IterableExtensions.<IEObjectDescription>head(delta.getDescriptions()).getQualifiedName();

View file

@ -0,0 +1,28 @@
/**
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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
*/
package org.eclipse.xtext.ide.tests.testlanguage.scoping;
import com.google.inject.Inject;
import org.eclipse.xtext.common.services.DefaultTerminalConverters;
import org.eclipse.xtext.conversion.IValueConverter;
import org.eclipse.xtext.conversion.ValueConverter;
import org.eclipse.xtext.conversion.impl.QualifiedNameValueConverter;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
@SuppressWarnings("all")
public class PartialSerializationTestLanguageValueConverter extends DefaultTerminalConverters {
@Inject
private QualifiedNameValueConverter fqnc;
@ValueConverter(rule = "QualifiedName")
public IValueConverter<String> QualifiedName() {
return this.fqnc;
}
}

View file

@ -9,8 +9,6 @@ package org.eclipse.xtext.ide.serializer.hooks;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionDiffBuilder;
import org.eclipse.xtext.ide.serializer.impl.EObjectDescriptionDeltaProvider.Deltas;
import org.eclipse.xtext.resource.XtextResource;
@ -33,7 +31,5 @@ public interface IReferenceUpdaterContext {
void modifyModel(Runnable runnable);
void updateReference(EObject owner, EReference reference);
void updateReference(EObject owner, EReference reference, int index);
void updateReference(IUpdatableReference updatableReference);
}

View file

@ -12,6 +12,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
@ -34,45 +35,118 @@ public class EObjectDescriptionDeltaProvider {
public static class Delta {
private List<IEObjectDescription> descriptions = Lists.newArrayList();
private final EObject object;
private boolean qualifiedNameChanged;
private boolean simpleNameAndUserDataChanged;
private IEObjectSnapshot snapshot;
public Delta(EObject object) {
public Delta(EObject object, IEObjectSnapshot snapshot, List<IEObjectDescription> descriptions) {
super();
this.object = object;
this.snapshot = snapshot;
this.descriptions = descriptions;
this.simpleNameAndUserDataChanged = !forAll(this::isSimpleNameAndUserdataEqual);
this.qualifiedNameChanged = !forAll(this::isQualifiedNameEqual);
}
protected boolean forAll(BiFunction<IEObjectDescription, IEObjectDescription, Boolean> comparator) {
if (snapshot == null) {
return false;
}
List<IEObjectDescription> snap = snapshot.getDescriptions();
if (snap == null && descriptions == null) {
return true;
}
if (snap == null || descriptions == null) {
return false;
}
int size = descriptions.size();
if (size == 0 && snap.isEmpty()) {
return true;
}
if (size != snap.size()) {
return false;
}
if (size == 1) {
return comparator.apply(snap.get(0), descriptions.get(0));
}
Set<IEObjectDescription> candidates = Sets.newHashSet(snap);
FOR: for (IEObjectDescription d : descriptions) {
Iterator<IEObjectDescription> it = candidates.iterator();
while (it.hasNext()) {
IEObjectDescription s = it.next();
if (comparator.apply(d, s)) {
it.remove();
continue FOR;
}
}
return false;
}
return true;
}
public List<IEObjectDescription> getDescriptions() {
return descriptions;
}
public EObject getObject() {
return object;
}
public IEObjectSnapshot getSnapshot() {
return snapshot;
}
public EObject getObject() {
return object;
public boolean hasQualifiedNameChanged() {
return qualifiedNameChanged;
}
public boolean hasSimpleNameOrUserdataChanged() {
return simpleNameAndUserDataChanged;
}
protected boolean isQualifiedNameEqual(IEObjectDescription desc1, IEObjectDescription desc2) {
return desc1.getQualifiedName().equals(desc2.getQualifiedName());
}
protected boolean isSimpleNameAndUserdataEqual(IEObjectDescription oldDesc, IEObjectDescription newDesc) {
if (!oldDesc.getName().equals(newDesc.getName())) {
return false;
}
if (!isUserDataEqual(oldDesc, newDesc)) {
return false;
}
return true;
}
protected boolean isUserDataEqual(IEObjectDescription oldObj, IEObjectDescription newObj) {
String[] oldKeys = oldObj.getUserDataKeys();
String[] newKeys = newObj.getUserDataKeys();
if (oldKeys.length != newKeys.length)
return false;
for (String key : oldKeys) {
if (!Arrays.contains(newKeys, key))
return false;
String oldValue = oldObj.getUserData(key);
String newValue = newObj.getUserData(key);
if (!Objects.equal(oldValue, newValue))
return false;
}
return true;
}
}
public static class Deltas {
private Collection<IResourceSnapshot> snapshots;
private final Map<EObject, Delta> deltas = Maps.newLinkedHashMap();
private Collection<IResourceSnapshot> snapshots;
public Delta getDelta(EObject obj) {
return deltas.get(obj);
}
public Collection<IResourceSnapshot> getSnapshots() {
return snapshots;
}
public Delta findContainingDelta(EObject obj) {
EObject current = obj;
while (current != null) {
Delta delta = deltas.get(current);
if (delta != null) {
return delta;
}
current = current.eContainer();
}
return null;
}
}
public static class Group {
@ -87,37 +161,7 @@ public class EObjectDescriptionDeltaProvider {
}
protected Delta createDelta(EObject object, IEObjectSnapshot snapshot, List<IEObjectDescription> descriptions) {
Delta delta = new Delta(object);
delta.descriptions = descriptions;
delta.snapshot = snapshot;
if (snapshot == null || snapshot.getDescriptions() == null || delta.descriptions == null) {
return delta;
}
if (delta.descriptions.size() != snapshot.getDescriptions().size()) {
return delta;
}
if (delta.snapshot.getDescriptions().size() == 1 && delta.descriptions.size() == 1) {
if (!simpleNameAndUserDataEquals(delta.snapshot.getDescriptions().get(0), delta.descriptions.get(0))) {
return delta;
}
return null;
}
Set<IEObjectDescription> remaining = Sets.newHashSet(snapshot.getDescriptions());
FOR: for (IEObjectDescription desc : descriptions) {
Iterator<IEObjectDescription> it = remaining.iterator();
while (it.hasNext()) {
IEObjectDescription next = it.next();
if (simpleNameAndUserDataEquals(desc, next)) {
it.remove();
continue FOR;
}
}
return null;
}
if (remaining.isEmpty()) {
return delta;
}
return null;
return new Delta(object, snapshot, descriptions);
}
public Deltas getDelta(ChangeSerializer serializer, Collection<IResourceSnapshot> snapshots) {
@ -144,37 +188,11 @@ public class EObjectDescriptionDeltaProvider {
result.snapshots = snapshots;
for (Group g : groups.values()) {
Delta delta = createDelta(g.object, g.snapshot, g.descriptions);
if (delta != null) {
if (delta.hasQualifiedNameChanged() || delta.hasSimpleNameOrUserdataChanged()) {
result.deltas.put(delta.object, delta);
}
}
return result;
}
protected boolean simpleNameAndUserDataEquals(IEObjectDescription oldDesc, IEObjectDescription newDesc) {
if (!oldDesc.getName().equals(newDesc.getName())) {
return false;
}
if (!userDataEuqals(oldDesc, newDesc)) {
return false;
}
return true;
}
protected boolean userDataEuqals(IEObjectDescription oldObj, IEObjectDescription newObj) {
String[] oldKeys = oldObj.getUserDataKeys();
String[] newKeys = newObj.getUserDataKeys();
if (oldKeys.length != newKeys.length)
return false;
for (String key : oldKeys) {
if (!Arrays.contains(newKeys, key))
return false;
String oldValue = oldObj.getUserData(key);
String newValue = newObj.getUserData(key);
if (!Objects.equal(oldValue, newValue))
return false;
}
return true;
}
}

View file

@ -9,12 +9,14 @@ package org.eclipse.xtext.ide.serializer.impl;
import java.util.List;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.conversion.IValueConverterService;
import org.eclipse.xtext.conversion.ValueConverterException;
import org.eclipse.xtext.formatting2.regionaccess.IEObjectRegion;
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionDiffBuilder;
import org.eclipse.xtext.ide.serializer.hooks.IReferenceSnapshot;
@ -39,6 +41,9 @@ import com.google.inject.Inject;
*/
public class ReferenceUpdater implements IReferenceUpdater {
@Inject
private IQualifiedNameConverter converter;
@Inject
private LinkingHelper linkingHelper;
@ -51,6 +56,52 @@ public class ReferenceUpdater implements IReferenceUpdater {
@Inject
private IValueConverterService valueConverter;
protected boolean containsReferenceText(Delta delta, QualifiedName exp) {
DESC: for (IEObjectDescription desc : delta.getDescriptions()) {
QualifiedName cand = desc.getQualifiedName();
if (cand.getSegmentCount() >= exp.getSegmentCount()) {
for (int i = 1; i <= exp.getSegmentCount(); i++) {
String expSeg = exp.getSegment(exp.getSegmentCount() - i);
String candSeg = cand.getSegment(cand.getSegmentCount() - i);
if (!expSeg.equals(candSeg)) {
continue DESC;
}
}
}
return true;
}
return false;
}
protected IUpdatableReference createUpdatableReference(ISemanticRegion current) {
EReference ref = (EReference) current.getContainingFeature();
CrossReference crossRef = GrammarUtil.containingCrossReference(current.getGrammarElement());
EObject owner = current.getContainingRegion().getSemanticElement();
Object value = owner.eGet(ref);
if (value instanceof List<?>) {
List<?> targets = (List<?>) value;
int i = current.getIndexInContainingFeature();
EObject t = (EObject) targets.get(i);
return new UpdatableReference(owner, ref, i, t, crossRef, current);
} else if (value instanceof EObject) {
return new UpdatableReference(owner, ref, -1, (EObject) value, crossRef, current);
} else {
throw new IllegalStateException();
}
}
public Delta findContainingDelta(Deltas deltas, EObject obj) {
EObject current = obj;
while (current != null) {
Delta delta = deltas.getDelta(current);
if (delta != null && delta.hasSimpleNameOrUserdataChanged()) {
return delta;
}
current = current.eContainer();
}
return null;
}
protected String findValidName(IUpdatableReference updatable, IScope scope) {
Iterable<IEObjectDescription> elements = scope.getElements(updatable.getTargetEObject());
String ruleName = linkingHelper.getRuleNameFrom(updatable.getCrossReference());
@ -66,10 +117,24 @@ public class ReferenceUpdater implements IReferenceUpdater {
return null;
}
protected QualifiedName getQualifiedName(IUpdatableReference updatable) {
String text = updatable.getReferenceRegion().getText();
String ruleName = linkingHelper.getRuleNameFrom(updatable.getCrossReference());
try {
Object converted = valueConverter.toValue(text, ruleName, null);
if (converted != null) {
return converter.toQualifiedName(converted.toString());
}
} catch (ValueConverterException e) {
// do nothing
}
return null;
}
@Override
public boolean isAffected(Deltas deltas, RelatedResource resource) {
for (IReferenceSnapshot ref : resource.outgoingReferences) {
Delta delta = deltas.findContainingDelta(ref.getTarget().getObject());
Delta delta = deltas.getDelta(ref.getTarget().getObject());
if (delta != null) {
return true;
}
@ -77,41 +142,38 @@ public class ReferenceUpdater implements IReferenceUpdater {
return false;
}
protected boolean needsUpdating(Deltas deltas, EObject source, EObject target) {
Delta targetDelta = deltas.findContainingDelta(target);
if (targetDelta != null && targetDelta.getObject() == target)
protected boolean needsUpdating(IReferenceUpdaterContext context, IUpdatableReference ref) {
QualifiedName fqn = getQualifiedName(ref);
if (fqn == null) {
return false;
}
EObject target = ref.getTargetEObject();
Deltas deltas = context.getEObjectDescriptionDeltas();
Delta delta = deltas.getDelta(target);
if (delta != null && !containsReferenceText(delta, fqn)) {
return true;
Delta sourceDelta = deltas.findContainingDelta(source);
}
Delta targetDelta = findContainingDelta(deltas, target);
if (targetDelta != null && targetDelta.getObject() == target) {
return true;
}
Delta sourceDelta = findContainingDelta(deltas, ref.getSourceEObject());
return !Objects.equal(sourceDelta, targetDelta);
}
@Override
public void update(IReferenceUpdaterContext context) {
EObject root = context.getResource().getContents().get(0);
Deltas deltas = context.getEObjectDescriptionDeltas();
TreeIterator<EObject> iterator = EcoreUtil2.eAll(root);
while (iterator.hasNext()) {
EObject next = iterator.next();
for (EReference ref : next.eClass().getEAllReferences()) {
if (ref.isContainment()) {
continue;
}
Object value = next.eGet(ref);
if (value instanceof List<?>) {
List<?> targets = (List<?>) value;
for (int i = 0; i < targets.size(); i++) {
EObject t = (EObject) targets.get(i);
if (needsUpdating(deltas, next, t)) {
context.updateReference(next, ref, i);
}
}
} else if (value instanceof EObject) {
if (needsUpdating(deltas, next, (EObject) value)) {
context.updateReference(next, ref);
}
IEObjectRegion root = context.getModifyableDocument().getOriginalTextRegionAccess().regionForRootEObject();
ISemanticRegion current = root.getPreviousHiddenRegion().getNextSemanticRegion();
while (current != null) {
EStructuralFeature feature = current.getContainingFeature();
if (feature instanceof EReference && !((EReference) feature).isContainment()) {
IUpdatableReference updatable = createUpdatableReference(current);
if (needsUpdating(context, updatable)) {
context.updateReference(updatable);
}
}
current = current.getNextSemanticRegion();
}
}

View file

@ -9,19 +9,12 @@ package org.eclipse.xtext.ide.serializer.impl;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.formatting2.regionaccess.IEObjectRegion;
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionDiffBuilder;
import org.eclipse.xtext.ide.serializer.hooks.IReferenceUpdaterContext;
import org.eclipse.xtext.ide.serializer.hooks.IUpdatableReference;
import org.eclipse.xtext.ide.serializer.impl.EObjectDescriptionDeltaProvider.Deltas;
import org.eclipse.xtext.resource.XtextResource;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
/**
@ -70,33 +63,8 @@ public class ReferenceUpdaterContext implements IReferenceUpdaterContext {
}
@Override
public void updateReference(EObject owner, EReference reference) {
IEObjectRegion objectRegion = diffBuilder.getOriginalTextRegionAccess().regionForEObject(owner);
ISemanticRegion region = objectRegion.getRegionFor().feature(reference);
EObject target = (EObject) owner.eGet(reference);
CrossReference crossref = GrammarUtil.containingCrossReference(region.getGrammarElement());
updateReference(owner, reference, -1, region, target, crossref);
}
@Override
public void updateReference(EObject owner, EReference reference, int index) {
IEObjectRegion objectRegion = diffBuilder.getOriginalTextRegionAccess().regionForEObject(owner);
List<ISemanticRegion> regions = objectRegion.getRegionFor().features(reference);
ISemanticRegion region = regions.get(index);
EObject target = (EObject) ((List<?>) owner.eGet(reference)).get(index);
CrossReference crossref = GrammarUtil.containingCrossReference(region.getGrammarElement());
updateReference(owner, reference, index, region, target, crossref);
}
private void updateReference(EObject owner, EReference reference, int index, ISemanticRegion region, EObject target,
CrossReference crossref) {
Preconditions.checkNotNull(owner);
Preconditions.checkNotNull(reference);
Preconditions.checkArgument(!reference.isContainment());
if (region == null || target == null || target.eIsProxy() || crossref == null) {
return;
}
references.add(new UpdatableReference(owner, reference, index, target, crossref, region));
public void updateReference(IUpdatableReference updatableReference) {
references.add(updatableReference);
}
}

View file

@ -13,6 +13,8 @@ import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
import org.eclipse.xtext.ide.serializer.hooks.IUpdatableReference;
import com.google.common.base.Preconditions;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
@ -27,6 +29,12 @@ public class UpdatableReference implements IUpdatableReference {
public UpdatableReference(EObject owner, EReference reference, int index, EObject target, CrossReference crossref,
ISemanticRegion region) {
super();
Preconditions.checkNotNull(owner);
Preconditions.checkNotNull(reference);
Preconditions.checkArgument(!reference.isContainment());
Preconditions.checkNotNull(target);
Preconditions.checkNotNull(crossref);
Preconditions.checkNotNull(region);
this.owner = owner;
this.reference = reference;
this.index = index;

View file

@ -20,7 +20,7 @@ class FileAwareTestLanguageReferenceUpdater extends ReferenceUpdater {
val pkgName = names.getFullyQualifiedName(pkg)
val actual = pkg.imports.toMap[element]
val targets = context.updatableReferences.map[targetEObject].filter(Element)
val expected = targets.filter[names.getFullyQualifiedName(it).skipLast(1) != pkgName].toSet
val expected = targets.filter[!names.getFullyQualifiedName(it).startsWith(pkgName)].toSet
val toAdd = expected.filter[!actual.containsKey(it)].toSet
val toDelete = actual.filter[!expected.contains($0)]
if (!toAdd.isEmpty || !toDelete.isEmpty) {

View file

@ -1,6 +1,5 @@
package org.eclipse.xtext.testlanguages.fileAware.ide.refactoring;
import com.google.common.base.Objects;
import com.google.common.collect.Iterables;
import java.util.Map;
import java.util.Set;
@ -50,8 +49,8 @@ public class FileAwareTestLanguageReferenceUpdater extends ReferenceUpdater {
};
final Iterable<Element> targets = Iterables.<Element>filter(ListExtensions.<IUpdatableReference, EObject>map(context.getUpdatableReferences(), _function_1), Element.class);
final Function1<Element, Boolean> _function_2 = (Element it) -> {
QualifiedName _skipLast = this.names.getFullyQualifiedName(it).skipLast(1);
return Boolean.valueOf((!Objects.equal(_skipLast, pkgName)));
boolean _startsWith = this.names.getFullyQualifiedName(it).startsWith(pkgName);
return Boolean.valueOf((!_startsWith));
};
final Set<Element> expected = IterableExtensions.<Element>toSet(IterableExtensions.<Element>filter(targets, _function_2));
final Function1<Element, Boolean> _function_3 = (Element it) -> {