Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,16 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;

import java.util.List;

import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.AbstractRule;
Expand All @@ -42,6 +49,9 @@
public class AbstractSelectorFragmentProviderTest {
// CHECKSTYLE:ON

private static final String NAME_FEATURE = "name";
private static final String CHILDREN_FEATURE = "children";

@Inject
private ParseHelper<EObject> parseHelper;

Expand Down Expand Up @@ -78,6 +88,85 @@ public void testEscapedSelectorValue() {
assertFragmentMatchesAndResolves(grammar.eResource(), "/0/5(0='Foo')#0/2(3='foo.bar#')", grammar.getRules().get(0).getAlternatives());
}

@Test
public void testCaseSensitiveSelectorValue() {
EObject container = createContainer(true, "foo", "FOO");
for (EObject child : children(container)) {
assertSame(child, resolve(container, segment(child)));
}
}

@Test
public void testCaseInsensitiveSelectorValue() {
EObject container = createContainer(false, "foo");
EObject child = children(container).get(0);
assertSame(child, resolve(container, segment(child).replace("'foo'", "'FOO'")));
}

@Test
public void testCaseInsensitiveSiblingsDifferingInCase() {
EObject container = createContainer(false, "foo", "FOO");
for (EObject child : children(container)) {
assertSame(child, resolve(container, segment(child)));
}
}

/**
* Creates a container whose children are named by the given names, using a dynamic model whose child class is case sensitive if requested.
*/
private static EObject createContainer(final boolean caseSensitive, final String... names) {
EcoreFactory factory = EcoreFactory.eINSTANCE;
EPackage ePackage = factory.createEPackage();
ePackage.setName("test");
ePackage.setNsURI("http://www.foo.com/test/" + caseSensitive);
EClass childClass = factory.createEClass();
childClass.setName("Child");
if (caseSensitive) {
// the provider only checks the name of the super type
EClass marker = factory.createEClass();
marker.setName("ICaseSensitiveNamedElement");
marker.setInterface(true);
marker.setAbstract(true);
ePackage.getEClassifiers().add(marker);
childClass.getESuperTypes().add(marker);
}
EAttribute name = factory.createEAttribute();
name.setName(NAME_FEATURE);
name.setEType(EcorePackage.Literals.ESTRING);
childClass.getEStructuralFeatures().add(name);
EClass containerClass = factory.createEClass();
containerClass.setName("Container");
EReference childrenReference = factory.createEReference();
childrenReference.setName(CHILDREN_FEATURE);
childrenReference.setEType(childClass);
childrenReference.setContainment(true);
childrenReference.setUpperBound(-1);
containerClass.getEStructuralFeatures().add(childrenReference);
ePackage.getEClassifiers().add(childClass);
ePackage.getEClassifiers().add(containerClass);

EObject container = EcoreUtil.create(containerClass);
for (String childName : names) {
EObject child = EcoreUtil.create(childClass);
child.eSet(name, childName);
children(container).add(child);
}
return container;
}

@SuppressWarnings("unchecked")
private static List<EObject> children(final EObject container) {
return (List<EObject>) container.eGet(container.eClass().getEStructuralFeature(CHILDREN_FEATURE));
}

private static String segment(final EObject child) {
return new TestSelectorFragmentProvider().nameSegment(child);
}

private static EObject resolve(final EObject container, final String segment) {
return new TestSelectorFragmentProvider().getEObjectFromSegment(container, segment);
}

private void assertFragmentMatchesAndResolves(final Resource res, final String expectedFragment, final EObject obj) {
String fragment = res.getURIFragment(obj);
assertEquals(expectedFragment, fragment);
Expand Down Expand Up @@ -121,6 +210,13 @@ public boolean appendFragmentSegment(final EObject object, final StringBuilder b
return super.appendFragmentSegment(object, builder);
}

// the test runs in another bundle, so protected methods are only reachable from this subclass
String nameSegment(final EObject obj) {
StringBuilder builder = new StringBuilder();
computeSelectorFragmentSegment(obj, obj.eClass().getEStructuralFeature(NAME_FEATURE), false, builder);
return builder.toString();
}

private boolean appendFragmentSegment(final Grammar obj, final StringBuilder builder) {
return computeSelectorFragmentSegment(obj, XtextPackage.Literals.GRAMMAR__NAME, true, builder);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ protected boolean computeSelectorFragmentSegment(final EObject obj, final EStruc
int selectorIndex = 0;
final int objectIndex = containmentList.indexOf(obj);
for (int i = 0; i < objectIndex; i++) {
final Object value = containmentList.get(i).eGet(selectorFeature);
if ((selectorValue == null && value == null) || (selectorValue != null && selectorValue.equals(value))) {
final EObject sibling = containmentList.get(i);
final Object value = sibling.eGet(selectorFeature);
if (selectorValue == null ? value == null : value != null && matches(sibling, value.toString(), selectorValue.toString())) {
selectorIndex++;
}
}
Expand Down Expand Up @@ -133,6 +134,21 @@ private static Boolean isCaseSensitive(final EClass eClass) {
return caseSensitive;
}

/**
* Returns whether the given selector value text matches the expected value, ignoring case unless the object's class is case sensitive.
*
* @param object
* object whose selector value is matched
* @param text
* text of the object's selector value
* @param expected
* expected selector value, may be {@code null}
* @return {@code true} if the values match
*/
private static boolean matches(final EObject object, final String text, final String expected) {
return text.equalsIgnoreCase(expected) && (!isCaseSensitive(object.eClass()) || text.equals(expected));
}

@Override
@SuppressWarnings({"unchecked", "PMD.NPathComplexity"})
public EObject getEObjectFromSegment(final EObject container, final String segment) {
Expand Down Expand Up @@ -165,8 +181,7 @@ public EObject getEObjectFromSegment(final EObject container, final String segme
return object;
}
} else {
String text = value.toString();
if (text.equalsIgnoreCase(matchedValue) && (!isCaseSensitive(object.eClass()) || text.equals(matchedValue)) && index++ == matchedIndex) {
if (matches(object, value.toString(), matchedValue) && index++ == matchedIndex) {
return object;
}
}
Expand Down