diff --git a/com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/naming/QualifiedNameSegmentTreeLookupTest.java b/com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/naming/QualifiedNameSegmentTreeLookupTest.java index ffe58a7918..9fccb22092 100644 --- a/com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/naming/QualifiedNameSegmentTreeLookupTest.java +++ b/com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/naming/QualifiedNameSegmentTreeLookupTest.java @@ -22,6 +22,8 @@ import java.util.Collections; import java.util.List; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; import org.eclipse.emf.common.util.URI; import org.eclipse.xtext.naming.QualifiedName; import org.junit.jupiter.api.Test; @@ -32,6 +34,8 @@ @SuppressWarnings({"nls", "unused", "PMD.JUnitAssertionsShouldIncludeMessage"}) // CHECKSTYLE:CHECK-OFF MultipleStringLiteralsCheck public class QualifiedNameSegmentTreeLookupTest { + private static final Logger LOGGER = LogManager.getLogger(QualifiedNameSegmentTreeLookupTest.class); + private final QualifiedNameSegmentTreeLookup lookup = new QualifiedNameSegmentTreeLookup(URI.class, true); @Test @@ -173,6 +177,42 @@ public void testLoadStore() throws IOException, ClassNotFoundException { } } + @Test + public void testGetMappings() { + final QualifiedName a = name("A"); + final QualifiedName b = name("B"); + final QualifiedName c = name("A.C"); + final QualifiedName d = name("A.D"); + final QualifiedName e = name("B.E"); + final QualifiedName f = name("B.F"); + final QualifiedName g = name("A.C.G"); + final QualifiedName h = name("A.C.H"); + final QualifiedName i = name("A.D.I"); + final QualifiedName j = name("A.D.J"); + + List nameList = List.of(a, b, c, d, e, f, g, h, i, j); + for (QualifiedName qn : nameList) { + lookup.put(qn, uri(qn)); + } + + URI value = URI.createURI("scheme:/host"); + + lookup.put(c, value); + lookup.put(g, value); + lookup.put(h, value); + lookup.put(f, value); + lookup.put(b, value); + + Collection result = lookup.getMappings(value); + Collection expected = List.of(c, g, h, f, b); + + assertContentEquals(expected, result); + + URI noSuchValue = URI.createURI("scheme:/anotherHost"); + expected = lookup.getMappings(noSuchValue); + assertEquals(expected.size(), 0); + } + private QualifiedName name(final String str) { return QualifiedNames.safeQualifiedName(str); } diff --git a/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/naming/QualifiedNameLookup.java b/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/naming/QualifiedNameLookup.java index 2303b959e9..a9bf8b4a89 100644 --- a/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/naming/QualifiedNameLookup.java +++ b/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/naming/QualifiedNameLookup.java @@ -48,6 +48,15 @@ public interface QualifiedNameLookup extends ICache { */ Collection get(QualifiedNamePattern pattern, boolean excludeDuplicates); + /** + * Returns a collection of {@link QualifiedName}s the given value is mapped to. + * + * @param value + * value to find all mappings for, must not be {@code null} + * @return a {@link Collection} of {@link QualifiedName}s the given value is mapped to, never {@code null} + */ + Collection getMappings(T value); + /** * Adds a mapping for the given key and value. If there already are values associated with the given key, then the given value will be added (unless already * present). diff --git a/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/naming/QualifiedNameSegmentTreeLookup.java b/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/naming/QualifiedNameSegmentTreeLookup.java index f8fb2122c6..bc6730e24e 100644 --- a/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/naming/QualifiedNameSegmentTreeLookup.java +++ b/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/naming/QualifiedNameSegmentTreeLookup.java @@ -284,6 +284,27 @@ public void accept(final Visitor visitor) { } } + public List> getMappings(final Object value) { + List> result = new ArrayList<>(); + if (children != null) { + for (SegmentNode node : children) { + for (List mapping : node.getMappings(value)) { + if (!segment.isBlank()) { + mapping.add(0, segment); + } + result.add(mapping); + } + } + } + if (ArrayUtils.find(values, value) >= 0) { + List l = new ArrayList<>(1); + l.add(segment); + result.add(l); + } + + return result; + } + /** * Adopted from {@link java.util.Collections#binarySearch(List, Object)} which can't be used here because the element being searched has a different type. * @@ -538,6 +559,22 @@ public Collection get(final QualifiedNamePattern pattern, final boolean exclu return root.matches(pattern.lowerInclusive(), 0, root.find(pattern.upperExclusive(), 0, false), pattern.isRecursivePattern(), excludeDuplicates); } + @Override + public Collection getMappings(final T value) { + + if (value == null) { + throw new IllegalArgumentException("QualifiedNameLookup does not support null values"); //$NON-NLS-1$ + } + + List> mappings = root.getMappings(value); + List result = new ArrayList<>(mappings.size()); + for (List mapping : mappings) { + result.add(QualifiedName.create(mapping)); + } + + return result; + } + @Override @SuppressFBWarnings("AT_NONATOMIC_OPERATIONS_ON_SHARED_VARIABLE") public void put(final QualifiedName name, final T value) { diff --git a/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/naming/TreeSetLookup.java b/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/naming/TreeSetLookup.java index 45dbc79d30..381c02d219 100644 --- a/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/naming/TreeSetLookup.java +++ b/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/naming/TreeSetLookup.java @@ -10,6 +10,7 @@ *******************************************************************************/ package com.avaloq.tools.ddk.xtext.naming; +import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.Map; @@ -71,6 +72,19 @@ public Collection get(final QualifiedNamePattern pattern, final boolean exclu return result; } + @Override + public Collection getMappings(final T value) { + Collection mappings = new ArrayList<>(); + + for (Map.Entry entry : lookupMap.entrySet()) { + if (ArrayUtils.find(entry.getValue(), value) >= 0) { + mappings.add(entry.getKey()); + } + } + + return mappings; + } + @Override public void removeMappings(final T value) { Iterator> iter = lookupMap.entrySet().iterator();