Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
bcbd262
Update quality status documents
JoelAdbu Aug 10, 2026
e07c3a6
restored quality status documentation from devonfw
JoelAdbu Aug 10, 2026
a737325
Update quality status documents
JoelAdbu Aug 10, 2026
8152aac
restored quality status documentation from devonfw
JoelAdbu Aug 10, 2026
09fada9
Merge remote-tracking branch 'origin/main'
JoelAdbu Aug 13, 2026
502c99d
Update quality status documents
JoelAdbu Aug 17, 2026
4f729aa
Update quality status documents
JoelAdbu Aug 24, 2026
3ed67d8
Merge remote-tracking branch 'origin/main'
JoelAdbu Aug 25, 2026
5cfced2
Update quality status documents
JoelAdbu Aug 10, 2026
9095297
restored quality status documentation from devonfw
JoelAdbu Aug 10, 2026
b1835e5
Update quality status documents
JoelAdbu Aug 10, 2026
3be5def
restored quality status documentation from devonfw
JoelAdbu Aug 10, 2026
7046b2a
Merge remote-tracking branch 'origin/main'
JoelAdbu Aug 26, 2026
e17cad0
Update quality status documents
JoelAdbu Aug 31, 2026
fc671cf
Merge branch 'devonfw:main' into main
JoelAdbu Sep 1, 2026
4efaf21
Update quality status documents
JoelAdbu Aug 10, 2026
e54ffcc
restored quality status documentation from devonfw
JoelAdbu Aug 10, 2026
6c62693
Update quality status documents
JoelAdbu Aug 10, 2026
0954290
reset quality-status
JoelAdbu Sep 2, 2026
6969fed
restored quality-status
JoelAdbu Sep 2, 2026
f9266be
Merge remote-tracking branch 'origin/main'
JoelAdbu Sep 2, 2026
91a36d4
Update quality status documents
JoelAdbu Aug 10, 2026
076304e
restored quality status documentation from devonfw
JoelAdbu Aug 10, 2026
93070ba
Update quality status documents
JoelAdbu Aug 10, 2026
fdd7b76
restored quality status documentation from devonfw
JoelAdbu Aug 10, 2026
4d56b08
Update quality status documents
JoelAdbu Aug 10, 2026
adcc0c4
restored quality status documentation from devonfw
JoelAdbu Aug 10, 2026
34c9b7b
Update quality status documents
JoelAdbu Aug 10, 2026
223d0f3
restored quality status documentation from devonfw
JoelAdbu Aug 10, 2026
8db23ad
Update quality status documents
JoelAdbu Aug 17, 2026
8a38f54
Update quality status documents
JoelAdbu Aug 24, 2026
eaeb521
Update quality status documents
JoelAdbu Aug 31, 2026
78409bb
Merge remote-tracking branch 'origin/main'
JoelAdbu Sep 3, 2026
f9fabab
restore quality-status
JoelAdbu Sep 4, 2026
bff3ed8
Merge remote-tracking branch 'upstream/main'
JoelAdbu Sep 7, 2026
3b7fc68
#992:
JoelAdbu Sep 7, 2026
2d93166
#992:
JoelAdbu Sep 7, 2026
9b51f78
Merge branch 'main' into feature/992-support-multiple-attributes-for-…
JoelAdbu Sep 7, 2026
960b595
#992:
JoelAdbu Sep 7, 2026
6796030
Merge remote-tracking branch 'origin/feature/992-support-multiple-att…
JoelAdbu Sep 7, 2026
a7eaa72
#992:
JoelAdbu Sep 7, 2026
4d02479
Merge remote-tracking branch 'upstream/main' into feature/992-support…
JoelAdbu Sep 9, 2026
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
2 changes: 1 addition & 1 deletion CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This file documents all notable changes to https://github.com/devonfw/IDEasy[IDE
== 2026.09.002

Release with new features and bugfixes:

* https://github.com/devonfw/IDEasy/issues/992[#992]: Support multiple attributes for merge:id
* https://github.com/devonfw/IDEasy/issues/2190[#2190]: IDEasy destroys my python installation
* https://github.com/devonfw/IDEasy/issues/2178[#2178]: Make ReleaseCommandlet independent of specific build commandlet and fix `ide build` using npm instead of yarn
* https://github.com/devonfw/IDEasy/issues/2142[#2142]: Move IDE-specific metadata (.idea, .vscode) out of workspace
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.devonfw.tools.ide.merge.xml.matcher;

