• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

devonfw / IDEasy / 16436171980

22 Jul 2025 06:08AM UTC coverage: 68.479% (+0.02%) from 68.464%
16436171980

Pull #1411

github

web-flow
Merge 0194d4742 into f845f1747
Pull Request #1411: Fix XML merge warning message to include file paths for better debugging

3287 of 5202 branches covered (63.19%)

Branch coverage included in aggregate %.

8412 of 11882 relevant lines covered (70.8%)

3.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.29
cli/src/main/java/com/devonfw/tools/ide/merge/xml/matcher/IdComputer.java
1
package com.devonfw.tools.ide.merge.xml.matcher;
2

3
import java.nio.file.Path;
4
import javax.xml.xpath.XPath;
5
import javax.xml.xpath.XPathConstants;
6
import javax.xml.xpath.XPathExpression;
7
import javax.xml.xpath.XPathExpressionException;
8
import javax.xml.xpath.XPathFactory;
9

10
import org.w3c.dom.Element;
11
import org.w3c.dom.NodeList;
12

13
import com.devonfw.tools.ide.context.IdeContext;
14
import com.devonfw.tools.ide.merge.xml.XmlMergeSupport;
15

16
/**
17
 * The IdComputer class is responsible for building XPath expressions and evaluating those expressions to match elements in a target document.
18
 */
19
public class IdComputer {
20

21
  /** Name of the {@link com.devonfw.tools.ide.environment.EnvironmentVariables variable} to fail on ambiguous merge. */
22
  public static final String FAIL_ON_AMBIGOUS_MERGE = "FAIL_ON_AMBIGOUS_MERGE";
23

24
  /** The value of merge:id that is used to evaluate the xpath expression. */
25
  private final String id;
26

27
  private final IdeContext context;
28

29
  private static final XPathFactory xPathFactory = XPathFactory.newInstance();
3✔
30

31
  /**
32
   * The constructor.
33
   *
34
   * @param id the {@link #getId() merge ID}.
35
   */
36
  public IdComputer(String id, IdeContext context) {
37

38
    super();
2✔
39
    this.id = id;
3✔
40
    this.context = context;
3✔
41
  }
1✔
42

43
  /**
44
   * @return the value of "merge:id" attribute what is an {@link XPath} expression.
45
   * @see XmlMergeSupport#getMergeId(Element)
46
   */
47
  public String getId() {
48

49
    return this.id;
×
50
  }
51

52
  /**
53
   * Evaluates the XPath expression for the given merge element in the target element.
54
   *
55
   * @param templateElement the template {@link Element} for which to build the {@link XPath} expression.
56
   * @param workspaceElement the workspace {@link Element} in which to evaluate the {@link XPath} expression.
57
   * @param templatePath the {@link Path} to the template XML file.
58
   * @param workspacePath the {@link Path} to the workspace XML file.
59
   * @return the matched Element if found, or {@code null} if not found
60
   */
61
  public Element evaluateExpression(Element templateElement, Element workspaceElement, Path templatePath, Path workspacePath) {
62
    XPath xpath = xPathFactory.newXPath();
3✔
63
    xpath.setNamespaceContext(new NamespaceContextFromElement(templateElement));
6✔
64
    String xpathExpr = buildXPathExpression(templateElement);
4✔
65
    try {
66
      XPathExpression xpathExpression = xpath.compile(xpathExpr);
4✔
67
      NodeList nodeList = (NodeList) xpathExpression.evaluate(workspaceElement, XPathConstants.NODESET);
6✔
68
      int length = nodeList.getLength();
3✔
69
      if (length == 1) {
3✔
70
        return (Element) nodeList.item(0);
5✔
71
      } else if (length == 0) {
2✔
72
        return null;
2✔
73
      } else {
74
        String message = length + " matches found for XPath " + xpathExpr + " in workspace XML file `" + workspacePath + "` at " + XmlMergeSupport.getXPath(workspaceElement, true) + " for template file `" + templatePath + "`";
11✔
75
        if ("true".equals(this.context.getVariables().get(FAIL_ON_AMBIGOUS_MERGE))) {
8✔
76
          throw new IllegalStateException(message);
5✔
77
        } else {
78
          this.context.warning(message);
4✔
79
        }
80
        return (Element) nodeList.item(0);
5✔
81
      }
82
    } catch (XPathExpressionException e) {
×
83
      throw new IllegalStateException("Failed to compile XPath expression " + xpath, e);
×
84
    }
85
  }
86

87
  /**
88
   * Builds the XPath expression for the given merge element based on the {@link #getId()} merge:id} value.
89
   *
90
   * @param element the {@link Element} for which to build the XPath expression
91
   * @return the XPath expression as a {@link String}.
92
   */
93
  private String buildXPathExpression(Element element) {
94

95
    String namespaceURI = element.getNamespaceURI();
3✔
96
    String localName = element.getLocalName();
3✔
97
    if (localName == null) {
2!
98
      localName = element.getTagName();
×
99
    }
100
    String prefix = element.getPrefix();
3✔
101

102
    StringBuilder xpathBuilder = new StringBuilder(localName.length());
6✔
103
    if ((prefix != null) && !prefix.isEmpty()) {
5!
104
      xpathBuilder.append(prefix).append(":");
6✔
105
    }
106
    xpathBuilder.append(localName);
4✔
107
    if (this.id.startsWith("@")) {
5✔
108
      String attributeName = this.id.substring(1);
5✔
109
      String attributeValue = element.getAttribute(attributeName);
4✔
110
      xpathBuilder.append('[').append(this.id).append("='").append(XmlMergeSupport.escapeSingleQuotes(attributeValue)).append("']");
14✔
111
    } else if (this.id.equals(XmlMergeSupport.XPATH_ELEMENT_NAME)) {
6✔
112
      xpathBuilder.append("[local-name()='").append(localName).append("']");
8✔
113
      if ((namespaceURI != null) && !namespaceURI.isEmpty()) {
2!
114
        xpathBuilder.append(" and namespace-uri()='").append(namespaceURI).append('\'');
×
115
      }
116
    } else if (this.id.equals(XmlMergeSupport.XPATH_ELEMENT_TEXT)) {
5✔
117
      String textContent = element.getTextContent();
3✔
118
      xpathBuilder.append("[text()='").append(XmlMergeSupport.escapeSingleQuotes(textContent)).append("']");
9✔
119
    } else { // custom xpath like ../element[@attr='value']
1✔
120
      return this.id;
3✔
121
    }
122
    return xpathBuilder.toString();
3✔
123
  }
124

125
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc