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

devonfw / IDEasy / 13944696202

19 Mar 2025 10:48AM UTC coverage: 67.644% (-0.01%) from 67.657%
13944696202

Pull #1146

github

web-flow
Merge 255542aee into 2ef884351
Pull Request #1146: Fix/1008 improve upgrade settings

3043 of 4923 branches covered (61.81%)

Branch coverage included in aggregate %.

7843 of 11170 relevant lines covered (70.21%)

3.07 hits per line

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

84.51
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
import java.util.logging.Logger;
6

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

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

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

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

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

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

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

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

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

58
  /**
59
   * @param templateElement the {@link Element} of the template XML file to merge.
60
   * @param resultElement the {@link Element} populated with the workspace XML file to merge into.
61
   * @param matcher the {@link ElementMatcher}.
62
   */
63
  public void merge(Element templateElement, Element resultElement, ElementMatcher matcher) {
64
    Logger logger = Logger.getLogger(XmlMergeStrategy.class.getName());
4✔
65
    try {
66
      doMerge(templateElement, resultElement, matcher);
5✔
67
    } catch (XmlMergeException e) {
×
68
      logger.warning("XML Merge Exception: " + e.getMessage());
×
69
    } catch (RuntimeException e) {
1✔
70
      logger.warning("Merge strategy " + this + " failed on " + XmlMergeSupport.getXPath(templateElement, true) + ": " + e.getMessage());
10✔
71
    }
1✔
72
  }
1✔
73

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

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

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

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

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

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

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

153
  @Override
154
  public String toString() {
155

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