import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
Expand All @@ -10,6 +13,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;

import com.devonfw.tools.ide.context.IdeContext;
Expand Down Expand Up @@ -96,7 +100,7 @@ public Element evaluateExpression(Element templateElement, Element workspaceElem
* @param element the {@link Element} for which to build the XPath expression
* @return the XPath expression as a {@link String}.
*/
private String buildXPathExpression(Element element) {
protected String buildXPathExpression(Element element) {

String namespaceURI = element.getNamespaceURI();
String localName = element.getLocalName();
Expand All @@ -110,7 +114,31 @@ private String buildXPathExpression(Element element) {
xpathBuilder.append(prefix).append(":");
}
xpathBuilder.append(localName);
if (this.id.startsWith("@")) {
if (this.id.startsWith("@@")) {
xpathBuilder.append('[');
NamedNodeMap attributesMap = element.getAttributes();
boolean first = true;
for (int i = 0; i < attributesMap.getLength(); i++) {
String attribute = attributesMap.item(i).getNodeName();
String attributeValue = attributesMap.item(i).getNodeValue();
if (!attribute.equals("id")) {
appendAttributePredicate(xpathBuilder, attribute, attributeValue, !first);
first = false;
}
}
xpathBuilder.append(']');
} else if (this.id.startsWith("@") && this.id.contains(",")) {
List<String> attributeNames = Arrays.stream(this.id.split(","))
.map(attribute -> attribute.substring(1))
.toList();
xpathBuilder.append('[');
for (int i = 0; i < attributeNames.size(); i++) {
String attribute = attributeNames.get(i);
String attributeValue = element.getAttribute(attribute);
appendAttributePredicate(xpathBuilder, attribute, attributeValue, i > 0);
}
xpathBuilder.append(']');
} else if (this.id.startsWith("@")) {
String attributeName = this.id.substring(1);
String attributeValue = element.getAttribute(attributeName);
xpathBuilder.append('[').append(this.id).append("='").append(XmlMergeSupport.escapeSingleQuotes(attributeValue)).append("']");
Expand All @@ -128,4 +156,15 @@ private String buildXPathExpression(Element element) {
return xpathBuilder.toString();
}

private void appendAttributePredicate(StringBuilder xpathBuilder, String attributeName, String attributeValue, boolean appendAnd) {
if (appendAnd) {
xpathBuilder.append(" and ");
}
xpathBuilder.append('@')
.append(attributeName)
.append("='")
.append(XmlMergeSupport.escapeSingleQuotes(attributeValue))
.append("'");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.devonfw.tools.ide.merge.xml.matcher;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.params.provider.Arguments.arguments;

import java.io.ByteArrayInputStream;
import java.util.stream.Stream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
* Test of {@link IdComputer}.
*/
class IdComputerTest {

@ParameterizedTest
@MethodSource("buildXPathExpressionTestCases")
void testBuildXPathExpression(String elementXml, String mergeId, String expectedXPath) throws Exception {

// arrange
Element element = parse(elementXml);
IdComputer computer = new IdComputer(mergeId, null);
// act
String actual = computer.buildXPathExpression(element);
// assert
assertThat(actual).isEqualTo(expectedXPath);
}

private static Stream<Arguments> buildXPathExpressionTestCases() {

return Stream.of(
arguments("<component name=\"RunManager\" selected=\"Application.IDEasy\"/>", "@name", "component[@name='RunManager']"),
arguments("<configuration default=\"true\" type=\"JUnit\" name=\"foo\"/>", "@default,@type",
"configuration[@default='true' and @type='JUnit']"),
arguments("<configuration id=\"cfg1\" name=\"foo\" type=\"JUnit\"/>", "@@",
"configuration[@name='foo' and @type='JUnit']"),
arguments("<configuration default=\"true\" type=\"JUnit\" name=\"foo\"/>", "@@",
"configuration[@default='true' and @name='foo' and @type='JUnit']"),
arguments("<component name=\"RunManager\"/>", "name()", "component[local-name()='component']"),
arguments("<value>myValue</value>", "text()", "value[text()='myValue']"));
}

/**
* Parses the given XML fragment and returns its root {@link Element}.
*
* @param xml the XML fragment to parse.
* @return the root element of the parsed document
* @throws Exception in case of a parse error
*/
private static Element parse(String xml) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
return document.getDocumentElement();
}
}