1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.configuration2;
19
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.Reader;
23 import java.io.StringReader;
24 import java.io.StringWriter;
25 import java.io.Writer;
26 import java.net.URL;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.Map;
33
34 import javax.xml.parsers.DocumentBuilder;
35 import javax.xml.parsers.DocumentBuilderFactory;
36 import javax.xml.parsers.ParserConfigurationException;
37 import javax.xml.transform.OutputKeys;
38 import javax.xml.transform.Result;
39 import javax.xml.transform.Source;
40 import javax.xml.transform.Transformer;
41 import javax.xml.transform.dom.DOMSource;
42 import javax.xml.transform.stream.StreamResult;
43
44 import org.apache.commons.configuration2.convert.ListDelimiterHandler;
45 import org.apache.commons.configuration2.ex.ConfigurationException;
46 import org.apache.commons.configuration2.io.ConfigurationLogger;
47 import org.apache.commons.configuration2.io.FileLocator;
48 import org.apache.commons.configuration2.io.FileLocatorAware;
49 import org.apache.commons.configuration2.io.InputStreamSupport;
50 import org.apache.commons.configuration2.resolver.DefaultEntityResolver;
51 import org.apache.commons.configuration2.tree.ImmutableNode;
52 import org.apache.commons.configuration2.tree.NodeTreeWalker;
53 import org.apache.commons.configuration2.tree.ReferenceNodeHandler;
54 import org.apache.commons.lang3.StringUtils;
55 import org.apache.commons.lang3.mutable.MutableObject;
56 import org.w3c.dom.Attr;
57 import org.w3c.dom.CDATASection;
58 import org.w3c.dom.Document;
59 import org.w3c.dom.Element;
60 import org.w3c.dom.NamedNodeMap;
61 import org.w3c.dom.Node;
62 import org.w3c.dom.NodeList;
63 import org.w3c.dom.Text;
64 import org.xml.sax.EntityResolver;
65 import org.xml.sax.InputSource;
66 import org.xml.sax.SAXException;
67 import org.xml.sax.SAXParseException;
68 import org.xml.sax.helpers.DefaultHandler;
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171 public class XMLConfiguration extends BaseHierarchicalConfiguration implements FileBasedConfiguration, FileLocatorAware, InputStreamSupport {
172
173 static final int DEFAULT_INDENT_SIZE = 2;
174
175
176 static final String INDENT_AMOUNT_PROPERTY = "{http://xml.apache.org/xslt}indent-amount";
177
178
179 private static final String DEFAULT_ROOT_NAME = "configuration";
180
181
182 private static final String ATTR_SPACE = "xml:space";
183
184
185 private static final String ATTR_SPACE_INTERNAL = "config-xml:space";
186
187
188 private static final String VALUE_PRESERVE = "preserve";
189
190
191 private static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
192
193
194 private static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
195
196
197 private String rootElementName;
198
199
200 private String publicID;
201
202
203 private String systemID;
204
205
206 private DocumentBuilder documentBuilder;
207
208
209 private boolean validating;
210
211
212 private boolean schemaValidation;
213
214
215 private EntityResolver entityResolver = new DefaultEntityResolver();
216
217
218 private FileLocator locator;
219
220
221
222
223 public XMLConfiguration() {
224 initLogger(new ConfigurationLogger(XMLConfiguration.class));
225 }
226
227
228
229
230
231
232
233
234
235 public XMLConfiguration(final HierarchicalConfiguration<ImmutableNode> c) {
236 super(c);
237 rootElementName = c != null ? c.getRootElementName() : null;
238 initLogger(new ConfigurationLogger(XMLConfiguration.class));
239 }
240
241
242
243
244
245
246
247
248 @Override
249 protected String getRootElementNameInternal() {
250 final Document doc = getDocument();
251 if (doc == null) {
252 return rootElementName == null ? DEFAULT_ROOT_NAME : rootElementName;
253 }
254 return doc.getDocumentElement().getNodeName();
255 }
256
257
258
259
260
261
262
263
264
265
266 public void setRootElementName(final String name) {
267 beginRead(true);
268 try {
269 if (getDocument() != null) {
270 throw new UnsupportedOperationException("The name of the root element " + "cannot be changed when loaded from an XML document!");
271 }
272 rootElementName = name;
273 } finally {
274 endRead();
275 }
276 }
277
278
279
280
281
282
283
284
285 public DocumentBuilder getDocumentBuilder() {
286 return documentBuilder;
287 }
288
289
290
291
292
293
294
295
296
297 public void setDocumentBuilder(final DocumentBuilder documentBuilder) {
298 this.documentBuilder = documentBuilder;
299 }
300
301
302
303
304
305
306
307
308 public String getPublicID() {
309 beginRead(false);
310 try {
311 return publicID;
312 } finally {
313 endRead();
314 }
315 }
316
317
318
319
320
321
322
323
324 public void setPublicID(final String publicID) {
325 beginWrite(false);
326 try {
327 this.publicID = publicID;
328 } finally {
329 endWrite();
330 }
331 }
332
333
334
335
336
337
338
339
340 public String getSystemID() {
341 beginRead(false);
342 try {
343 return systemID;
344 } finally {
345 endRead();
346 }
347 }
348
349
350
351
352
353
354
355
356 public void setSystemID(final String systemID) {
357 beginWrite(false);
358 try {
359 this.systemID = systemID;
360 } finally {
361 endWrite();
362 }
363 }
364
365
366
367
368
369
370
371 public boolean isValidating() {
372 return validating;
373 }
374
375
376
377
378
379
380
381
382 public void setValidating(final boolean validating) {
383 if (!schemaValidation) {
384 this.validating = validating;
385 }
386 }
387
388
389
390
391
392
393
394 public boolean isSchemaValidation() {
395 return schemaValidation;
396 }
397
398
399
400
401
402
403
404
405
406 public void setSchemaValidation(final boolean schemaValidation) {
407 this.schemaValidation = schemaValidation;
408 if (schemaValidation) {
409 this.validating = true;
410 }
411 }
412
413
414
415
416
417
418
419 public void setEntityResolver(final EntityResolver resolver) {
420 this.entityResolver = resolver;
421 }
422
423
424
425
426
427
428
429 public EntityResolver getEntityResolver() {
430 return this.entityResolver;
431 }
432
433
434
435
436
437
438
439 public Document getDocument() {
440 final XMLDocumentHelper docHelper = getDocumentHelper();
441 return docHelper != null ? docHelper.getDocument() : null;
442 }
443
444
445
446
447
448
449 private XMLDocumentHelper getDocumentHelper() {
450 final ReferenceNodeHandler handler = getReferenceHandler();
451 return (XMLDocumentHelper) handler.getReference(handler.getRootNode());
452 }
453
454
455
456
457
458
459 private ReferenceNodeHandler getReferenceHandler() {
460 return getSubConfigurationParentModel().getReferenceNodeHandler();
461 }
462
463
464
465
466
467
468
469 private void initProperties(final XMLDocumentHelper docHelper, final boolean elemRefs) {
470 final Document document = docHelper.getDocument();
471 setPublicID(docHelper.getSourcePublicID());
472 setSystemID(docHelper.getSourceSystemID());
473
474 final ImmutableNode.Builder rootBuilder = new ImmutableNode.Builder();
475 final MutableObject<String> rootValue = new MutableObject<>();
476 final Map<ImmutableNode, Object> elemRefMap = elemRefs ? new HashMap<>() : null;
477 final Map<String, String> attributes = constructHierarchy(rootBuilder, rootValue, document.getDocumentElement(), elemRefMap, true, 0);
478 attributes.remove(ATTR_SPACE_INTERNAL);
479 final ImmutableNode top = rootBuilder.value(rootValue.getValue()).addAttributes(attributes).create();
480 getSubConfigurationParentModel().mergeRoot(top, document.getDocumentElement().getTagName(), elemRefMap, elemRefs ? docHelper : null, this);
481 }
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496 private Map<String, String> constructHierarchy(final ImmutableNode.Builder node, final MutableObject<String> refValue, final Element element,
497 final Map<ImmutableNode, Object> elemRefs, final boolean trim, final int level) {
498 final boolean trimFlag = shouldTrim(element, trim);
499 final Map<String, String> attributes = processAttributes(element);
500 attributes.put(ATTR_SPACE_INTERNAL, String.valueOf(trimFlag));
501 final StringBuilder buffer = new StringBuilder();
502 final NodeList list = element.getChildNodes();
503 boolean hasChildren = false;
504
505 for (int i = 0; i < list.getLength(); i++) {
506 final Node w3cNode = list.item(i);
507 if (w3cNode instanceof Element) {
508 final Element child = (Element) w3cNode;
509 final ImmutableNode.Builder childNode = new ImmutableNode.Builder();
510 childNode.name(child.getTagName());
511 final MutableObject<String> refChildValue = new MutableObject<>();
512 final Map<String, String> attrmap = constructHierarchy(childNode, refChildValue, child, elemRefs, trimFlag, level + 1);
513 final boolean childTrim = Boolean.parseBoolean(attrmap.remove(ATTR_SPACE_INTERNAL));
514 childNode.addAttributes(attrmap);
515 final ImmutableNode newChild = createChildNodeWithValue(node, childNode, child, refChildValue.getValue(), childTrim, attrmap, elemRefs);
516 if (elemRefs != null && !elemRefs.containsKey(newChild)) {
517 elemRefs.put(newChild, child);
518 }
519 hasChildren = true;
520 } else if (w3cNode instanceof Text) {
521 final Text data = (Text) w3cNode;
522 buffer.append(data.getData());
523 }
524 }
525
526 boolean childrenFlag = false;
527 if (hasChildren || trimFlag) {
528 childrenFlag = hasChildren || attributes.size() > 1;
529 }
530 final String text = determineValue(buffer.toString(), childrenFlag, trimFlag);
531 if (!text.isEmpty() || !childrenFlag && level != 0) {
532 refValue.setValue(text);
533 }
534 return attributes;
535 }
536
537
538
539
540
541
542
543
544
545
546
547 private static String determineValue(final String content, final boolean hasChildren, final boolean trimFlag) {
548 final boolean shouldTrim = trimFlag || StringUtils.isBlank(content) && hasChildren;
549 return shouldTrim ? content.trim() : content;
550 }
551
552
553
554
555
556
557
558 private static Map<String, String> processAttributes(final Element element) {
559 final NamedNodeMap attributes = element.getAttributes();
560 final Map<String, String> attrmap = new HashMap<>();
561
562 for (int i = 0; i < attributes.getLength(); ++i) {
563 final Node w3cNode = attributes.item(i);
564 if (w3cNode instanceof Attr) {
565 final Attr attr = (Attr) w3cNode;
566 attrmap.put(attr.getName(), attr.getValue());
567 }
568 }
569
570 return attrmap;
571 }
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588 private ImmutableNode createChildNodeWithValue(final ImmutableNode.Builder parent, final ImmutableNode.Builder child, final Element elem,
589 final String value, final boolean trim, final Map<String, String> attrmap, final Map<ImmutableNode, Object> elemRefs) {
590 final ImmutableNode addedChildNode;
591 final Collection<String> values;
592
593 if (value != null) {
594 values = getListDelimiterHandler().split(value, trim);
595 } else {
596 values = Collections.emptyList();
597 }
598
599 if (values.size() > 1) {
600 final Map<ImmutableNode, Object> refs = isSingleElementList(elem) ? elemRefs : null;
601 final Iterator<String> it = values.iterator();
602
603 child.value(it.next());
604 addedChildNode = child.create();
605 parent.addChild(addedChildNode);
606 XMLListReference.assignListReference(refs, addedChildNode, elem);
607
608
609 while (it.hasNext()) {
610 final ImmutableNode.Builder c = new ImmutableNode.Builder();
611 c.name(addedChildNode.getNodeName());
612 c.value(it.next());
613 c.addAttributes(attrmap);
614 final ImmutableNode newChild = c.create();
615 parent.addChild(newChild);
616 XMLListReference.assignListReference(refs, newChild, null);
617 }
618 } else {
619 if (values.size() == 1) {
620
621
622 child.value(values.iterator().next());
623 }
624 addedChildNode = child.create();
625 parent.addChild(addedChildNode);
626 }
627
628 return addedChildNode;
629 }
630
631
632
633
634
635
636
637 private static boolean isSingleElementList(final Element element) {
638 final Node parentNode = element.getParentNode();
639 return countChildElements(parentNode, element.getTagName()) == 1;
640 }
641
642
643
644
645
646
647
648
649 private static int countChildElements(final Node parent, final String name) {
650 final NodeList childNodes = parent.getChildNodes();
651 int count = 0;
652 for (int i = 0; i < childNodes.getLength(); i++) {
653 final Node item = childNodes.item(i);
654 if (item instanceof Element && name.equals(((Element) item).getTagName())) {
655 count++;
656 }
657 }
658 return count;
659 }
660
661
662
663
664
665
666
667
668
669
670
671 private static boolean shouldTrim(final Element element, final boolean currentTrim) {
672 final Attr attr = element.getAttributeNode(ATTR_SPACE);
673
674 if (attr == null) {
675 return currentTrim;
676 }
677 return !VALUE_PRESERVE.equals(attr.getValue());
678 }
679
680
681
682
683
684
685
686
687
688
689
690 protected DocumentBuilder createDocumentBuilder() throws ParserConfigurationException {
691 if (getDocumentBuilder() != null) {
692 return getDocumentBuilder();
693 }
694 final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
695 if (isValidating()) {
696 factory.setValidating(true);
697 if (isSchemaValidation()) {
698 factory.setNamespaceAware(true);
699 factory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
700 }
701 }
702
703 final DocumentBuilder result = factory.newDocumentBuilder();
704 result.setEntityResolver(this.entityResolver);
705
706 if (isValidating()) {
707
708 result.setErrorHandler(new DefaultHandler() {
709 @Override
710 public void error(final SAXParseException ex) throws SAXException {
711 throw ex;
712 }
713 });
714 }
715 return result;
716 }
717
718
719
720
721
722
723
724
725
726
727 protected Transformer createTransformer() throws ConfigurationException {
728 final Transformer transformer = XMLDocumentHelper.createTransformer();
729
730 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
731 transformer.setOutputProperty(INDENT_AMOUNT_PROPERTY, Integer.toString(DEFAULT_INDENT_SIZE));
732 if (locator != null && locator.getEncoding() != null) {
733 transformer.setOutputProperty(OutputKeys.ENCODING, locator.getEncoding());
734 }
735 if (publicID != null) {
736 transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, publicID);
737 }
738 if (systemID != null) {
739 transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, systemID);
740 }
741
742 return transformer;
743 }
744
745
746
747
748
749
750
751 private Document createDocument() throws ConfigurationException {
752 final ReferenceNodeHandler handler = getReferenceHandler();
753 final XMLDocumentHelper docHelper = (XMLDocumentHelper) handler.getReference(handler.getRootNode());
754 final XMLDocumentHelper newHelper = docHelper == null ? XMLDocumentHelper.forNewDocument(getRootElementName()) : docHelper.createCopy();
755
756 final XMLBuilderVisitor builder = new XMLBuilderVisitor(newHelper, getListDelimiterHandler());
757 builder.handleRemovedNodes(handler);
758 builder.processDocument(handler);
759 initRootElementText(newHelper.getDocument(), getModel().getNodeHandler().getRootNode().getValue());
760 return newHelper.getDocument();
761 }
762
763
764
765
766
767
768
769 private void initRootElementText(final Document doc, final Object value) {
770 final Element elem = doc.getDocumentElement();
771 final NodeList children = elem.getChildNodes();
772
773
774 for (int i = 0; i < children.getLength(); i++) {
775 final Node nd = children.item(i);
776 if (nd.getNodeType() == Node.TEXT_NODE) {
777 elem.removeChild(nd);
778 }
779 }
780
781 if (value != null) {
782
783 elem.appendChild(doc.createTextNode(String.valueOf(value)));
784 }
785 }
786
787
788
789
790 @Override
791 public void initFileLocator(final FileLocator loc) {
792 locator = loc;
793 }
794
795
796
797
798
799
800
801
802
803 @Override
804 public void read(final Reader in) throws ConfigurationException, IOException {
805 load(new InputSource(in));
806 }
807
808
809
810
811
812
813
814
815
816
817 @Override
818 public void read(final InputStream in) throws ConfigurationException, IOException {
819 load(new InputSource(in));
820 }
821
822
823
824
825
826
827
828 private void load(final InputSource source) throws ConfigurationException {
829 if (locator == null) {
830 throw new ConfigurationException(
831 "Load operation not properly " + "initialized! Do not call read(InputStream) directly," + " but use a FileHandler to load a configuration.");
832 }
833
834 try {
835 final URL sourceURL = locator.getSourceURL();
836 if (sourceURL != null) {
837 source.setSystemId(sourceURL.toString());
838 }
839
840 final DocumentBuilder builder = createDocumentBuilder();
841 final Document newDocument = builder.parse(source);
842 final Document oldDocument = getDocument();
843 initProperties(XMLDocumentHelper.forSourceDocument(newDocument), oldDocument == null);
844 } catch (final SAXParseException spe) {
845 throw new ConfigurationException("Error parsing " + source.getSystemId(), spe);
846 } catch (final Exception e) {
847 this.getLogger().debug("Unable to load the configuration: " + e);
848 throw new ConfigurationException("Unable to load the configuration", e);
849 }
850 }
851
852
853
854
855
856
857
858
859 @Override
860 public void write(final Writer writer) throws ConfigurationException, IOException {
861 write(writer, createTransformer());
862 }
863
864
865
866
867
868
869
870
871
872 public void write(final Writer writer, final Transformer transformer) throws ConfigurationException {
873 final Source source = new DOMSource(createDocument());
874 final Result result = new StreamResult(writer);
875 XMLDocumentHelper.transform(transformer, source, result);
876 }
877
878
879
880
881
882
883 public void validate() throws ConfigurationException {
884 beginWrite(false);
885 try {
886 final Transformer transformer = createTransformer();
887 final Source source = new DOMSource(createDocument());
888 final StringWriter writer = new StringWriter();
889 final Result result = new StreamResult(writer);
890 XMLDocumentHelper.transform(transformer, source, result);
891 final Reader reader = new StringReader(writer.getBuffer().toString());
892 final DocumentBuilder builder = createDocumentBuilder();
893 builder.parse(new InputSource(reader));
894 } catch (final SAXException | IOException | ParserConfigurationException pce) {
895 throw new ConfigurationException("Validation failed", pce);
896 } finally {
897 endWrite();
898 }
899 }
900
901
902
903
904 static class XMLBuilderVisitor extends BuilderVisitor {
905
906 private final Document document;
907
908
909 private final Map<Node, Node> elementMapping;
910
911
912 private final Map<ImmutableNode, Element> newElements;
913
914
915 private final ListDelimiterHandler listDelimiterHandler;
916
917
918
919
920
921
922
923 public XMLBuilderVisitor(final XMLDocumentHelper docHelper, final ListDelimiterHandler handler) {
924 document = docHelper.getDocument();
925 elementMapping = docHelper.getElementMapping();
926 listDelimiterHandler = handler;
927 newElements = new HashMap<>();
928 }
929
930
931
932
933
934
935 public void processDocument(final ReferenceNodeHandler refHandler) {
936 updateAttributes(refHandler.getRootNode(), document.getDocumentElement());
937 NodeTreeWalker.INSTANCE.walkDFS(refHandler.getRootNode(), this, refHandler);
938 }
939
940
941
942
943
944
945
946 public void handleRemovedNodes(final ReferenceNodeHandler refHandler) {
947 refHandler.removedReferences().stream().filter(Node.class::isInstance).forEach(ref -> removeReference(elementMapping.get(ref)));
948 }
949
950
951
952
953
954 @Override
955 protected void insert(final ImmutableNode newNode, final ImmutableNode parent, final ImmutableNode sibling1, final ImmutableNode sibling2,
956 final ReferenceNodeHandler refHandler) {
957 if (XMLListReference.isListNode(newNode, refHandler)) {
958 return;
959 }
960
961 final Element elem = document.createElement(newNode.getNodeName());
962 newElements.put(newNode, elem);
963 updateAttributes(newNode, elem);
964 if (newNode.getValue() != null) {
965 final String txt = String.valueOf(listDelimiterHandler.escape(newNode.getValue(), ListDelimiterHandler.NOOP_TRANSFORMER));
966 elem.appendChild(document.createTextNode(txt));
967 }
968 if (sibling2 == null) {
969 getElement(parent, refHandler).appendChild(elem);
970 } else if (sibling1 != null) {
971 getElement(parent, refHandler).insertBefore(elem, getElement(sibling1, refHandler).getNextSibling());
972 } else {
973 getElement(parent, refHandler).insertBefore(elem, getElement(parent, refHandler).getFirstChild());
974 }
975 }
976
977
978
979
980
981 @Override
982 protected void update(final ImmutableNode node, final Object reference, final ReferenceNodeHandler refHandler) {
983 if (XMLListReference.isListNode(node, refHandler)) {
984 if (XMLListReference.isFirstListItem(node, refHandler)) {
985 final String value = XMLListReference.listValue(node, refHandler, listDelimiterHandler);
986 updateElement(node, refHandler, value);
987 }
988 } else {
989 final Object value = listDelimiterHandler.escape(refHandler.getValue(node), ListDelimiterHandler.NOOP_TRANSFORMER);
990 updateElement(node, refHandler, value);
991 }
992 }
993
994 private void updateElement(final ImmutableNode node, final ReferenceNodeHandler refHandler, final Object value) {
995 final Element element = getElement(node, refHandler);
996 updateElement(element, value);
997 updateAttributes(node, element);
998 }
999
1000
1001
1002
1003
1004
1005
1006 private void updateElement(final Element element, final Object value) {
1007 Text txtNode = findTextNodeForUpdate(element);
1008 if (value == null) {
1009
1010 if (txtNode != null) {
1011 element.removeChild(txtNode);
1012 }
1013 } else {
1014 final String newValue = String.valueOf(value);
1015 if (txtNode == null) {
1016 txtNode = document.createTextNode(newValue);
1017 if (element.getFirstChild() != null) {
1018 element.insertBefore(txtNode, element.getFirstChild());
1019 } else {
1020 element.appendChild(txtNode);
1021 }
1022 } else {
1023 txtNode.setNodeValue(newValue);
1024 }
1025 }
1026 }
1027
1028
1029
1030
1031
1032
1033 private void removeReference(final Node element) {
1034 final Node parentElem = element.getParentNode();
1035 if (parentElem != null) {
1036 parentElem.removeChild(element);
1037 }
1038 }
1039
1040
1041
1042
1043
1044
1045
1046
1047 private Element getElement(final ImmutableNode node, final ReferenceNodeHandler refHandler) {
1048 final Element elementNew = newElements.get(node);
1049 if (elementNew != null) {
1050 return elementNew;
1051 }
1052
1053
1054 final Object reference = refHandler.getReference(node);
1055 final Node element;
1056 if (reference instanceof XMLDocumentHelper) {
1057 element = ((XMLDocumentHelper) reference).getDocument().getDocumentElement();
1058 } else if (reference instanceof XMLListReference) {
1059 element = ((XMLListReference) reference).getElement();
1060 } else {
1061 element = (Node) reference;
1062 }
1063 return element != null ? (Element) elementMapping.get(element) : document.getDocumentElement();
1064 }
1065
1066
1067
1068
1069
1070
1071
1072 private static void updateAttributes(final ImmutableNode node, final Element elem) {
1073 if (node != null && elem != null) {
1074 clearAttributes(elem);
1075 node.getAttributes().forEach((k, v) -> {
1076 if (v != null) {
1077 elem.setAttribute(k, v.toString());
1078 }
1079 });
1080 }
1081 }
1082
1083
1084
1085
1086
1087
1088 private static void clearAttributes(final Element elem) {
1089 final NamedNodeMap attributes = elem.getAttributes();
1090 for (int i = 0; i < attributes.getLength(); i++) {
1091 elem.removeAttribute(attributes.item(i).getNodeName());
1092 }
1093 }
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103 private static Text findTextNodeForUpdate(final Element elem) {
1104 Text result = null;
1105
1106 final NodeList children = elem.getChildNodes();
1107 final Collection<Node> textNodes = new ArrayList<>();
1108 for (int i = 0; i < children.getLength(); i++) {
1109 final Node nd = children.item(i);
1110 if (nd instanceof Text) {
1111 if (result == null) {
1112 result = (Text) nd;
1113 } else {
1114 textNodes.add(nd);
1115 }
1116 }
1117 }
1118
1119
1120 if (result instanceof CDATASection) {
1121 textNodes.add(result);
1122 result = null;
1123 }
1124
1125
1126 textNodes.forEach(elem::removeChild);
1127 return result;
1128 }
1129 }
1130 }