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

devonfw / IDEasy / 9907372175

12 Jul 2024 11:49AM UTC coverage: 61.142% (-0.02%) from 61.162%
9907372175

push

github

hohwille
fixed tests

1997 of 3595 branches covered (55.55%)

Branch coverage included in aggregate %.

5296 of 8333 relevant lines covered (63.55%)

2.8 hits per line

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

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

3
import java.util.Locale;
4
import javax.xml.XMLConstants;
5

6
import org.w3c.dom.DOMException;
7
import org.w3c.dom.Document;
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
import com.devonfw.tools.ide.merge.xmlmerger.model.MergeAttribute;
14
import com.devonfw.tools.ide.merge.xmlmerger.model.MergeElement;
15

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

21
  /**
22
   * Combines source and target elements. Overrides text nodes and attributes. This process is recursively applied to child elements. If the source element
23
   * exists in the target document, they are combined, otherwise, the source element is appended.
24
   */
25
  COMBINE {
11✔
26
    @Override
27
    public void merge(MergeElement sourceElement, MergeElement targetElement, ElementMatcher elementMatcher) {
28

29
      combineAttributes(sourceElement, targetElement);
4✔
30
      combineChildNodes(sourceElement, targetElement, elementMatcher);
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
    public void merge(MergeElement sourceElement, MergeElement targetElement, ElementMatcher elementMatcher) {
41

42
      try {
43
        updateAndRemoveNsAttributes(sourceElement, elementMatcher);
4✔
44
        Node importedNode = targetElement.getElement().getOwnerDocument().importNode(sourceElement.getElement(), true);
8✔
45
        targetElement.getElement().getParentNode().replaceChild(importedNode, targetElement.getElement());
8✔
46
      } catch (DOMException e) {
×
47
        throw new IllegalStateException("Failed to override element " + sourceElement.getXPath() + " in " + sourceElement.getDocumentPath(), e);
×
48
      }
1✔
49
    }
1✔
50
  },
51

52
  /**
53
   * Keeps the existing target element intact if the source element exists in the target document, otherwise, it is appended.
54
   */
55
  KEEP {
11✔
56
    @Override
57
    public void merge(MergeElement sourceElement, MergeElement targetElement, ElementMatcher elementMatcher) {
58

59
      // Do nothing, keep the existing element
60
    }
1✔
61
  };
62

63
  /**
64
   * Merges the source element into the target element using the specific strategy.
65
   *
66
   * @param sourceElement the source element to be merged
67
   * @param targetElement the target element to merge into
68
   * @param elementMatcher the element matcher used for matching elements
69
   */
70
  public abstract void merge(MergeElement sourceElement, MergeElement targetElement, ElementMatcher elementMatcher);
71

72
  /**
73
   * Returns the MergeStrategy enum constant with the specified name.
74
   *
75
   * @param name the name of the enum constant to return
76
   * @return the enum constant with the specified name
77
   */
78
  public static MergeStrategy of(String name) {
79

80
    return Enum.valueOf(MergeStrategy.class, name.toUpperCase(Locale.ROOT));
7✔
81
  }
82

83
  /**
84
   * Updates the element matcher with the merge:id attribute and removes all merge namespace attributes.
85
   *
86
   * @param mergeElement the merge element to process
87
   * @param elementMatcher the element matcher to update
88
   */
89
  protected void updateAndRemoveNsAttributes(MergeElement mergeElement, ElementMatcher elementMatcher) {
90

91
    for (MergeAttribute attribute : mergeElement.getElementAttributes()) {
11✔
92
      if (attribute.isMergeNsAttr()) {
3✔
93
        if (attribute.isMergeNsIdAttr()) {
3✔
94
          elementMatcher.updateId(mergeElement.getQName(), attribute.getValue());
6✔
95
        }
96
        mergeElement.getElement().removeAttributeNode(attribute.getAttr());
6✔
97
      }
98
    }
1✔
99
    for (MergeElement childElement : mergeElement.getChildElements()) {
11✔
100
      updateAndRemoveNsAttributes(childElement, elementMatcher);
4✔
101
    }
1✔
102
  }
1✔
103

104
  /**
105
   * Combines attributes from the update element into the target element.
106
   *
107
   * @param updateElement the element with the new attributes
108
   * @param targetElement the element to receive the new attributes
109
   */
110
  protected void combineAttributes(MergeElement updateElement, MergeElement targetElement) {
111

112
    try {
113
      Element targetElementNode = targetElement.getElement();
3✔
114
      for (MergeAttribute updateAttr : updateElement.getElementAttributes()) {
11✔
115
        if (!updateAttr.isMergeNsAttr()) {
3✔
116
          String namespaceUri = updateAttr.getAttr().getNamespaceURI();
4✔
117
          String attrName = updateAttr.getAttr().getName();
4✔
118
          String attrValue = updateAttr.getValue();
3✔
119

120
          if (namespaceUri != null && !namespaceUri.isEmpty()) {
5!
121
            String prefix = updateAttr.getAttr().getPrefix();
4✔
122
            if (prefix != null && !prefix.isEmpty()) {
5!
123
              if (targetElementNode.getAttributeNodeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, prefix) == null) {
5!
124
                targetElementNode.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + prefix, namespaceUri);
6✔
125
              }
126
              targetElementNode.setAttributeNS(namespaceUri, attrName, attrValue);
6✔
127
            } else {
128
              targetElementNode.setAttributeNS(namespaceUri, attrName, attrValue);
×
129
            }
130
          } else {
1✔
131
            targetElementNode.setAttribute(attrName, attrValue);
4✔
132
          }
133
        }
134
      }
1✔
135
    } catch (DOMException e) {
×
136
      throw new IllegalStateException("Failed to combine attributes for element " + updateElement.getXPath() + " in " + updateElement.getDocumentPath(), e);
×
137
    }
1✔
138
  }
1✔
139

140
  /**
141
   * Combines child nodes (elements, text and CDATA) from the update element into the target element.
142
   *
143
   * @param updateElement the element with the new child nodes
144
   * @param targetElement the element to receive the new child nodes
145
   * @param elementMatcher the element matcher used for matching elements
146
   */
147
  protected void combineChildNodes(MergeElement updateElement, MergeElement targetElement, ElementMatcher elementMatcher) {
148

149
    try {
150
      NodeList updateChildNodes = updateElement.getElement().getChildNodes();
4✔
151
      for (int i = 0; i < updateChildNodes.getLength(); i++) {
8✔
152
        Node updateChild = updateChildNodes.item(i);
4✔
153
        if (updateChild.getNodeType() == Node.ELEMENT_NODE) {
4✔
154
          MergeElement sourceChildElement = new MergeElement((Element) updateChild, updateElement.getDocumentPath());
8✔
155
          MergeElement matchedTargetChild = elementMatcher.matchElement(sourceChildElement, targetElement);
5✔
156
          if (matchedTargetChild != null) {
2✔
157
            MergeStrategy childStrategy = MergeStrategy.of(sourceChildElement.getMergingStrategy());
4✔
158
            childStrategy.merge(sourceChildElement, matchedTargetChild, elementMatcher);
5✔
159
          } else {
1✔
160
            appendElement(sourceChildElement, targetElement, elementMatcher);
5✔
161
          }
162
        } else if (updateChild.getNodeType() == Node.TEXT_NODE || updateChild.getNodeType() == Node.CDATA_SECTION_NODE) {
9!
163
          if (!updateChild.getTextContent().isBlank()) {
4✔
164
            replaceTextNode(targetElement, updateChild);
4✔
165
          }
166
        }
167
      }
168
    } catch (DOMException e) {
×
169
      throw new IllegalStateException("Failed to combine child nodes for element " + updateElement.getXPath() + " in " + updateElement.getDocumentPath(), e);
×
170
    }
1✔
171
  }
1✔
172

173
  /**
174
   * Appends the source element as a child of the target element.
175
   *
176
   * @param sourceElement the element to be appended
177
   * @param targetElement the target element where the source element will be appended
178
   * @param elementMatcher the element matcher used for updating IDs
179
   */
180
  protected void appendElement(MergeElement sourceElement, MergeElement targetElement, ElementMatcher elementMatcher) {
181

182
    try {
183
      updateAndRemoveNsAttributes(sourceElement, elementMatcher);
4✔
184
      Document targetDocument = targetElement.getElement().getOwnerDocument();
4✔
185
      Element importedNode = (Element) targetDocument.importNode(sourceElement.getElement(), true);
7✔
186
      targetElement.getElement().appendChild(importedNode);
5✔
187
    } catch (Exception e) {
×
188
      throw new IllegalStateException("Failed to append element: " + sourceElement.getXPath(), e);
×
189
    }
1✔
190
  }
1✔
191

192
  /**
193
   * Replaces the text node in the target element with the text from the update element, otherwise appends it.
194
   *
195
   * @param targetElement the element to be updated
196
   * @param updateChild the new text node
197
   */
198
  protected void replaceTextNode(MergeElement targetElement, Node updateChild) {
199

200
    Element element = targetElement.getElement();
3✔
201
    try {
202
      NodeList targetChildNodes = element.getChildNodes();
3✔
203
      for (int i = 0; i < targetChildNodes.getLength(); i++) {
6!
204
        Node targetChild = targetChildNodes.item(i);
4✔
205
        if (targetChild.getNodeType() == Node.TEXT_NODE || targetChild.getNodeType() == Node.CDATA_SECTION_NODE) {
4!
206
          if (!targetChild.getTextContent().isBlank()) {
4!
207
            targetChild.setTextContent(updateChild.getTextContent().trim());
5✔
208
            return;
1✔
209
          }
210
        }
211
      }
212
      Node importedNode = element.getOwnerDocument().importNode(updateChild, true);
×
213
      element.appendChild(importedNode);
×
214
    } catch (DOMException e) {
×
215
      throw new IllegalStateException("Failed to replace text node for element " + targetElement.getXPath() + " in " + targetElement.getDocumentPath(), e);
×
216
    }
×
217
  }
×
218
}
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