• 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

84.29
cli/src/main/java/com/devonfw/tools/ide/merge/xmlmerger/XmlMergeStrategy.java
1
package com.devonfw.tools.ide.merge.xmlmerger;
2

3
import java.util.Locale;
4
import java.util.function.BiFunction;
5

6
import org.w3c.dom.Attr;
7
import org.w3c.dom.DOMException;
8
import org.w3c.dom.Element;
9
import org.w3c.dom.Node;
10
import org.w3c.dom.NodeList;
11

12
import com.devonfw.tools.ide.merge.xmlmerger.matcher.ElementMatcher;
13

14
/**
15
 * Enum of merge strategies for XML elements.
16
 */
17
public enum XmlMergeStrategy {
3✔
18

19
  /**
20
   * Combines source and target elements. Overrides text nodes and attributes. This process is recursively applied to child elements. If the source element
21
   * exists in the target document, they are combined, otherwise, the source element is appended.
22
   */
23
  COMBINE {
11✔
24
    @Override
25
    protected void doMerge(Element templateElement, Element resultElement, ElementMatcher matcher) {
26

27
      BiFunction<Attr, Attr, String> attributeMerger = null; // here we can allow more configuration flexibility e.g. via merge:attribute-override="id,name"
2✔
28
      XmlMergeSupport.combineAttributes(templateElement, resultElement, attributeMerger);
4✔
29
      combineChildNodes(templateElement, resultElement, matcher);
5✔
30
    }
1✔
31
  },
32

33
  /**
34
   * Replaces the target element with the source element, without considering child elements. If the element exists in the target, it is overridden, otherwise,
35
   * it is appended.
36
   */
37
  OVERRIDE {
11✔
38
    @Override
39
    protected void doMerge(Element templateElement, Element resultElement, ElementMatcher matcher) {
40

41
      Node importedNode = resultElement.getOwnerDocument().importNode(templateElement, true);
6✔
42
      resultElement.getParentNode().replaceChild(importedNode, resultElement);
6✔
43
    }
1✔
44
  },
45

46
  /**
47
   * Keeps the existing target element intact if the source element exists in the target document, otherwise, it is appended.
48
   */
49
  KEEP {
11✔
50
    @Override
51
    protected void doMerge(Element templateElement, Element resultElement, ElementMatcher matcher) {
52

53
      // Do nothing, keep the existing element
54
    }
1✔
55
  };
56

57
  /**
58
   * @param templateElement the {@link Element} of the template XML file to merge.
59
   * @param resultElement the {@link Element} populated with the workspace XML file to merge into.
60
   * @param matcher the {@link ElementMatcher}.
61
   */
62
  public void merge(Element templateElement, Element resultElement, ElementMatcher matcher) {
63
    try {
64
      doMerge(templateElement, resultElement, matcher);
5✔
65
    } catch (XmlMergeException e) {
×
66
      throw e;
×
67
    } catch (RuntimeException e) {
1✔
68
      throw new XmlMergeException("Merge strategy " + this + " failed on " + XmlMergeSupport.getXPath(templateElement, true), e);
11✔
69
    }
1✔
70
  }
1✔
71

72
  /**
73
   * Internal implementation of {@link #merge(Element, Element, ElementMatcher)}
74
   *
75
   * @param templateElement the {@link Element} of the template XML file to merge.
76
   * @param resultElement the {@link Element} populated with the workspace XML file to merge into.
77
   * @param matcher the {@link ElementMatcher}.
78
   */
79
  protected abstract void doMerge(Element templateElement, Element resultElement, ElementMatcher matcher);
80

81
  /**
82
   * Returns the MergeStrategy enum constant with the specified name.
83
   *
84
   * @param name the name of the enum constant to return
85
   * @return the enum constant with the specified name
86
   */
87
  public static XmlMergeStrategy of(String name) {
88

89
    return Enum.valueOf(XmlMergeStrategy.class, name.toUpperCase(Locale.ROOT));
7✔
90
  }
91

92
  /**
93
   * Combines child nodes (elements, text and CDATA) from the template into the result {@link Element}.
94
   *
95
   * @param templateElement the template {@link Element}.
96
   * @param resultElement the result {@link Element}.
97
   * @param elementMatcher the {@link ElementMatcher} used for matching elements.
98
   */
99
  protected void combineChildNodes(Element templateElement, Element resultElement, ElementMatcher elementMatcher) {
100

101
    NodeList templateChildNodes = templateElement.getChildNodes();
3✔
102
    for (int i = 0; i < templateChildNodes.getLength(); i++) {
8✔
103
      Node templateChild = templateChildNodes.item(i);
4✔
104
      if (templateChild.getNodeType() == Node.ELEMENT_NODE) {
4✔
105
        Element templateChildElement = (Element) templateChild;
3✔
106
        Element matchedResultElement = elementMatcher.matchElement(templateChildElement, resultElement);
5✔
107
        if (matchedResultElement != null) {
2✔
108
          XmlMergeStrategy mergeStrategy = XmlMergeSupport.getMergeStrategy(templateChildElement);
3✔
109
          if (mergeStrategy == null) {
2✔
110
            mergeStrategy = this; // fallback "this" will always be COMBINE
2✔
111
          }
112
          mergeStrategy.merge(templateChildElement, matchedResultElement, elementMatcher);
5✔
113
        } else {
1✔
114
          Node resultChildElement = resultElement.getOwnerDocument().importNode(templateChildElement, true);
6✔
115
          resultElement.appendChild(resultChildElement);
4✔
116
        }
117
      } else if (XmlMergeSupport.isTextual(templateChild)) {
4✔
118
        if (!templateChild.getTextContent().isBlank()) {
4✔
119
          replaceTextNode(resultElement, templateChild);
4✔
120
        }
121
      }
122
    }
123
  }
1✔
124

125
  /**
126
   * Replaces the text node in the target element with the text from the update element, otherwise appends it.
127
   *
128
   * @param resultElement the element to be updated
129
   * @param templateChild the new text node
130
   */
131
  protected void replaceTextNode(Element resultElement, Node templateChild) {
132

133
    try {
134
      NodeList targetChildNodes = resultElement.getChildNodes();
3✔
135
      for (int i = 0; i < targetChildNodes.getLength(); i++) {
6!
136
        Node targetChild = targetChildNodes.item(i);
4✔
137
        if (XmlMergeSupport.isTextual(targetChild)) {
3!
138
          if (!targetChild.getTextContent().isBlank()) {
4!
139
            targetChild.setTextContent(templateChild.getTextContent().trim());
5✔
140
            return;
1✔
141
          }
142
        }
143
      }
144
      Node importedNode = resultElement.getOwnerDocument().importNode(templateChild, true);
×
145
      resultElement.appendChild(importedNode);
×
146
    } catch (DOMException e) {
×
147
      throw new IllegalStateException("Failed to replace text node for element " + XmlMergeSupport.getXPath(resultElement), e);
×
148
    }
×
149
  }
×
150

151
  @Override
152
  public String toString() {
153

154
    return this.name().toLowerCase(Locale.ROOT);
5✔
155
  }
156
}
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