From d596f1756293dce50770f2c13eb267afaad064c0 Mon Sep 17 00:00:00 2001 From: Hannes Wellmann Date: Tue, 4 Jul 2023 23:08:53 +0200 Subject: [PATCH 1/8] [WIP] [E4 Xpath] Replace apache.commons.jxpath by javax.xml.xpath --- .../e4/emf/xpath/JavaXPathFactory.java | 39 ++ .../internal/java/JavaXPathFactoryImpl.java | 620 ++++++++++++++++++ bundles/org.eclipse.e4.emf.xpath/widgets.xml | 5 + .../test/ExampleQueriesApplicationTest.java | 18 +- 4 files changed, 679 insertions(+), 3 deletions(-) create mode 100644 bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/JavaXPathFactory.java create mode 100644 bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java create mode 100644 bundles/org.eclipse.e4.emf.xpath/widgets.xml diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/JavaXPathFactory.java b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/JavaXPathFactory.java new file mode 100644 index 00000000000..60464da530f --- /dev/null +++ b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/JavaXPathFactory.java @@ -0,0 +1,39 @@ +package org.eclipse.e4.emf.xpath; + +import org.eclipse.e4.emf.xpath.internal.java.JavaXPathFactoryImpl; +import org.eclipse.emf.ecore.EObject; + +/** + * @since 0.5 + */ +public abstract class JavaXPathFactory { + /** + * Creates a new XPathContext with the specified object as the root node. + * + * @param contextBean Object + * @return XPathContext + */ + 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. + * + * @param parentContext parent context + * @param contextBean Object + * @return XPathContext + */ + public abstract XPathContext newContext(XPathContext parentContext, T contextBean); + + /** + * @param the object type the xpath is created for + * @return Create a new XPath-Factory + */ + public static XPathContextFactory newInstance() { + return new JavaXPathFactoryImpl<>(); + } + + + +} diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java new file mode 100644 index 00000000000..78f92d3ea32 --- /dev/null +++ b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java @@ -0,0 +1,620 @@ +package org.eclipse.e4.emf.xpath.internal.java; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayDeque; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Queue; +import java.util.Spliterators; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathEvaluationResult; +import javax.xml.xpath.XPathEvaluationResult.XPathResultType; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathFactory; +import javax.xml.xpath.XPathNodes; + +import org.eclipse.e4.emf.xpath.XPathContext; +import org.eclipse.e4.emf.xpath.XPathContextFactory; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.w3c.dom.Attr; +import org.w3c.dom.DOMException; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.TypeInfo; +import org.w3c.dom.UserDataHandler; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSSerializer; + +public class JavaXPathFactoryImpl extends XPathContextFactory { + + public static void main(String[] args) throws Exception { + // parse the XML as a W3C Document + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document document; +// FileInputStream inputStream = new FileInputStream(new File("widgets.xml")); + ByteArrayInputStream inputStream = new ByteArrayInputStream(""" + + + + + + """.getBytes(StandardCharsets.UTF_8)); + try (InputStream stream = inputStream) { + document = builder.parse(new File("widgets.xml")); + } + document = builder.newDocument(); + Element root = document.createElement("foo"); + root.appendChild(document.createElement("bar")); + root.appendChild(document.createElement("bar")); + root.appendChild(document.createElement("bar")); + document.appendChild(root); + + // Get an XPath object and evaluate the expression + XPath xpath = XPathFactory.newInstance().newXPath(); + String expression = "/foo/bar"; + Node evaluateExpression = xpath.evaluateExpression(expression, document, Node.class); + Node widgetNode = (Node) xpath.evaluate(expression, document, XPathConstants.NODE); + + // or using the evaluateExpression method + Node widgetNode2 = xpath.evaluateExpression(expression, document, Node.class); + } + + @Override + public XPathContext newContext(T contextBean) { + return newContext(null, contextBean); + } + + @Override + public XPathContext newContext(XPathContext parentContext, T contextBean) { + if (!(contextBean instanceof EObject contextObject)) { + throw new IllegalArgumentException(); + } + // TODO: consider parent-context (may be null) + + XPathFactory factory = XPathFactory.newInstance(); +// factory.setXPathFunctionResolver((functionName, arity) -> null); +// factory.setXPathVariableResolver(e -> null); + XPath xpath = factory.newXPath(); + + DocumentBuilder documentBuilder; + try { + documentBuilder = DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new IllegalStateException(e); + } + Document proxyDoc = documentBuilder.newDocument(); + + Map proxy2object = new HashMap<>(); + Map object2proxy = new HashMap<>(); + + Queue queue = new ArrayDeque<>(); + queue.add(contextObject); + Element rootElement = createElement(contextObject, proxyDoc, proxyDoc); + object2proxy.put(contextObject, rootElement); + proxy2object.put(rootElement, contextObject); + + while (!queue.isEmpty()) { + EObject parent = queue.remove(); + Element parentNode = object2proxy.get(parent); + List eContents = parent.eContents(); + for (EObject childObject : eContents) { + Element childElement = createElement(childObject, parentNode, proxyDoc); + proxy2object.put(childElement, childObject); + object2proxy.put(childObject, childElement); + queue.add(childObject); + } + } + DOMImplementation implementation = proxyDoc.getImplementation(); + final DOMImplementationLS domImplementation = (DOMImplementationLS) implementation; + LSSerializer serial = domImplementation.createLSSerializer(); + serial.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE); + String writeToString = serial.writeToString(proxyDoc); + System.out.println(writeToString); +// try { +// proxyDoc = documentBuilder.parse(new ByteArrayInputStream(writeToString.getBytes(StandardCharsets.UTF_16))); +// +// } catch (SAXException | IOException e) { +// throw new IllegalStateException(e); +// } + + return new XPathContext() { + @Override + public Object getValue(String xpath) { + Iterator iterate = iterate(xpath); + return iterate.hasNext() ? iterate.next() : null; + } + + @Override + public Object getValue(String xpath, Class requiredType) { + return requiredType.cast(getValue(xpath)); + } + + @Override + @SuppressWarnings("unchecked") + public Iterator iterate(String xpath) { + return (Iterator) stream(xpath, Object.class).iterator(); + } + + public Stream stream(String path, Class type) { + try { + EObjectNodePointer item = new EObjectNodePointer(contextObject); + XPathEvaluationResult evaluateExpression = xpath.evaluateExpression(path, item); + XPathNodes value1 = (XPathNodes) evaluateExpression.value(); + if (value1.size() > 0) { + Node node = value1.get(0); + System.out.println(node); + } + + path = "/Application/TrimmedWindow"; + XPathEvaluationResult result1 = xpath.evaluateExpression(path, rootElement); + XPathNodes nodes2 = (XPathNodes) result1.value(); + if (nodes2.size() > 0) { + Node node = nodes2.get(0); + + System.out.println(node); + } + XPathExpression expr = xpath.compile(path); + XPathEvaluationResult result = expr.evaluateExpression(rootElement, XPathEvaluationResult.class); + XPathResultType type2 = result.type(); + Object value = result.value(); + + XPathNodes nodes = expr.evaluateExpression(rootElement, XPathNodes.class); + Stream stream = StreamSupport + .stream(Spliterators.spliterator(nodes.iterator(), nodes.size(), 0), false); + return stream.map(proxy2object::get).map(type::cast); + } catch (Exception e1) { + throw new IllegalStateException(e1); + } + } + + }; + } + + private Element createElement(EObject childObject, Node parentElement, Document doc) { + Document document = doc; // parentElement.getOwnerDocument(); + Element child = document.createElement(childObject.eClass().getName()); + List allAttributes = childObject.eClass().getEAllAttributes(); + for (EAttribute attribute : allAttributes) { + if (childObject.eIsSet(attribute)) { + Object value = childObject.eGet(attribute); + String stringValue = EcoreUtil.convertToString(attribute.getEAttributeType(), value); + child.setAttribute(attribute.getName(), stringValue); + } + } + parentElement.appendChild(child); + return child; + } + + private static class EObjectNodePointer implements Element { + + private final String name; + private final Document document; + private final EObject eObject; + private final EObjectNodePointer parent; +// private final Map obj2nodes; + + public EObjectNodePointer(EObject eObject) throws ParserConfigurationException { + this.name = eObject.eClass().getName(); + this.eObject = eObject; + this.parent = null; + // TODO: check if document is necessary + this.document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + } + + public EObjectNodePointer(String name, EObject eObject, +// Map obj2nodes, + EObjectNodePointer parent, Document document) { + this.name = name; + this.eObject = eObject; +// this.obj2nodes = obj2nodes; + this.parent = parent; + this.document = document; + } + + // TODO: hashCode/equals? + + @Override + public String getNodeName() { + return name; + } + + @Override + public String getTagName() { + return name; + } + + @Override + public String getNodeValue() throws DOMException { + throw unsupportedOperation(); + } + + @Override + public void setNodeValue(String nodeValue) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public short getNodeType() { + return ELEMENT_NODE; + } + + @Override + public Node getParentNode() { + return parent; + } + + @Override + public boolean hasChildNodes() { + EClass eClass = eObject.eClass(); + return availableAttributes(eClass.getEAllAttributes()).findAny().isPresent() + || availableReferences(eClass).findAny().isPresent(); + } + + @Override + public NodeList getChildNodes() { + EClass eClass = eObject.eClass(); + var attributeNodes = availableAttributes(eClass.getEAllAttributes()).map(this::createAttributeNode); + var referenceNodes = availableReferences(eClass).flatMap(this::createReferenceNodes); + List childNodes = Stream.concat(attributeNodes, referenceNodes).toList(); + return new NodeList() { + + @Override + public Node item(int index) { + return childNodes.get(index); + } + + @Override + public int getLength() { + return childNodes.size(); + } + }; + } + + private Stream availableReferences(EClass eClass) { + return eClass.getEAllReferences().stream().filter(eObject::eIsSet); + } + + private Stream createReferenceNodes(EReference ref) { + Object value = this.eObject.eGet(ref); + Stream stream = value instanceof List list ? list.stream() : Stream.of(value); + return stream.map(EObject.class::cast) + .map(e -> new EObjectNodePointer(ref.getName(), e, /* obj2nodes, */ this, document)); + } + + @Override + public Node getFirstChild() { + throw unsupportedOperation(); + } + + @Override + public Node getLastChild() { + throw unsupportedOperation(); + } + + @Override + public Node getPreviousSibling() { + throw unsupportedOperation(); + } + + @Override + public Node getNextSibling() { + throw unsupportedOperation(); + } + + @Override + public boolean hasAttributes() { + return availableAttributes(eObject.eClass().getEAllAttributes()).findAny().isPresent(); + } + + private Stream availableAttributes(List allAttributes) { + return allAttributes.stream().filter(eObject::eIsSet); + } + + private Node createAttributeNode(EAttribute attribute) { + Object value = eObject.eIsSet(attribute) ? eObject.eGet(attribute) : null; + Attr attributeNode = document.createAttribute(attribute.getName()); + String strValue = EcoreUtil.convertToString(attribute.getEAttributeType(), value); + attributeNode.setValue(strValue); + return attributeNode; + } + + @Override + public NamedNodeMap getAttributes() { + List allAttributes = eObject.eClass().getEAllAttributes(); + return new NamedNodeMap() { + + @Override + public Node setNamedItemNS(Node arg) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node setNamedItem(Node arg) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node removeNamedItemNS(String namespaceURI, String localName) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node removeNamedItem(String name) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node item(int index) { + var attribute = availableAttributes(allAttributes).skip(index).findFirst(); + return attribute.isPresent() ? createAttributeNode(attribute.get()) : null; + } + + @Override + public Node getNamedItemNS(String namespaceURI, String localName) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node getNamedItem(String name) { + var attribute = allAttributes.stream().filter(a -> a.getName().equals(name)).findFirst(); + return attribute.isPresent() ? createAttributeNode(attribute.get()) : null; + } + + @Override + public int getLength() { + return (int) availableAttributes(allAttributes).count(); + } + }; + } + + @Override + public String getAttribute(String name) { + return getAttributes().getNamedItem(name).getNodeValue(); + } + + @Override + public Document getOwnerDocument() { + return document; + } + + @Override + public boolean isSameNode(Node other) { + return other instanceof EObjectNodePointer node // + && node.name == name && node.eObject == eObject; + } + + @Override + public boolean isEqualNode(Node arg) { + return arg instanceof EObjectNodePointer node && Objects.equals(node.eObject, eObject); + } + + @Override + public String toString() { + return "[" + getTagName() + availableAttributes(eObject.eClass().getEAllAttributes()) + .map(a -> a.getName() + "=" + eObject.eGet(a)).collect(Collectors.joining(" ", " ", "]")); + } + + private static UnsupportedOperationException unsupportedOperation() { + return new UnsupportedOperationException(); + } + + @Override + public Node insertBefore(Node newChild, Node refChild) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node replaceChild(Node newChild, Node oldChild) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node removeChild(Node oldChild) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node appendChild(Node newChild) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node cloneNode(boolean deep) { + throw unsupportedOperation(); + } + + @Override + public void normalize() { + throw unsupportedOperation(); + } + + @Override + public boolean isSupported(String feature, String version) { + throw unsupportedOperation(); + } + + @Override + public String getNamespaceURI() { + return null; + } + + @Override + public String getPrefix() { + throw unsupportedOperation(); + } + + @Override + public void setPrefix(String prefix) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public String getLocalName() { + return null; + } + + @Override + public String getBaseURI() { + throw unsupportedOperation(); + } + + @Override + public short compareDocumentPosition(Node other) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public String getTextContent() throws DOMException { + throw unsupportedOperation(); + } + + @Override + public void setTextContent(String textContent) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public String lookupPrefix(String namespaceURI) { + throw unsupportedOperation(); + } + + @Override + public boolean isDefaultNamespace(String namespaceURI) { + throw unsupportedOperation(); + } + + @Override + public String lookupNamespaceURI(String prefix) { + throw unsupportedOperation(); + } + + @Override + public Object getFeature(String feature, String version) { + throw unsupportedOperation(); + } + + @Override + public Object setUserData(String key, Object data, UserDataHandler handler) { + throw unsupportedOperation(); + } + + @Override + public Object getUserData(String key) { + throw unsupportedOperation(); + } + + @Override + public void setAttribute(String name, String value) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public void removeAttribute(String name) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Attr getAttributeNode(String name) { + return (Attr) getAttributes().getNamedItem(name); + } + + @Override + public Attr setAttributeNode(Attr newAttr) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Attr removeAttributeNode(Attr oldAttr) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public NodeList getElementsByTagName(String name) { + throw unsupportedOperation(); + } + + @Override + public String getAttributeNS(String namespaceURI, String localName) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public void setAttributeNS(String namespaceURI, String qualifiedName, String value) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public void removeAttributeNS(String namespaceURI, String localName) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Attr getAttributeNodeNS(String namespaceURI, String localName) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Attr setAttributeNodeNS(Attr newAttr) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public NodeList getElementsByTagNameNS(String namespaceURI, String localName) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public boolean hasAttribute(String name) { + return getAttributes().getNamedItem(name) != null; + } + + @Override + public boolean hasAttributeNS(String namespaceURI, String localName) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public TypeInfo getSchemaTypeInfo() { + throw unsupportedOperation(); + } + + @Override + public void setIdAttribute(String name, boolean isId) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public void setIdAttributeNS(String namespaceURI, String localName, boolean isId) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException { + throw unsupportedOperation(); + } + + } + +} \ No newline at end of file diff --git a/bundles/org.eclipse.e4.emf.xpath/widgets.xml b/bundles/org.eclipse.e4.emf.xpath/widgets.xml new file mode 100644 index 00000000000..aa762be0e4c --- /dev/null +++ b/bundles/org.eclipse.e4.emf.xpath/widgets.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesApplicationTest.java b/tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesApplicationTest.java index 0fb975b0713..ed77547f4ec 100644 --- a/tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesApplicationTest.java +++ b/tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesApplicationTest.java @@ -13,10 +13,13 @@ ******************************************************************************/ package org.eclipse.e4.emf.xpath.test; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import org.eclipse.e4.emf.xpath.EcoreXPathContextFactory; +import org.eclipse.e4.emf.xpath.JavaXPathFactory; import org.eclipse.e4.emf.xpath.XPathContext; import org.eclipse.e4.emf.xpath.XPathContextFactory; import org.eclipse.e4.ui.internal.workbench.E4XMIResourceFactory; @@ -41,6 +44,7 @@ public class ExampleQueriesApplicationTest { private ResourceSet resourceSet; private XPathContext xpathContext; + private XPathContext xpathContext2; private Resource resource; @SuppressWarnings("restriction") @@ -63,7 +67,10 @@ public void setUp() { URI uri = URI.createPlatformPluginURI("/org.eclipse.e4.emf.xpath.test/model/Application.e4xmi", true); resource = resourceSet.getResource(uri, true); XPathContextFactory f = EcoreXPathContextFactory.newInstance(); - xpathContext = f.newContext(resource.getContents().get(0)); + EObject root = resource.getContents().get(0); + xpathContext = f.newContext(root); + XPathContextFactory jf = JavaXPathFactory.newInstance(); + xpathContext2 = jf.newContext(root); } @After @@ -76,8 +83,10 @@ public void tearDown() { @Test public void testAccessingTheApplication() { Object application = xpathContext.getValue("/"); - assertNotNull(application); - assertTrue(application instanceof MApplication); + assertThat(application, instanceOf(MApplication.class)); + + Object application2 = xpathContext2.getValue("/"); + assertThat(application2, instanceOf(MApplication.class)); } @Test @@ -94,6 +103,9 @@ public void testAccessingTheMainMenu() { public void testAccessingAllMenus() { Object menuEntries = xpathContext.getValue("//mainMenu/children"); assertNotNull(menuEntries); + + Object menuEntries2 = xpathContext2.getValue("//mainMenu/children"); + assertNotNull(menuEntries2); } From 21d22240da525f8b8d98df2bd036316a28318b59 Mon Sep 17 00:00:00 2001 From: Hannes Wellmann Date: Thu, 14 Sep 2023 23:13:20 +0200 Subject: [PATCH 2/8] [FIXUP] Only support containmeints?! --- .../e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java index 78f92d3ea32..504bfc49753 100644 --- a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java +++ b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java @@ -293,7 +293,7 @@ public int getLength() { } private Stream availableReferences(EClass eClass) { - return eClass.getEAllReferences().stream().filter(eObject::eIsSet); + return eClass.getEAllReferences().stream().filter(r -> r.isContainment()).filter(eObject::eIsSet); } private Stream createReferenceNodes(EReference ref) { From 42a3ebd01d426538615b8351e30554febd949eef Mon Sep 17 00:00:00 2001 From: Patrick Ziegler Date: Wed, 18 Sep 2024 18:57:05 +0200 Subject: [PATCH 3/8] Simplify creation of XML document from Eclipse data model Instead of using a queue, one can simply use eAllContents() to get a tree iterator over all elements. One one is created for each node, which is then attached to its container. --- .../internal/java/JavaXPathFactoryImpl.java | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java index 504bfc49753..6037e421129 100644 --- a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java +++ b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java @@ -4,13 +4,11 @@ import java.io.File; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.util.ArrayDeque; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Queue; import java.util.Spliterators; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -109,23 +107,17 @@ public XPathContext newContext(XPathContext parentContext, T contextBean) { Map proxy2object = new HashMap<>(); Map object2proxy = new HashMap<>(); - Queue queue = new ArrayDeque<>(); - queue.add(contextObject); Element rootElement = createElement(contextObject, proxyDoc, proxyDoc); object2proxy.put(contextObject, rootElement); proxy2object.put(rootElement, contextObject); - while (!queue.isEmpty()) { - EObject parent = queue.remove(); - Element parentNode = object2proxy.get(parent); - List eContents = parent.eContents(); - for (EObject childObject : eContents) { - Element childElement = createElement(childObject, parentNode, proxyDoc); - proxy2object.put(childElement, childObject); - object2proxy.put(childObject, childElement); - queue.add(childObject); - } - } + contextObject.eAllContents().forEachRemaining(eObject -> { + EObject eParent = eObject.eContainer(); + Element eParentNode = object2proxy.get(eParent); + Element eObjectNode = object2proxy.computeIfAbsent(eObject, + (x) -> createElement(eObject, eParentNode, proxyDoc)); + proxy2object.put(eObjectNode, eObject); + }); DOMImplementation implementation = proxyDoc.getImplementation(); final DOMImplementationLS domImplementation = (DOMImplementationLS) implementation; LSSerializer serial = domImplementation.createLSSerializer(); From faeee2024739d538d9752fc95dda4334a129dcc4 Mon Sep 17 00:00:00 2001 From: Patrick Ziegler Date: Wed, 18 Sep 2024 22:15:23 +0200 Subject: [PATCH 4/8] Move EObjectNodePointer out of JavaXPathFactoryImpl --- .../internal/java/EObjectNodePointer.java | 434 ++++++++++++++++++ .../internal/java/JavaXPathFactoryImpl.java | 420 ----------------- 2 files changed, 434 insertions(+), 420 deletions(-) create mode 100644 bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/EObjectNodePointer.java diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/EObjectNodePointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/EObjectNodePointer.java new file mode 100644 index 00000000000..59cc58e5a37 --- /dev/null +++ b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/EObjectNodePointer.java @@ -0,0 +1,434 @@ +package org.eclipse.e4.emf.xpath.internal.java; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.w3c.dom.Attr; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.TypeInfo; +import org.w3c.dom.UserDataHandler; + +public class EObjectNodePointer implements Element { + + private final String name; + private final Document document; + private final EObject eObject; + private final EObjectNodePointer parent; +// private final Map obj2nodes; + + public EObjectNodePointer(EObject eObject) throws ParserConfigurationException { + this.name = eObject.eClass().getName(); + this.eObject = eObject; + this.parent = null; + // TODO: check if document is necessary + this.document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + } + + public EObjectNodePointer(String name, EObject eObject, +// Map obj2nodes, + EObjectNodePointer parent, Document document) { + this.name = name; + this.eObject = eObject; +// this.obj2nodes = obj2nodes; + this.parent = parent; + this.document = document; + } + + // TODO: hashCode/equals? + + @Override + public String getNodeName() { + return name; + } + + @Override + public String getTagName() { + return name; + } + + @Override + public String getNodeValue() throws DOMException { + throw unsupportedOperation(); + } + + @Override + public void setNodeValue(String nodeValue) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public short getNodeType() { + return ELEMENT_NODE; + } + + @Override + public Node getParentNode() { + return parent; + } + + @Override + public boolean hasChildNodes() { + EClass eClass = eObject.eClass(); + return availableAttributes(eClass.getEAllAttributes()).findAny().isPresent() + || availableReferences(eClass).findAny().isPresent(); + } + + @Override + public NodeList getChildNodes() { + EClass eClass = eObject.eClass(); + var attributeNodes = availableAttributes(eClass.getEAllAttributes()).map(this::createAttributeNode); + var referenceNodes = availableReferences(eClass).flatMap(this::createReferenceNodes); + List childNodes = Stream.concat(attributeNodes, referenceNodes).toList(); + return new NodeList() { + + @Override + public Node item(int index) { + return childNodes.get(index); + } + + @Override + public int getLength() { + return childNodes.size(); + } + }; + } + + private Stream availableReferences(EClass eClass) { + return eClass.getEAllReferences().stream().filter(r -> r.isContainment()).filter(eObject::eIsSet); + } + + private Stream createReferenceNodes(EReference ref) { + Object value = this.eObject.eGet(ref); + Stream stream = value instanceof List list ? list.stream() : Stream.of(value); + return stream.map(EObject.class::cast) + .map(e -> new EObjectNodePointer(ref.getName(), e, /* obj2nodes, */ this, document)); + } + + @Override + public Node getFirstChild() { + throw unsupportedOperation(); + } + + @Override + public Node getLastChild() { + throw unsupportedOperation(); + } + + @Override + public Node getPreviousSibling() { + throw unsupportedOperation(); + } + + @Override + public Node getNextSibling() { + throw unsupportedOperation(); + } + + @Override + public boolean hasAttributes() { + return availableAttributes(eObject.eClass().getEAllAttributes()).findAny().isPresent(); + } + + private Stream availableAttributes(List allAttributes) { + return allAttributes.stream().filter(eObject::eIsSet); + } + + private Node createAttributeNode(EAttribute attribute) { + Object value = eObject.eIsSet(attribute) ? eObject.eGet(attribute) : null; + Attr attributeNode = document.createAttribute(attribute.getName()); + String strValue = EcoreUtil.convertToString(attribute.getEAttributeType(), value); + attributeNode.setValue(strValue); + return attributeNode; + } + + @Override + public NamedNodeMap getAttributes() { + List allAttributes = eObject.eClass().getEAllAttributes(); + return new NamedNodeMap() { + + @Override + public Node setNamedItemNS(Node arg) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node setNamedItem(Node arg) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node removeNamedItemNS(String namespaceURI, String localName) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node removeNamedItem(String name) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node item(int index) { + var attribute = availableAttributes(allAttributes).skip(index).findFirst(); + return attribute.isPresent() ? createAttributeNode(attribute.get()) : null; + } + + @Override + public Node getNamedItemNS(String namespaceURI, String localName) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node getNamedItem(String name) { + var attribute = allAttributes.stream().filter(a -> a.getName().equals(name)).findFirst(); + return attribute.isPresent() ? createAttributeNode(attribute.get()) : null; + } + + @Override + public int getLength() { + return (int) availableAttributes(allAttributes).count(); + } + }; + } + + @Override + public String getAttribute(String name) { + return getAttributes().getNamedItem(name).getNodeValue(); + } + + @Override + public Document getOwnerDocument() { + return document; + } + + @Override + public boolean isSameNode(Node other) { + return other instanceof EObjectNodePointer node // + && node.name == name && node.eObject == eObject; + } + + @Override + public boolean isEqualNode(Node arg) { + return arg instanceof EObjectNodePointer node && Objects.equals(node.eObject, eObject); + } + + @Override + public String toString() { + return "[" + getTagName() + availableAttributes(eObject.eClass().getEAllAttributes()) + .map(a -> a.getName() + "=" + eObject.eGet(a)).collect(Collectors.joining(" ", " ", "]")); + } + + private static UnsupportedOperationException unsupportedOperation() { + return new UnsupportedOperationException(); + } + + @Override + public Node insertBefore(Node newChild, Node refChild) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node replaceChild(Node newChild, Node oldChild) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node removeChild(Node oldChild) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node appendChild(Node newChild) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Node cloneNode(boolean deep) { + throw unsupportedOperation(); + } + + @Override + public void normalize() { + throw unsupportedOperation(); + } + + @Override + public boolean isSupported(String feature, String version) { + throw unsupportedOperation(); + } + + @Override + public String getNamespaceURI() { + return null; + } + + @Override + public String getPrefix() { + throw unsupportedOperation(); + } + + @Override + public void setPrefix(String prefix) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public String getLocalName() { + return null; + } + + @Override + public String getBaseURI() { + throw unsupportedOperation(); + } + + @Override + public short compareDocumentPosition(Node other) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public String getTextContent() throws DOMException { + throw unsupportedOperation(); + } + + @Override + public void setTextContent(String textContent) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public String lookupPrefix(String namespaceURI) { + throw unsupportedOperation(); + } + + @Override + public boolean isDefaultNamespace(String namespaceURI) { + throw unsupportedOperation(); + } + + @Override + public String lookupNamespaceURI(String prefix) { + throw unsupportedOperation(); + } + + @Override + public Object getFeature(String feature, String version) { + throw unsupportedOperation(); + } + + @Override + public Object setUserData(String key, Object data, UserDataHandler handler) { + throw unsupportedOperation(); + } + + @Override + public Object getUserData(String key) { + throw unsupportedOperation(); + } + + @Override + public void setAttribute(String name, String value) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public void removeAttribute(String name) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Attr getAttributeNode(String name) { + return (Attr) getAttributes().getNamedItem(name); + } + + @Override + public Attr setAttributeNode(Attr newAttr) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Attr removeAttributeNode(Attr oldAttr) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public NodeList getElementsByTagName(String name) { + throw unsupportedOperation(); + } + + @Override + public String getAttributeNS(String namespaceURI, String localName) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public void setAttributeNS(String namespaceURI, String qualifiedName, String value) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public void removeAttributeNS(String namespaceURI, String localName) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Attr getAttributeNodeNS(String namespaceURI, String localName) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public Attr setAttributeNodeNS(Attr newAttr) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public NodeList getElementsByTagNameNS(String namespaceURI, String localName) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public boolean hasAttribute(String name) { + return getAttributes().getNamedItem(name) != null; + } + + @Override + public boolean hasAttributeNS(String namespaceURI, String localName) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public TypeInfo getSchemaTypeInfo() { + throw unsupportedOperation(); + } + + @Override + public void setIdAttribute(String name, boolean isId) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public void setIdAttributeNS(String namespaceURI, String localName, boolean isId) throws DOMException { + throw unsupportedOperation(); + } + + @Override + public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException { + throw unsupportedOperation(); + } + + } \ No newline at end of file diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java index 6037e421129..c04a69827bd 100644 --- a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java +++ b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java @@ -8,9 +8,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Spliterators; -import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -28,20 +26,12 @@ import org.eclipse.e4.emf.xpath.XPathContext; import org.eclipse.e4.emf.xpath.XPathContextFactory; import org.eclipse.emf.ecore.EAttribute; -import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.util.EcoreUtil; -import org.w3c.dom.Attr; -import org.w3c.dom.DOMException; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.w3c.dom.TypeInfo; -import org.w3c.dom.UserDataHandler; import org.w3c.dom.ls.DOMImplementationLS; import org.w3c.dom.ls.LSSerializer; @@ -199,414 +189,4 @@ private Element createElement(EObject childObject, Node parentElement, Document return child; } - private static class EObjectNodePointer implements Element { - - private final String name; - private final Document document; - private final EObject eObject; - private final EObjectNodePointer parent; -// private final Map obj2nodes; - - public EObjectNodePointer(EObject eObject) throws ParserConfigurationException { - this.name = eObject.eClass().getName(); - this.eObject = eObject; - this.parent = null; - // TODO: check if document is necessary - this.document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); - } - - public EObjectNodePointer(String name, EObject eObject, -// Map obj2nodes, - EObjectNodePointer parent, Document document) { - this.name = name; - this.eObject = eObject; -// this.obj2nodes = obj2nodes; - this.parent = parent; - this.document = document; - } - - // TODO: hashCode/equals? - - @Override - public String getNodeName() { - return name; - } - - @Override - public String getTagName() { - return name; - } - - @Override - public String getNodeValue() throws DOMException { - throw unsupportedOperation(); - } - - @Override - public void setNodeValue(String nodeValue) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public short getNodeType() { - return ELEMENT_NODE; - } - - @Override - public Node getParentNode() { - return parent; - } - - @Override - public boolean hasChildNodes() { - EClass eClass = eObject.eClass(); - return availableAttributes(eClass.getEAllAttributes()).findAny().isPresent() - || availableReferences(eClass).findAny().isPresent(); - } - - @Override - public NodeList getChildNodes() { - EClass eClass = eObject.eClass(); - var attributeNodes = availableAttributes(eClass.getEAllAttributes()).map(this::createAttributeNode); - var referenceNodes = availableReferences(eClass).flatMap(this::createReferenceNodes); - List childNodes = Stream.concat(attributeNodes, referenceNodes).toList(); - return new NodeList() { - - @Override - public Node item(int index) { - return childNodes.get(index); - } - - @Override - public int getLength() { - return childNodes.size(); - } - }; - } - - private Stream availableReferences(EClass eClass) { - return eClass.getEAllReferences().stream().filter(r -> r.isContainment()).filter(eObject::eIsSet); - } - - private Stream createReferenceNodes(EReference ref) { - Object value = this.eObject.eGet(ref); - Stream stream = value instanceof List list ? list.stream() : Stream.of(value); - return stream.map(EObject.class::cast) - .map(e -> new EObjectNodePointer(ref.getName(), e, /* obj2nodes, */ this, document)); - } - - @Override - public Node getFirstChild() { - throw unsupportedOperation(); - } - - @Override - public Node getLastChild() { - throw unsupportedOperation(); - } - - @Override - public Node getPreviousSibling() { - throw unsupportedOperation(); - } - - @Override - public Node getNextSibling() { - throw unsupportedOperation(); - } - - @Override - public boolean hasAttributes() { - return availableAttributes(eObject.eClass().getEAllAttributes()).findAny().isPresent(); - } - - private Stream availableAttributes(List allAttributes) { - return allAttributes.stream().filter(eObject::eIsSet); - } - - private Node createAttributeNode(EAttribute attribute) { - Object value = eObject.eIsSet(attribute) ? eObject.eGet(attribute) : null; - Attr attributeNode = document.createAttribute(attribute.getName()); - String strValue = EcoreUtil.convertToString(attribute.getEAttributeType(), value); - attributeNode.setValue(strValue); - return attributeNode; - } - - @Override - public NamedNodeMap getAttributes() { - List allAttributes = eObject.eClass().getEAllAttributes(); - return new NamedNodeMap() { - - @Override - public Node setNamedItemNS(Node arg) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public Node setNamedItem(Node arg) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public Node removeNamedItemNS(String namespaceURI, String localName) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public Node removeNamedItem(String name) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public Node item(int index) { - var attribute = availableAttributes(allAttributes).skip(index).findFirst(); - return attribute.isPresent() ? createAttributeNode(attribute.get()) : null; - } - - @Override - public Node getNamedItemNS(String namespaceURI, String localName) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public Node getNamedItem(String name) { - var attribute = allAttributes.stream().filter(a -> a.getName().equals(name)).findFirst(); - return attribute.isPresent() ? createAttributeNode(attribute.get()) : null; - } - - @Override - public int getLength() { - return (int) availableAttributes(allAttributes).count(); - } - }; - } - - @Override - public String getAttribute(String name) { - return getAttributes().getNamedItem(name).getNodeValue(); - } - - @Override - public Document getOwnerDocument() { - return document; - } - - @Override - public boolean isSameNode(Node other) { - return other instanceof EObjectNodePointer node // - && node.name == name && node.eObject == eObject; - } - - @Override - public boolean isEqualNode(Node arg) { - return arg instanceof EObjectNodePointer node && Objects.equals(node.eObject, eObject); - } - - @Override - public String toString() { - return "[" + getTagName() + availableAttributes(eObject.eClass().getEAllAttributes()) - .map(a -> a.getName() + "=" + eObject.eGet(a)).collect(Collectors.joining(" ", " ", "]")); - } - - private static UnsupportedOperationException unsupportedOperation() { - return new UnsupportedOperationException(); - } - - @Override - public Node insertBefore(Node newChild, Node refChild) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public Node replaceChild(Node newChild, Node oldChild) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public Node removeChild(Node oldChild) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public Node appendChild(Node newChild) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public Node cloneNode(boolean deep) { - throw unsupportedOperation(); - } - - @Override - public void normalize() { - throw unsupportedOperation(); - } - - @Override - public boolean isSupported(String feature, String version) { - throw unsupportedOperation(); - } - - @Override - public String getNamespaceURI() { - return null; - } - - @Override - public String getPrefix() { - throw unsupportedOperation(); - } - - @Override - public void setPrefix(String prefix) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public String getLocalName() { - return null; - } - - @Override - public String getBaseURI() { - throw unsupportedOperation(); - } - - @Override - public short compareDocumentPosition(Node other) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public String getTextContent() throws DOMException { - throw unsupportedOperation(); - } - - @Override - public void setTextContent(String textContent) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public String lookupPrefix(String namespaceURI) { - throw unsupportedOperation(); - } - - @Override - public boolean isDefaultNamespace(String namespaceURI) { - throw unsupportedOperation(); - } - - @Override - public String lookupNamespaceURI(String prefix) { - throw unsupportedOperation(); - } - - @Override - public Object getFeature(String feature, String version) { - throw unsupportedOperation(); - } - - @Override - public Object setUserData(String key, Object data, UserDataHandler handler) { - throw unsupportedOperation(); - } - - @Override - public Object getUserData(String key) { - throw unsupportedOperation(); - } - - @Override - public void setAttribute(String name, String value) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public void removeAttribute(String name) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public Attr getAttributeNode(String name) { - return (Attr) getAttributes().getNamedItem(name); - } - - @Override - public Attr setAttributeNode(Attr newAttr) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public Attr removeAttributeNode(Attr oldAttr) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public NodeList getElementsByTagName(String name) { - throw unsupportedOperation(); - } - - @Override - public String getAttributeNS(String namespaceURI, String localName) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public void setAttributeNS(String namespaceURI, String qualifiedName, String value) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public void removeAttributeNS(String namespaceURI, String localName) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public Attr getAttributeNodeNS(String namespaceURI, String localName) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public Attr setAttributeNodeNS(Attr newAttr) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public NodeList getElementsByTagNameNS(String namespaceURI, String localName) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public boolean hasAttribute(String name) { - return getAttributes().getNamedItem(name) != null; - } - - @Override - public boolean hasAttributeNS(String namespaceURI, String localName) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public TypeInfo getSchemaTypeInfo() { - throw unsupportedOperation(); - } - - @Override - public void setIdAttribute(String name, boolean isId) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public void setIdAttributeNS(String namespaceURI, String localName, boolean isId) throws DOMException { - throw unsupportedOperation(); - } - - @Override - public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException { - throw unsupportedOperation(); - } - - } - } \ No newline at end of file From 1c24dcea8c903d4071394bdc859229fe4b46cfb3 Mon Sep 17 00:00:00 2001 From: Patrick Ziegler Date: Wed, 18 Sep 2024 22:25:41 +0200 Subject: [PATCH 5/8] Avoid code duplication by using parameterized tests --- .../test/ExampleQueriesApplicationTest.java | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesApplicationTest.java b/tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesApplicationTest.java index ed77547f4ec..4019d852170 100644 --- a/tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesApplicationTest.java +++ b/tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesApplicationTest.java @@ -18,6 +18,9 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import java.util.Collection; +import java.util.List; + import org.eclipse.e4.emf.xpath.EcoreXPathContextFactory; import org.eclipse.e4.emf.xpath.JavaXPathFactory; import org.eclipse.e4.emf.xpath.XPathContext; @@ -39,12 +42,24 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; +@RunWith(Parameterized.class) public class ExampleQueriesApplicationTest { + @Parameters + public static Collection data() { + return List.of(EcoreXPathContextFactory.newInstance(), JavaXPathFactory.newInstance()); + } + + @Parameter + public XPathContextFactory xpathContextFactory; + private ResourceSet resourceSet; private XPathContext xpathContext; - private XPathContext xpathContext2; private Resource resource; @SuppressWarnings("restriction") @@ -66,11 +81,8 @@ public void setUp() { URI uri = URI.createPlatformPluginURI("/org.eclipse.e4.emf.xpath.test/model/Application.e4xmi", true); resource = resourceSet.getResource(uri, true); - XPathContextFactory f = EcoreXPathContextFactory.newInstance(); EObject root = resource.getContents().get(0); - xpathContext = f.newContext(root); - XPathContextFactory jf = JavaXPathFactory.newInstance(); - xpathContext2 = jf.newContext(root); + xpathContext = xpathContextFactory.newContext(root); } @After @@ -84,9 +96,6 @@ public void tearDown() { public void testAccessingTheApplication() { Object application = xpathContext.getValue("/"); assertThat(application, instanceOf(MApplication.class)); - - Object application2 = xpathContext2.getValue("/"); - assertThat(application2, instanceOf(MApplication.class)); } @Test @@ -103,9 +112,6 @@ public void testAccessingTheMainMenu() { public void testAccessingAllMenus() { Object menuEntries = xpathContext.getValue("//mainMenu/children"); assertNotNull(menuEntries); - - Object menuEntries2 = xpathContext2.getValue("//mainMenu/children"); - assertNotNull(menuEntries2); } From f700e27d91932bb13f49808b113d36fb1e01de2f Mon Sep 17 00:00:00 2001 From: Patrick Ziegler Date: Fri, 11 Oct 2024 06:09:16 +0200 Subject: [PATCH 6/8] Move XPathContext to separate class --- .../META-INF/MANIFEST.MF | 1 + .../xpath/internal/java/EObjectContext.java | 161 ++++++++++++++++++ .../internal/java/EObjectNodePointer.java | 1 + .../internal/java/JavaXPathFactoryImpl.java | 110 ++---------- 4 files changed, 173 insertions(+), 100 deletions(-) create mode 100644 bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/EObjectContext.java diff --git a/bundles/org.eclipse.e4.emf.xpath/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.emf.xpath/META-INF/MANIFEST.MF index 74cc46b7049..1fd6ad3841b 100644 --- a/bundles/org.eclipse.e4.emf.xpath/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.e4.emf.xpath/META-INF/MANIFEST.MF @@ -5,6 +5,7 @@ Bundle-SymbolicName: org.eclipse.e4.emf.xpath Bundle-Version: 0.5.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-17 Require-Bundle: org.eclipse.emf.ecore;bundle-version="2.35.0", + org.eclipse.emf.ecore.xmi;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", diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/EObjectContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/EObjectContext.java new file mode 100644 index 00000000000..2a6f3acef45 --- /dev/null +++ b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/EObjectContext.java @@ -0,0 +1,161 @@ +package org.eclipse.e4.emf.xpath.internal.java; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Spliterators; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathEvaluationResult; +import javax.xml.xpath.XPathEvaluationResult.XPathResultType; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathFactory; +import javax.xml.xpath.XPathNodes; + +import org.eclipse.e4.emf.xpath.XPathContext; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.ecore.xmi.XMLHelper; +import org.eclipse.emf.ecore.xmi.XMLResource; +import org.eclipse.emf.ecore.xmi.impl.XMLHelperImpl; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSSerializer; + +public class EObjectContext implements XPathContext { + private final String PROXY_KEY = "EObjectContext.Proxy"; + private final Map proxy2node; + private final Document root; + private XMLHelper xml; + + public EObjectContext() throws ParserConfigurationException { + proxy2node = new HashMap<>(); + root = DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder().newDocument(); + } + + @Override + public Object getValue(String xpath) { + Iterator iterate = iterate(xpath); + return iterate.hasNext() ? iterate.next() : null; + } + + @Override + public T getValue(String xpath, Class requiredType) { + return requiredType.cast(getValue(xpath)); + } + + @Override + @SuppressWarnings("unchecked") + public Iterator iterate(String xpath) { + return (Iterator) stream(xpath, Object.class).iterator(); + } + + @Override + public Stream stream(String path, Class type) { + try { + XPathFactory factory = XPathFactory.newInstance(); +// factory.setXPathFunctionResolver((functionName, arity) -> null); +// factory.setXPathVariableResolver(e -> null); + XPath xpath = factory.newXPath(); + /* + EObjectNodePointer item = new EObjectNodePointer(contextObject); + XPathEvaluationResult evaluateExpression = xpath.evaluateExpression(path, item); + XPathNodes value1 = (XPathNodes) evaluateExpression.value(); + if (value1.size() > 0) { + Node node = value1.get(0); + System.out.println(node); + }*/ + +// path = "/Application/TrimmedWindow"; + XPathEvaluationResult result1 = xpath.evaluateExpression(path, root); + XPathNodes nodes2 = (XPathNodes) result1.value(); + if (nodes2.size() > 0) { + Node node = nodes2.get(0); + + System.out.println(node); + } + + prettyPrint(); + + XPathExpression expr = xpath.compile(path); + XPathEvaluationResult result = expr.evaluateExpression(root, XPathEvaluationResult.class); + XPathResultType type2 = result.type(); + Object value = result.value(); + + NodeList nodes1 = (NodeList) expr.evaluate(root, XPathConstants.NODESET); + XPathNodes nodes = expr.evaluateExpression(root, XPathNodes.class); + + System.out.println(nodes1.getLength()); + System.out.println(nodes.size()); + + Stream stream = StreamSupport.stream(Spliterators.spliterator(nodes.iterator(), nodes.size(), 0), + false); + return stream.map(this::getProxy).map(type::cast); + } catch (Exception e1) { + throw new IllegalStateException(e1); + } + } + + private void prettyPrint() { + final DOMImplementation implementation = root.getImplementation(); + final DOMImplementationLS domImplementation = (DOMImplementationLS) implementation; + final LSSerializer serial = domImplementation.createLSSerializer(); + serial.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE); + + final String writeToString = serial.writeToString(root); + System.out.println(writeToString); + } + + protected Element getNode(EObject proxy) { + return (Element) proxy2node.get(proxy); + } + + protected EObject getProxy(Node element) { + return (EObject) element.getUserData(PROXY_KEY); + } + + protected Element addRoot(EObject eObject) { + // TODO What about other types of resources? + xml = new XMLHelperImpl((XMLResource) eObject.eResource()); + Element object = createElement(eObject); + root.appendChild(object); + return object; + } + + protected Element add(EObject eChildObject, EObject eParentObject) { + Element childObject = createElement(eChildObject); + Node parentObject = proxy2node.get(eParentObject); + parentObject.appendChild(childObject); + return childObject; + } + + private Element createElement(EObject eObject) { + String qName = "application"; + if (eObject.eContainingFeature() != null) { + qName = xml.getQName(eObject.eContainingFeature()); + } + Element object = root.createElement(qName); + object.setUserData(PROXY_KEY, eObject, null); + proxy2node.put(eObject, object); + List eAttributes = eObject.eClass().getEAllAttributes(); + for (EAttribute eAttribute : eAttributes) { + if (eObject.eIsSet(eAttribute)) { + Object value = eObject.eGet(eAttribute); + String stringValue = EcoreUtil.convertToString(eAttribute.getEAttributeType(), value); + object.setAttribute(eAttribute.getName(), stringValue); + } + } + return object; + } +} diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/EObjectNodePointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/EObjectNodePointer.java index 59cc58e5a37..c5450daed1c 100644 --- a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/EObjectNodePointer.java +++ b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/EObjectNodePointer.java @@ -23,6 +23,7 @@ import org.w3c.dom.TypeInfo; import org.w3c.dom.UserDataHandler; +// TODO Is this still needed? public class EObjectNodePointer implements Element { private final String name; diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java index c04a69827bd..293b92915a1 100644 --- a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java +++ b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java @@ -4,36 +4,20 @@ import java.io.File; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Spliterators; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathEvaluationResult; -import javax.xml.xpath.XPathEvaluationResult.XPathResultType; -import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathFactory; -import javax.xml.xpath.XPathNodes; import org.eclipse.e4.emf.xpath.XPathContext; import org.eclipse.e4.emf.xpath.XPathContextFactory; -import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.util.EcoreUtil; -import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; -import org.w3c.dom.ls.DOMImplementationLS; -import org.w3c.dom.ls.LSSerializer; public class JavaXPathFactoryImpl extends XPathContextFactory { @@ -81,39 +65,30 @@ public XPathContext newContext(XPathContext parentContext, T contextBean) { } // TODO: consider parent-context (may be null) - XPathFactory factory = XPathFactory.newInstance(); -// factory.setXPathFunctionResolver((functionName, arity) -> null); -// factory.setXPathVariableResolver(e -> null); - XPath xpath = factory.newXPath(); + EObjectContext context; - DocumentBuilder documentBuilder; try { - documentBuilder = DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder(); + context = new EObjectContext(); } catch (ParserConfigurationException e) { throw new IllegalStateException(e); } - Document proxyDoc = documentBuilder.newDocument(); - - Map proxy2object = new HashMap<>(); - Map object2proxy = new HashMap<>(); - - Element rootElement = createElement(contextObject, proxyDoc, proxyDoc); - object2proxy.put(contextObject, rootElement); - proxy2object.put(rootElement, contextObject); + context.addRoot(contextObject); + contextObject.eContents(); contextObject.eAllContents().forEachRemaining(eObject -> { EObject eParent = eObject.eContainer(); - Element eParentNode = object2proxy.get(eParent); - Element eObjectNode = object2proxy.computeIfAbsent(eObject, - (x) -> createElement(eObject, eParentNode, proxyDoc)); - proxy2object.put(eObjectNode, eObject); + if (context.getNode(eObject) == null) { + context.add(eObject, eParent); + } }); + /* DOMImplementation implementation = proxyDoc.getImplementation(); final DOMImplementationLS domImplementation = (DOMImplementationLS) implementation; LSSerializer serial = domImplementation.createLSSerializer(); serial.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE); String writeToString = serial.writeToString(proxyDoc); System.out.println(writeToString); + */ // try { // proxyDoc = documentBuilder.parse(new ByteArrayInputStream(writeToString.getBytes(StandardCharsets.UTF_16))); // @@ -121,72 +96,7 @@ public XPathContext newContext(XPathContext parentContext, T contextBean) { // throw new IllegalStateException(e); // } - return new XPathContext() { - @Override - public Object getValue(String xpath) { - Iterator iterate = iterate(xpath); - return iterate.hasNext() ? iterate.next() : null; - } - - @Override - public Object getValue(String xpath, Class requiredType) { - return requiredType.cast(getValue(xpath)); - } - - @Override - @SuppressWarnings("unchecked") - public Iterator iterate(String xpath) { - return (Iterator) stream(xpath, Object.class).iterator(); - } - - public Stream stream(String path, Class type) { - try { - EObjectNodePointer item = new EObjectNodePointer(contextObject); - XPathEvaluationResult evaluateExpression = xpath.evaluateExpression(path, item); - XPathNodes value1 = (XPathNodes) evaluateExpression.value(); - if (value1.size() > 0) { - Node node = value1.get(0); - System.out.println(node); - } - - path = "/Application/TrimmedWindow"; - XPathEvaluationResult result1 = xpath.evaluateExpression(path, rootElement); - XPathNodes nodes2 = (XPathNodes) result1.value(); - if (nodes2.size() > 0) { - Node node = nodes2.get(0); - - System.out.println(node); - } - XPathExpression expr = xpath.compile(path); - XPathEvaluationResult result = expr.evaluateExpression(rootElement, XPathEvaluationResult.class); - XPathResultType type2 = result.type(); - Object value = result.value(); - - XPathNodes nodes = expr.evaluateExpression(rootElement, XPathNodes.class); - Stream stream = StreamSupport - .stream(Spliterators.spliterator(nodes.iterator(), nodes.size(), 0), false); - return stream.map(proxy2object::get).map(type::cast); - } catch (Exception e1) { - throw new IllegalStateException(e1); - } - } - - }; - } - - private Element createElement(EObject childObject, Node parentElement, Document doc) { - Document document = doc; // parentElement.getOwnerDocument(); - Element child = document.createElement(childObject.eClass().getName()); - List allAttributes = childObject.eClass().getEAllAttributes(); - for (EAttribute attribute : allAttributes) { - if (childObject.eIsSet(attribute)) { - Object value = childObject.eGet(attribute); - String stringValue = EcoreUtil.convertToString(attribute.getEAttributeType(), value); - child.setAttribute(attribute.getName(), stringValue); - } - } - parentElement.appendChild(child); - return child; + return context; } } \ No newline at end of file From a9f3906a54567285d3eb0db0b65e37e25665dd5b Mon Sep 17 00:00:00 2001 From: Patrick Ziegler Date: Fri, 11 Oct 2024 06:16:47 +0200 Subject: [PATCH 7/8] Initial support for parent context-handling --- .../xpath/internal/java/EObjectContext.java | 22 ++++++++++++++++--- .../internal/java/JavaXPathFactoryImpl.java | 3 ++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/EObjectContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/EObjectContext.java index 2a6f3acef45..40fd3754956 100644 --- a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/EObjectContext.java +++ b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/EObjectContext.java @@ -36,12 +36,22 @@ public class EObjectContext implements XPathContext { private final String PROXY_KEY = "EObjectContext.Proxy"; private final Map proxy2node; + private final EObjectContext parentContext; private final Document root; private XMLHelper xml; public EObjectContext() throws ParserConfigurationException { - proxy2node = new HashMap<>(); - root = DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder().newDocument(); + this(null); + } + + public EObjectContext(EObjectContext parentContext) throws ParserConfigurationException { + this.proxy2node = new HashMap<>(); + this.parentContext = parentContext; + if (parentContext == null) { + this.root = DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder().newDocument(); + } else { + this.root = parentContext.root; + } } @Override @@ -129,7 +139,12 @@ protected Element addRoot(EObject eObject) { // TODO What about other types of resources? xml = new XMLHelperImpl((XMLResource) eObject.eResource()); Element object = createElement(eObject); - root.appendChild(object); + if (parentContext == null) { + root.appendChild(object); + } else { + Element parentObject = getNode(eObject.eContainer()); + parentObject.appendChild(object); + } return object; } @@ -141,6 +156,7 @@ protected Element add(EObject eChildObject, EObject eParentObject) { } private Element createElement(EObject eObject) { + // TODO: Better way to get the "root" QName String qName = "application"; if (eObject.eContainingFeature() != null) { qName = xml.getQName(eObject.eContainingFeature()); diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java index 293b92915a1..95d44b5e51e 100644 --- a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java +++ b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/xpath/internal/java/JavaXPathFactoryImpl.java @@ -68,7 +68,8 @@ public XPathContext newContext(XPathContext parentContext, T contextBean) { EObjectContext context; try { - context = new EObjectContext(); + // TODO: What about the compatibility with other contexts' + context = new EObjectContext((EObjectContext) parentContext); } catch (ParserConfigurationException e) { throw new IllegalStateException(e); } From dc9d2d451f2c6b38d0fc5da9891dcae584ca162f Mon Sep 17 00:00:00 2001 From: Patrick Ziegler Date: Fri, 11 Oct 2024 06:22:08 +0200 Subject: [PATCH 8/8] Synchronize tests --- .../e4/emf/xpath/test/ExampleQueriesApplicationTest.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesApplicationTest.java b/tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesApplicationTest.java index 4019d852170..43959e48634 100644 --- a/tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesApplicationTest.java +++ b/tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesApplicationTest.java @@ -13,8 +13,6 @@ ******************************************************************************/ package org.eclipse.e4.emf.xpath.test; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -81,8 +79,7 @@ public void setUp() { URI uri = URI.createPlatformPluginURI("/org.eclipse.e4.emf.xpath.test/model/Application.e4xmi", true); resource = resourceSet.getResource(uri, true); - EObject root = resource.getContents().get(0); - xpathContext = xpathContextFactory.newContext(root); + xpathContext = xpathContextFactory.newContext(resource.getContents().get(0)); } @After @@ -95,7 +92,8 @@ public void tearDown() { @Test public void testAccessingTheApplication() { Object application = xpathContext.getValue("/"); - assertThat(application, instanceOf(MApplication.class)); + assertNotNull(application); + assertTrue(application instanceof MApplication); } @Test