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

devonfw / IDEasy / 13990652864

21 Mar 2025 11:23AM UTC coverage: 67.671% (-0.02%) from 67.688%
13990652864

Pull #1138

github

web-flow
Merge 3478dc537 into 64fd95283
Pull Request #1138: #1130: improve behaviour on ambigous xpath match

3038 of 4918 branches covered (61.77%)

Branch coverage included in aggregate %.

7832 of 11145 relevant lines covered (70.27%)

3.07 hits per line

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

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

3
import javax.xml.xpath.XPath;
4
import javax.xml.xpath.XPathConstants;
5
import javax.xml.xpath.XPathExpression;
6
import javax.xml.xpath.XPathExpressionException;
7
import javax.xml.xpath.XPathFactory;
8

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

12
import com.devonfw.tools.ide.context.IdeContext;
13
import com.devonfw.tools.ide.merge.xmlmerger.XmlMergeSupport;
14

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

20
  /** The value of merge:id that is used to evaluate the xpath expression. */
21
  private final String id;
22

23
  private final IdeContext context;
24

25
  private static final XPathFactory xPathFactory = XPathFactory.newInstance();
3✔
26

27
  private final boolean throwExceptionOnMultipleMatches = Boolean.parseBoolean(System.getProperty("throwExceptionOnMultipleMatches", "false"));
6✔
28

29

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

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

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

48
    return this.id;
×
49
  }
50

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

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

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

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

122
}
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

© 2025 Coveralls, Inc