Skip to content

Commit 95cf53a

Browse files
committed
Add XPathContext.stream() and strengthen type of getValue(String, Class)
1 parent 61a15a0 commit 95cf53a

File tree

7 files changed

+64
-22
lines changed

7 files changed

+64
-22
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+
<component id="org.eclipse.e4.emf.xpath" version="2">
3+
<resource path="src/org/eclipse/e4/emf/xpath/XPathContext.java" type="org.eclipse.e4.emf.xpath.XPathContext">
4+
<filter id="404000815">
5+
<message_arguments>
6+
<message_argument value="org.eclipse.e4.emf.xpath.XPathContext"/>
7+
<message_argument value="stream(String, Class&lt;T&gt;)"/>
8+
</message_arguments>
9+
</filter>
10+
</resource>
11+
</component>

bundles/org.eclipse.e4.emf.xpath/META-INF/MANIFEST.MF

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
22
Bundle-ManifestVersion: 2
33
Bundle-Name: %Bundle-Name
44
Bundle-SymbolicName: org.eclipse.e4.emf.xpath
5-
Bundle-Version: 0.4.300.qualifier
5+
Bundle-Version: 0.5.0.qualifier
66
Bundle-RequiredExecutionEnvironment: JavaSE-17
77
Require-Bundle: org.eclipse.emf.ecore;bundle-version="2.35.0",
88
org.eclipse.core.runtime;bundle-version="3.29.0"

bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/internal/xpath/JXPathContextImpl.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.apache.commons.jxpath.JXPathContext;
2222
import org.apache.commons.jxpath.NodeSet;
2323
import org.apache.commons.jxpath.Pointer;
24+
import org.apache.commons.jxpath.util.TypeUtils;
2425
import org.eclipse.e4.emf.xpath.XPathContext;
2526
import org.eclipse.emf.ecore.EObject;
2627

@@ -84,8 +85,11 @@ public Object getValue(String xpath) {
8485
}
8586

8687
@Override
87-
public Object getValue(String xpath, Class<?> requiredType) {
88-
return context.getValue(xpath, requiredType);
88+
public <T> T getValue(String xpath, Class<T> requiredType) {
89+
Object value = context.getValue(xpath, requiredType);
90+
@SuppressWarnings("unchecked")
91+
T typedValue = (T) TypeUtils.convert(value, requiredType);
92+
return typedValue;
8993
}
9094

9195
@Override

bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/XPathContext.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
package org.eclipse.e4.emf.xpath;
1515

1616
import java.util.Iterator;
17+
import java.util.Spliterator;
18+
import java.util.Spliterators;
19+
import java.util.stream.Stream;
20+
import java.util.stream.StreamSupport;
21+
22+
import org.apache.commons.jxpath.util.TypeUtils;
1723

1824
/**
1925
* Context in which the xpath is executed
@@ -42,7 +48,7 @@ public interface XPathContext {
4248
* required type
4349
* @return Object found
4450
*/
45-
Object getValue(String xpath, Class<?> requiredType);
51+
<T> T getValue(String xpath, Class<T> requiredType);
4652

4753
/**
4854
* Traverses the xpath and returns an Iterator of all results found for the
@@ -55,4 +61,22 @@ public interface XPathContext {
5561
* @return Iterator&lt;Object&gt;
5662
*/
5763
<T> Iterator<T> iterate(String xpath);
64+
65+
/**
66+
* Traverses the xpath and returns an {@link Stream} of all results found for
67+
* the path. If the xpath matches no properties in the graph, the stream will be
68+
* empty.
69+
*
70+
* @param <T> the expected object type
71+
* @param xpath the xpath expression to iterate
72+
* @param type the type of elements in the returned stream
73+
* @return a stream of elements matching the specified xpath and of the given
74+
* type
75+
* @since 0.5
76+
*/
77+
default <T> Stream<T> stream(String xpath, Class<T> type) {
78+
Iterator<?> iterator = iterate(xpath);
79+
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false)
80+
.filter(e -> TypeUtils.canConvert(e, type)).map(e -> TypeUtils.convert(e, type)).map(type::cast);
81+
}
5882
}

bundles/org.eclipse.e4.ui.model.workbench/src/org/eclipse/e4/ui/model/fragment/impl/StringModelFragmentImpl.java

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
package org.eclipse.e4.ui.model.fragment.impl;
1717

1818
import java.util.ArrayList;
19-
import java.util.Collections;
20-
import java.util.Iterator;
2119
import java.util.List;
2220
import java.util.regex.Pattern;
2321
import org.eclipse.e4.emf.xpath.EcoreXPathContextFactory;
@@ -355,31 +353,22 @@ private void mergeIdList(MApplication application, List<MApplicationElement> ret
355353
private void mergeXPath(MApplication application, List<MApplicationElement> ret, String xPath) {
356354
List<MApplicationElement> targetElements;
357355
if ("/".equals(xPath)) {
358-
targetElements = Collections.singletonList(application);
356+
targetElements = List.of(application);
359357
} else {
360358
XPathContextFactory<EObject> f = EcoreXPathContextFactory.newInstance();
361359
XPathContext xpathContext = f.newContext((EObject) application);
362-
Iterator<Object> i = xpathContext.iterate(xPath);
363-
364-
targetElements = new ArrayList<>();
365360
try {
366-
while (i.hasNext()) {
367-
Object obj = i.next();
368-
if (obj instanceof MApplicationElement) {
369-
MApplicationElement o = (MApplicationElement) obj;
370-
targetElements.add(o);
371-
}
372-
}
361+
targetElements = xpathContext.stream(xPath, MApplicationElement.class).toList();
373362
} catch (Exception ex) {
363+
targetElements = List.of();
374364
// custom xpath functions will throw exceptions
375365
ex.printStackTrace();
376366
}
377367
}
378368
for (MApplicationElement targetElement : targetElements) {
379369
EStructuralFeature feature = ((EObject) targetElement).eClass().getEStructuralFeature(getFeaturename());
380370
if (feature != null) {
381-
List<MApplicationElement> elements;
382-
elements = new ArrayList<>();
371+
List<MApplicationElement> elements = new ArrayList<>();
383372
for (MApplicationElement element : getElements()) {
384373
elements.add((MApplicationElement) EcoreUtil.copy((EObject) element));
385374
}

tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesApplicationTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ public void testAccessingTheMainMenu() {
8585
Object menu = xpathContext.getValue("//mainMenu");
8686
assertNotNull(menu);
8787
assertTrue(menu instanceof MMenu);
88+
89+
MMenu mMenu = xpathContext.getValue("//mainMenu", MMenu.class);
90+
assertNotNull(mMenu);
8891
}
8992

9093
@Test

tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesTestCase.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import static org.junit.Assert.assertTrue;
2222

2323
import java.util.Iterator;
24+
import java.util.List;
2425

2526
import org.apache.commons.jxpath.JXPathNotFoundException;
2627
import org.eclipse.e4.emf.xpath.EcoreXPathContextFactory;
@@ -75,6 +76,9 @@ public void testSimpleQuery() {
7576
assertNotNull(application);
7677
assertSame(RootImpl.class, application.getClass());
7778

79+
RootImpl rootApplication = xpathContext.getValue("/", RootImpl.class);
80+
assertNotNull(rootApplication);
81+
7882
application = xpathContext.getValue(".");
7983
assertNotNull(application);
8084
assertSame(RootImpl.class, application.getClass());
@@ -85,14 +89,19 @@ public void testSimpleQuery() {
8589
assertNotNull(application);
8690
assertSame(RootImpl.class, application.getClass());
8791

92+
rootApplication = xpathContext.getValue(".[@id='root']", RootImpl.class);
93+
assertNotNull(rootApplication);
94+
8895
assertEquals("element1",xpathContext.getValue("nodes[1]/@id"));
8996

9097
assertEquals(NodeImpl.class, xpathContext.getValue("//.[@id='element2.2']").getClass());
9198
assertEquals(ExtendedNodeImpl.class,xpathContext.getValue("//.[ecore:eClassName(.)='ExtendedNode']").getClass());
9299

100+
ExtendedNodeImpl extendedNode = xpathContext.getValue("//.[ecore:eClassName(.)='ExtendedNode']",
101+
ExtendedNodeImpl.class);
102+
assertNotNull(extendedNode);
93103
}
94104

95-
96105
@Test
97106
public void testMenuQuery() {
98107
Object application = xpathContext.getValue("/");
@@ -109,8 +118,10 @@ public void testMenuQuery() {
109118
assertSame(MenuImpl.class, i.next().getClass());
110119
// EMF model has a loop in it, it just goes back to the top
111120
//assertFalse(i.hasNext());
112-
}
113-
114121

122+
List<MenuImpl> list = xpathContext.stream("//.[@id='menu.1']", MenuImpl.class).toList();
123+
// EMF model has a loop in it, it just goes back to the top
124+
assertEquals(26, list.size());
125+
}
115126

116127
}

0 commit comments

Comments
 (0)