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

devonfw / IDEasy / 9905881806

12 Jul 2024 09:46AM UTC coverage: 61.162% (-0.2%) from 61.387%
9905881806

push

github

web-flow
#403: Improve XML merger (#456)

1997 of 3595 branches covered (55.55%)

Branch coverage included in aggregate %.

5296 of 8329 relevant lines covered (63.59%)

2.8 hits per line

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

76.81
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 com.devonfw.tools.ide.merge.xmlmerger.model.MergeElement;
4
import org.w3c.dom.Element;
5
import org.w3c.dom.NodeList;
6

7
import javax.xml.XMLConstants;
8
import javax.xml.namespace.NamespaceContext;
9
import javax.xml.xpath.XPath;
10
import javax.xml.xpath.XPathConstants;
11
import javax.xml.xpath.XPathExpression;
12
import javax.xml.xpath.XPathExpressionException;
13
import javax.xml.xpath.XPathFactory;
14
import java.util.Collections;
15
import java.util.Iterator;
16

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

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

27
  private static final XPathFactory xPathFactory = XPathFactory.newInstance();
3✔
28

29
  public IdComputer(String id) {
2✔
30

31
    this.id = id;
3✔
32
  }
1✔
33

34
  /**
35
   * @return the value of id.
36
   */
37
  public String getId() {
38

39
    return this.id;
3✔
40
  }
41

42
  /**
43
   * Evaluates the XPath expression for the given merge element in the target element.
44
   *
45
   * @param sourceElement the merge element for which to build the XPath expression
46
   * @param targetElement the target element in which to evaluate the XPath expression
47
   * @return the matched Element if found, or null if not found
48
   */
49

50
  public Element evaluateExpression(MergeElement sourceElement, MergeElement targetElement) {
51
    try {
52
      XPath xpath = xPathFactory.newXPath();
3✔
53
      final String elementPrefix = sourceElement.getElement().getPrefix();
4✔
54
      final String elementNamespaceURI = sourceElement.getElement().getNamespaceURI();
4✔
55

56
      if (elementPrefix != null && !elementPrefix.isEmpty()) {
5!
57
        xpath.setNamespaceContext(new NamespaceContext() { // simple impl of NameSpaceContext that should suffice for our usecases
20✔
58
          @Override
59
          public String getNamespaceURI(String prefix) {
60
            return prefix.equals(elementPrefix) ? elementNamespaceURI : XMLConstants.NULL_NS_URI;
9!
61
          }
62

63
          @Override
64
          public String getPrefix(String namespaceURI) {
65
            return namespaceURI.equals(elementNamespaceURI) ? elementPrefix : null;
×
66
          }
67

68
          @Override
69
          public Iterator<String> getPrefixes(String namespaceURI) {
70
            return Collections.singletonList(getPrefix(namespaceURI)).iterator();
×
71
          }
72
        });
73
      }
74

75
      String xpathExpr = buildXPathExpression(sourceElement);
4✔
76
      XPathExpression xpathExpression = xpath.compile(xpathExpr);
4✔
77
      NodeList nodeList = (NodeList) xpathExpression.evaluate(targetElement.getElement(), XPathConstants.NODESET);
7✔
78
      int length = nodeList.getLength();
3✔
79
      if (length > 1) {
3!
80
        throw new IllegalStateException(
×
81
            length + " matches found when trying to match element " + sourceElement.getXPath() + " in target document " + targetElement.getDocumentPath());
×
82
      } else {
83
        return (Element) nodeList.item(0);
5✔
84
      }
85
    } catch (XPathExpressionException e) {
×
86
      throw new IllegalStateException("Failed to match " + sourceElement.getXPath(), e);
×
87
    }
88
  }
89

90
  /**
91
   * Builds the XPath expression for the given merge element based on the ID value.
92
   *
93
   * @param mergeElement the merge element for which to build the XPath expression
94
   * @return the XPath expression as a String
95
   */
96
  private String buildXPathExpression(MergeElement mergeElement) {
97

98
    Element element = mergeElement.getElement();
3✔
99
    String namespaceURI = element.getNamespaceURI();
3✔
100
    String localName = element.getLocalName();
3✔
101
    String prefix = element.getPrefix();
3✔
102

103
    StringBuilder xpathBuilder = new StringBuilder();
4✔
104
    if (prefix != null && !prefix.isEmpty()) {
5!
105
      xpathBuilder.append(prefix).append(":");
6✔
106
    }
107
    xpathBuilder.append(localName);
4✔
108

109
    if (id.startsWith("@")) {
5✔
110
      String attributeName = id.substring(1);
5✔
111
      String attributeValue = element.getAttribute(attributeName);
4✔
112
      xpathBuilder.append(String.format("[@%s='%s']", attributeName, attributeValue));
15✔
113
    } else if (id.equals("name()")) {
6✔
114
      xpathBuilder.append(String.format("[local-name()='%s']", localName));
11✔
115
      if (namespaceURI != null && !namespaceURI.isEmpty()) {
2!
116
        xpathBuilder.append(String.format(" and namespace-uri()='%s'", namespaceURI));
×
117
      }
118
    } else if (id.equals("text()")) {
5✔
119
      String textContent = element.getTextContent();
3✔
120
      xpathBuilder.append(String.format("[text()='%s']", textContent));
11✔
121
    } else { // custom xpath like ../element[@attr='value']
1✔
122
      return id;
3✔
123
    }
124

125
    return xpathBuilder.toString();
3✔
126
  }
127

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