Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various minor improvements for E4 EMF Xpath #2289

Merged
merged 4 commits into from
Sep 23, 2024
Merged
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
11 changes: 11 additions & 0 deletions bundles/org.eclipse.e4.emf.xpath/.settings/.api_filters
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<component id="org.eclipse.e4.emf.xpath" version="2">
<resource path="src/org/eclipse/e4/emf/xpath/XPathContext.java" type="org.eclipse.e4.emf.xpath.XPathContext">
<filter id="404000815">
<message_arguments>
<message_argument value="org.eclipse.e4.emf.xpath.XPathContext"/>
<message_argument value="stream(String, Class&lt;T&gt;)"/>
</message_arguments>
</filter>
</resource>
</component>
10 changes: 7 additions & 3 deletions bundles/org.eclipse.e4.emf.xpath/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Bundle-SymbolicName: org.eclipse.e4.emf.xpath
Bundle-Version: 0.4.300.qualifier
Bundle-Version: 0.5.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-17
Require-Bundle: org.apache.commons.jxpath;bundle-version="1.2.0",
org.eclipse.emf.ecore;bundle-version="2.35.0",
Require-Bundle: org.eclipse.emf.ecore;bundle-version="2.35.0",
org.eclipse.core.runtime;bundle-version="3.29.0"
Export-Package: org.eclipse.e4.emf.internal.xpath;x-internal:=true,
org.eclipse.e4.emf.internal.xpath.helper;x-friends:="org.eclipse.e4.emf.xpath.test,org.eclipse.e4.ui.model.workbench,org.eclipse.e4.ui.workbench",
org.eclipse.e4.emf.xpath
Import-Package: org.apache.commons.jxpath;version="[1.3.0,2.0.0)",
org.apache.commons.jxpath.ri;version="[1.3.0,2.0.0)",
org.apache.commons.jxpath.ri.compiler;version="[1.3.0,2.0.0)",
org.apache.commons.jxpath.ri.model;version="[1.3.0,2.0.0)",
org.apache.commons.jxpath.util;version="[1.3.0,2.0.0)"
Bundle-Vendor: %Bundle-Vendor
Automatic-Module-Name: org.eclipse.e4.emf.xpath
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2010, 2015 BestSolution.at and others.
* Copyright (c) 2010, 2024 BestSolution.at and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -20,18 +20,17 @@
/**
* Factory creating context using JXPath
*
* @param <Type>
* the object the XPath is created for
* @param <T> the object the XPath is created for
*/
public class JXPathContextFactoryImpl<Type> extends XPathContextFactory<Type> {
public class JXPathContextFactoryImpl<T> extends XPathContextFactory<T> {

@Override
public XPathContext newContext(XPathContext parentContext, Object contextBean) {
return new JXPathContextImpl(parentContext, contextBean);
}

@Override
public XPathContext newContext(Type contextBean) {
public XPathContext newContext(T contextBean) {
return new JXPathContextImpl(contextBean);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.NodeSet;
import org.apache.commons.jxpath.Pointer;
import org.apache.commons.jxpath.util.TypeUtils;
import org.eclipse.e4.emf.xpath.XPathContext;
import org.eclipse.emf.ecore.EObject;

Expand Down Expand Up @@ -84,12 +85,15 @@ public Object getValue(String xpath) {
}

@Override
public Object getValue(String xpath, Class<?> requiredType) {
return context.getValue(xpath, requiredType);
public <T> T getValue(String xpath, Class<T> requiredType) {
Object value = context.getValue(xpath, requiredType);
@SuppressWarnings("unchecked")
T typedValue = (T) TypeUtils.convert(value, requiredType);
return typedValue;
}

@Override
public <Type> Iterator<Type> iterate(String xpath) {
public <T> Iterator<T> iterate(String xpath) {
return context.iterate(xpath);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2010, 2015 BestSolution.at and others.
* Copyright (c) 2010, 2024 BestSolution.at and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -14,6 +14,12 @@
package org.eclipse.e4.emf.xpath;

import java.util.Iterator;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import org.apache.commons.jxpath.util.TypeUtils;

/**
* Context in which the xpath is executed
Expand Down Expand Up @@ -42,19 +48,35 @@ public interface XPathContext {
* required type
* @return Object found
*/
Object getValue(String xpath, Class<?> requiredType);
<T> T getValue(String xpath, Class<T> requiredType);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the method signature is the same after generic erasure this is a binary and source compatible change.
But as far as I can tell, this method and its overload is not used within the SDK at all, besides tests.


/**
* Traverses the xpath and returns an Iterator of all results found for the
* path. If the xpath matches no properties in the graph, the Iterator will
* be empty, but not null.
* path. If the xpath matches no properties in the graph, the Iterator will be
* empty, but not null.
*
* @param <O>
* the expected object type
* @param <T> the expected object type
*
* @param xpath
* to iterate
* @param xpath to iterate
* @return Iterator&lt;Object&gt;
*/
<O> Iterator<O> iterate(String xpath);
<T> Iterator<T> iterate(String xpath);

/**
* Traverses the xpath and returns an {@link Stream} of all results found for
* the path. If the xpath matches no properties in the graph, the stream will be
* empty.
*
* @param <T> the expected object type
* @param xpath the xpath expression to iterate
* @param type the type of elements in the returned stream
* @return a stream of elements matching the specified xpath and of the given
* type
* @since 0.5
*/
default <T> Stream<T> stream(String xpath, Class<T> type) {
Iterator<?> iterator = iterate(xpath);
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false)
.filter(e -> TypeUtils.canConvert(e, type)).map(e -> TypeUtils.convert(e, type)).map(type::cast);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2010, 2015 BestSolution.at and others.
* Copyright (c) 2010, 2024 BestSolution.at and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -18,39 +18,34 @@
/**
* Factory responsible to create an XPath-Context
*
* @param <Type>
* the object type the XPath is created for
* @param <T> the object type the XPath is created for
*/
public abstract class XPathContextFactory<Type extends Object> {
public abstract class XPathContextFactory<T> {

/**
* Creates a new XPathContext with the specified object as the root node.
*
* @param contextBean
* Object
* @param contextBean Object
* @return XPathContext
*/
public abstract XPathContext newContext(Type contextBean);
public abstract XPathContext newContext(T contextBean);

/**
* Creates a new XPathContext with the specified bean as the root node and
* the specified parent context. Variables defined in a parent context can
* be referenced in XPaths passed to the child context.
* Creates a new XPathContext with the specified bean as the root node and the
* specified parent context. Variables defined in a parent context can be
* referenced in XPaths passed to the child context.
*
* @param parentContext
* parent context
* @param contextBean
* Object
* @param parentContext parent context
* @param contextBean Object
* @return XPathContext
*/
public abstract XPathContext newContext(XPathContext parentContext, Type contextBean);
public abstract XPathContext newContext(XPathContext parentContext, T contextBean);

/**
* @param <Type>
* the object type the xpath is created for
* @param <T> the object type the xpath is created for
* @return Create a new XPath-Factory
*/
public static <Type> XPathContextFactory<Type> newInstance() {
public static <T> XPathContextFactory<T> newInstance() {
return new JXPathContextFactoryImpl<>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.e4.ui.model.workbench;singleton:=true
Bundle-Version: 2.4.300.qualifier
Bundle-Version: 2.4.400.qualifier
Bundle-Vendor: %providerName
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
package org.eclipse.e4.ui.model.fragment.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import org.eclipse.e4.emf.xpath.EcoreXPathContextFactory;
Expand Down Expand Up @@ -355,31 +353,22 @@ private void mergeIdList(MApplication application, List<MApplicationElement> ret
private void mergeXPath(MApplication application, List<MApplicationElement> ret, String xPath) {
List<MApplicationElement> targetElements;
if ("/".equals(xPath)) {
targetElements = Collections.singletonList(application);
targetElements = List.of(application);
} else {
XPathContextFactory<EObject> f = EcoreXPathContextFactory.newInstance();
XPathContext xpathContext = f.newContext((EObject) application);
Iterator<Object> i = xpathContext.iterate(xPath);

targetElements = new ArrayList<>();
try {
while (i.hasNext()) {
Object obj = i.next();
if (obj instanceof MApplicationElement) {
MApplicationElement o = (MApplicationElement) obj;
targetElements.add(o);
}
}
targetElements = xpathContext.stream(xPath, MApplicationElement.class).toList();
} catch (Exception ex) {
targetElements = List.of();
// custom xpath functions will throw exceptions
ex.printStackTrace();
}
}
for (MApplicationElement targetElement : targetElements) {
EStructuralFeature feature = ((EObject) targetElement).eClass().getEStructuralFeature(getFeaturename());
if (feature != null) {
List<MApplicationElement> elements;
elements = new ArrayList<>();
List<MApplicationElement> elements = new ArrayList<>();
for (MApplicationElement element : getElements()) {
elements.add((MApplicationElement) EcoreUtil.copy((EObject) element));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ public void testAccessingTheMainMenu() {
Object menu = xpathContext.getValue("//mainMenu");
assertNotNull(menu);
assertTrue(menu instanceof MMenu);

MMenu mMenu = xpathContext.getValue("//mainMenu", MMenu.class);
assertNotNull(mMenu);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue;

import java.util.Iterator;
import java.util.List;

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

RootImpl rootApplication = xpathContext.getValue("/", RootImpl.class);
assertNotNull(rootApplication);

application = xpathContext.getValue(".");
assertNotNull(application);
assertSame(RootImpl.class, application.getClass());
Expand All @@ -85,14 +89,19 @@ public void testSimpleQuery() {
assertNotNull(application);
assertSame(RootImpl.class, application.getClass());

rootApplication = xpathContext.getValue(".[@id='root']", RootImpl.class);
assertNotNull(rootApplication);

assertEquals("element1",xpathContext.getValue("nodes[1]/@id"));

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

ExtendedNodeImpl extendedNode = xpathContext.getValue("//.[ecore:eClassName(.)='ExtendedNode']",
ExtendedNodeImpl.class);
assertNotNull(extendedNode);
}


@Test
public void testMenuQuery() {
Object application = xpathContext.getValue("/");
Expand All @@ -109,8 +118,10 @@ public void testMenuQuery() {
assertSame(MenuImpl.class, i.next().getClass());
// EMF model has a loop in it, it just goes back to the top
//assertFalse(i.hasNext());
}


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

}
Loading